679 lines
22 KiB
Kotlin
679 lines
22 KiB
Kotlin
/*
|
|
* Copyright 2020 Dorkbox, llc
|
|
* Copyright 2012 The Netty Project
|
|
*
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at:
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
package dorkbox.netUtil
|
|
|
|
import java.io.IOException
|
|
import java.io.Writer
|
|
import java.net.Inet4Address
|
|
import java.net.InetAddress
|
|
import java.net.InetSocketAddress
|
|
import java.net.Socket
|
|
import java.util.*
|
|
import kotlin.math.floor
|
|
import kotlin.math.ln
|
|
import kotlin.math.pow
|
|
|
|
/**
|
|
* A class that holds a number of network-related constants, also from:
|
|
* (Netty, apache 2.0 license)
|
|
* https://github.com/netty/netty/blob/4.1/common/src/main/java/io/netty/util/NetUtil.java
|
|
*
|
|
* This class borrowed some of its methods from a modified fork of the
|
|
* [Inet6Util class]
|
|
* (http://svn.apache.org/repos/asf/harmony/enhanced/java/branches/java6/classlib/modules/luni/src/main/java/org/apache/harmony/luni/util/Inet6Util.java) which was part of Apache Harmony.
|
|
*/
|
|
object IPv4 {
|
|
/**
|
|
* Returns `true` if IPv4 should be used even if the system supports both IPv4 and IPv6. Setting this
|
|
* property to `true` will disable IPv6 support. The default value of this property is `false`.
|
|
*
|
|
* @see [Java SE networking properties](https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html)
|
|
*/
|
|
val isPreferred = Common.getBoolean("java.net.preferIPv4Stack", false)
|
|
|
|
/**
|
|
* The [Inet4Address] that represents the IPv4 loopback address '127.0.0.1'
|
|
*/
|
|
val LOCALHOST: Inet4Address by lazy {
|
|
// Create IPv4 loopback address.
|
|
// this will ALWAYS work
|
|
InetAddress.getByAddress("localhost", byteArrayOf(127, 0, 0, 1)) as Inet4Address
|
|
}
|
|
|
|
/**
|
|
* Windows is unable to work with 0.0.0.0 directly, and if you use LOOPBACK, you might not be able to access the server from another
|
|
* machine.
|
|
*
|
|
* What this does is open a connection to 1.1.1.1 and see get the interface this traffic was on, and use that interface IP address
|
|
*/
|
|
val WILDCARD: String by lazy {
|
|
if (Common.OS_WINDOWS) {
|
|
// silly windows can't work with 0.0.0.0, BUT we can't use loopback because we might need to reach this machine from a different host
|
|
// what we do is open a connection to 1.1.1.1 and see what interface this happened on, and this is used as the accessible
|
|
// interface
|
|
var ip = "127.0.0.1"
|
|
|
|
runCatching {
|
|
Socket().use {
|
|
it.connect(InetSocketAddress("1.1.1.1", 80))
|
|
ip = it.localAddress.hostAddress
|
|
}
|
|
}.onFailure {
|
|
Common.logger.error("Unable to determine outbound traffic local address. Using loopback instead.", it)
|
|
}
|
|
|
|
ip
|
|
} else {
|
|
// everyone else works correctly
|
|
"0.0.0.0"
|
|
}
|
|
}
|
|
|
|
private val SLASH_REGEX = "\\.".toRegex()
|
|
|
|
|
|
/**
|
|
* Determine whether a given string is a valid CIDR IP address. Accepts only 1.2.3.4/24
|
|
*
|
|
* @param ipAsString The string that will be checked.
|
|
*
|
|
* @return return true if the string is a valid IP address, false if it is not.
|
|
*/
|
|
fun isValidCidr(ipAsString: String): Boolean {
|
|
if (ipAsString.isEmpty()) {
|
|
return false
|
|
}
|
|
|
|
val slashIndex = ipAsString.indexOf('/')
|
|
if (slashIndex < 6) {
|
|
// something is malformed.
|
|
return false
|
|
}
|
|
|
|
val ipOnly = ipAsString.substring(0, slashIndex)
|
|
if (!isValid(ipOnly)) {
|
|
return false
|
|
}
|
|
|
|
try {
|
|
val cidr = ipAsString.substring(slashIndex + 1).toInt()
|
|
if (cidr in 0..32) {
|
|
return true
|
|
}
|
|
} catch (ignored: Exception) {
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
/**
|
|
* Takes a [String] and parses it to see if it is a valid IPV4 address.
|
|
*
|
|
* @return true, if the string represents an IPV4 address in dotted notation, false otherwise
|
|
*/
|
|
fun isValid(ip: String): Boolean {
|
|
return isValidIpV4Address(ip, 0, ip.length)
|
|
}
|
|
|
|
internal fun isValidIpV4Address(ip: String, from: Int, toExcluded: Int): Boolean {
|
|
val len = toExcluded - from
|
|
if (len !in 7..15) {
|
|
return false
|
|
}
|
|
|
|
var from = from
|
|
var i = ip.indexOf('.', from)
|
|
|
|
if (i <= 0 || !isValidIpV4Word(ip, from, i)) {
|
|
return false
|
|
}
|
|
|
|
from = i + 1
|
|
i = ip.indexOf('.', from)
|
|
if (i <= 0 || !isValidIpV4Word(ip, from, i)) {
|
|
return false
|
|
}
|
|
|
|
from = i + 1
|
|
i = ip.indexOf('.', from)
|
|
if (i <= 0 || !isValidIpV4Word(ip, from, i)) {
|
|
return false
|
|
}
|
|
|
|
if (i <= 0 || !isValidIpV4Word(ip, i + 1, toExcluded)) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
private fun isValidIpV4Word(word: CharSequence, from: Int, toExclusive: Int): Boolean {
|
|
val len = toExclusive - from
|
|
var c0 = ' '
|
|
var c1 = ' '
|
|
var c2 = ' '
|
|
|
|
if (len < 1 || len > 3 || word[from].also { c0 = it } < '0') {
|
|
return false
|
|
}
|
|
return if (len == 3) {
|
|
word[from + 1].also { c1 = it } >= '0'
|
|
&& word[from + 2].also { c2 = it } >= '0'
|
|
&& (c0 <= '1' && c1 <= '9' && c2 <= '9' || c0 == '2' && c1 <= '5' && (c2 <= '5' || c1 < '5' && c2 <= '9'))
|
|
}
|
|
else c0 <= '9' && (len == 1 || isValidNumericChar(word[from + 1]))
|
|
}
|
|
|
|
internal fun isValidNumericChar(c: Char): Boolean {
|
|
return c in '0'..'9'
|
|
}
|
|
|
|
internal fun isValidIPv4MappedChar(c: Char): Boolean {
|
|
return c == 'f' || c == 'F'
|
|
}
|
|
|
|
|
|
fun toBytesorNull(ip: String): ByteArray? {
|
|
if (!isValid(ip)) {
|
|
return null
|
|
}
|
|
|
|
var i = 0
|
|
return byteArrayOf(
|
|
ipv4WordToByte(ip, 0, ip.indexOf('.', 1).also { i = it }),
|
|
ipv4WordToByte(ip, i + 1, ip.indexOf('.', i + 2).also { i = it }),
|
|
ipv4WordToByte(ip, i + 1, ip.indexOf('.', i + 2).also { i = it }),
|
|
ipv4WordToByte(ip, i + 1, ip.length)
|
|
)
|
|
}
|
|
|
|
fun toBytes(ip: String): ByteArray {
|
|
var i = 0
|
|
return byteArrayOf(
|
|
ipv4WordToByte(ip, 0, ip.indexOf('.', 1).also { i = it }),
|
|
ipv4WordToByte(ip, i + 1, ip.indexOf('.', i + 2).also { i = it }),
|
|
ipv4WordToByte(ip, i + 1, ip.indexOf('.', i + 2).also { i = it }),
|
|
ipv4WordToByte(ip, i + 1, ip.length)
|
|
)
|
|
}
|
|
|
|
private fun decimalDigit(str: CharSequence, pos: Int): Int {
|
|
return str[pos] - '0'
|
|
}
|
|
|
|
private fun ipv4WordToByte(ip: CharSequence, from: Int, toExclusive: Int): Byte {
|
|
var newFrom = from
|
|
var ret = decimalDigit(ip, newFrom)
|
|
newFrom++
|
|
|
|
if (newFrom == toExclusive) {
|
|
return ret.toByte()
|
|
}
|
|
|
|
ret = ret * 10 + decimalDigit(ip, newFrom)
|
|
newFrom++
|
|
|
|
return if (newFrom == toExclusive) {
|
|
ret.toByte()
|
|
} else (ret * 10 + decimalDigit(ip, newFrom)).toByte()
|
|
}
|
|
|
|
fun findFreeSubnet24(): ByteArray? {
|
|
Common.logger.info("Scanning for available cidr...")
|
|
|
|
// have to find a free cidr
|
|
// start with 10.x.x.x /24 and just march through starting at 0 -> 200 for each, ping to see if there is a gateway (should be)
|
|
// and go from there.
|
|
|
|
|
|
// on linux, PING has the setuid bit set - so it runs "as root". isReachable() requires either java to have the setuid bit set
|
|
// (ie: sudo setcap cap_net_raw=ep /usr/lib/jvm/jdk/bin/java) or it requires to be run as root. We run as root in production, so it
|
|
// works.
|
|
val ip = byteArrayOf(10, 0, 0, 0)
|
|
var subnet24Counter = 0
|
|
|
|
while (true) {
|
|
ip[3]++
|
|
|
|
if (ip[3] > 255) {
|
|
ip[3] = 1
|
|
ip[2]++
|
|
subnet24Counter = 0
|
|
}
|
|
if (ip[2] > 255) {
|
|
ip[2] = 0
|
|
ip[1]++
|
|
}
|
|
if (ip[1] > 255) {
|
|
Common.logger.error("Exhausted all ip searches. FATAL ERROR.")
|
|
return null
|
|
}
|
|
|
|
try {
|
|
val address = InetAddress.getByAddress(ip)
|
|
val reachable = address.isReachable(100)
|
|
|
|
if (!reachable) {
|
|
subnet24Counter++
|
|
}
|
|
if (subnet24Counter == 250) {
|
|
// this means that we tried all /24 IPs, and ALL of them came back an "non-responsive". 100ms timeout is OK, because
|
|
// we are on a LAN, that should have MORE than one IP in the cidr, and it should be fairly responsive (ie: <10ms ping)
|
|
|
|
// we found an empty cidr
|
|
ip[3] = 1
|
|
return ip
|
|
}
|
|
} catch (e: IOException) {
|
|
e.printStackTrace()
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scans for existing IP addresses on the network.
|
|
*
|
|
* @param startingIp the IP address to start scanning at
|
|
* @param numberOfHosts the number of hosts to scan for. A /28 is 14 hosts : 2^(32-28) - 2 = 14
|
|
*
|
|
* @return true if no hosts were reachable (pingable)
|
|
*/
|
|
fun scanHosts(startingIp: String, numberOfHosts: Int): Boolean {
|
|
Common.logger.info("Scanning {} hosts, starting at IP {}.", numberOfHosts, startingIp)
|
|
|
|
|
|
val split = startingIp.split(SLASH_REGEX).toTypedArray()
|
|
val a = split[0].toByte()
|
|
val b = split[1].toByte()
|
|
val c = split[2].toByte()
|
|
val d = split[3].toByte()
|
|
|
|
val ip = byteArrayOf(a, b, c, d)
|
|
var counter = numberOfHosts
|
|
|
|
while (counter >= 0) {
|
|
counter--
|
|
|
|
ip[3]++
|
|
if (ip[3] > 255) {
|
|
ip[3] = 1
|
|
ip[2]++
|
|
}
|
|
if (ip[2] > 255) {
|
|
ip[2] = 0
|
|
ip[1]++
|
|
}
|
|
if (ip[1] > 255) {
|
|
Common.logger.error("Exhausted all ip searches. FATAL ERROR.")
|
|
return false
|
|
}
|
|
|
|
try {
|
|
val address = InetAddress.getByAddress(ip)
|
|
val reachable = address.isReachable(100)
|
|
if (reachable) {
|
|
Common.logger.error("IP address {} is already reachable on the network. Unable to continue.", address.hostAddress)
|
|
return false
|
|
}
|
|
} catch (e: IOException) {
|
|
Common.logger.error("Error pinging the IP address", e)
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* @param cidr the CIDR notation, ie: 24, 16, etc. That we want to convert into a netmask, as a string
|
|
*
|
|
* @return the netmask or if there were errors, the default /0 netmask
|
|
*/
|
|
fun getCidrAsNetmask(cidr: Int): String {
|
|
return when (cidr) {
|
|
32 -> "255.255.255.255"
|
|
31 -> "255.255.255.254"
|
|
30 -> "255.255.255.252"
|
|
29 -> "255.255.255.248"
|
|
28 -> "255.255.255.240"
|
|
27 -> "255.255.255.224"
|
|
26 -> "255.255.255.192"
|
|
25 -> "255.255.255.128"
|
|
24 -> "255.255.255.0"
|
|
23 -> "255.255.254.0"
|
|
22 -> "255.255.252.0"
|
|
21 -> "255.255.248.0"
|
|
20 -> "255.255.240.0"
|
|
19 -> "255.255.224.0"
|
|
18 -> "255.255.192.0"
|
|
17 -> "255.255.128.0"
|
|
16 -> "255.255.0.0"
|
|
15 -> "255.254.0.0"
|
|
14 -> "255.252.0.0"
|
|
13 -> "255.248.0.0"
|
|
12 -> "255.240.0.0"
|
|
11 -> "255.224.0.0"
|
|
10 -> "255.192.0.0"
|
|
9 -> "255.128.0.0"
|
|
8 -> "255.0.0.0"
|
|
7 -> "254.0.0.0"
|
|
6 -> "252.0.0.0"
|
|
5 -> "248.0.0.0"
|
|
4 -> "240.0.0.0"
|
|
3 -> "224.0.0.0"
|
|
2 -> "192.0.0.0"
|
|
1 -> "128.0.0.0"
|
|
else -> "0.0.0.0"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param cidr the CIDR notation, ie: 24, 16, etc. That we want to convert into a netmask, as a SIGNED INTEGER (the bits are still
|
|
* correct, but to see this "as unix would", you must convert to an unsigned integer.
|
|
*
|
|
* @return the netmask (as a signed int), or if there were errors, the default /0 netmask
|
|
*/
|
|
fun getCidrAsIntNetmask(cidr: Int): Int {
|
|
return when (cidr) {
|
|
32 -> -1
|
|
31 -> -2
|
|
30 -> -4
|
|
29 -> -8
|
|
28 -> -16
|
|
27 -> -32
|
|
26 -> -64
|
|
25 -> -128
|
|
24 -> -256
|
|
23 -> -512
|
|
22 -> -1024
|
|
21 -> -2048
|
|
20 -> -4096
|
|
19 -> -8192
|
|
18 -> -16384
|
|
17 -> -32768
|
|
16 -> -65536
|
|
15 -> -131072
|
|
14 -> -262144
|
|
13 -> -524288
|
|
12 -> -1048576
|
|
11 -> -2097152
|
|
10 -> -4194304
|
|
9 -> -8388608
|
|
8 -> -16777216
|
|
7 -> -33554432
|
|
6 -> -67108864
|
|
5 -> -134217728
|
|
4 -> -268435456
|
|
3 -> -536870912
|
|
2 -> -1073741824
|
|
1 -> -2147483648
|
|
else -> 0
|
|
}
|
|
}
|
|
|
|
fun getCidrFromMask(mask: String): Int {
|
|
return when (mask) {
|
|
"255.255.255.255" -> 32
|
|
"255.255.255.254" -> 31
|
|
"255.255.255.252" -> 30
|
|
"255.255.255.248" -> 29
|
|
"255.255.255.240" -> 28
|
|
"255.255.255.224" -> 27
|
|
"255.255.255.192" -> 26
|
|
"255.255.255.128" -> 25
|
|
"255.255.255.0" -> 24
|
|
"255.255.254.0" -> 23
|
|
"255.255.252.0" -> 22
|
|
"255.255.248.0" -> 21
|
|
"255.255.240.0" -> 20
|
|
"255.255.224.0" -> 19
|
|
"255.255.192.0" -> 18
|
|
"255.255.128.0" -> 17
|
|
"255.255.0.0" -> 16
|
|
"255.254.0.0" -> 15
|
|
"255.252.0.0" -> 14
|
|
"255.248.0.0" -> 13
|
|
"255.240.0.0" -> 12
|
|
"255.224.0.0" -> 11
|
|
"255.192.0.0" -> 10
|
|
"255.128.0.0" -> 9
|
|
"255.0.0.0" -> 8
|
|
"254.0.0.0" -> 7
|
|
"252.0.0.0" -> 6
|
|
"248.0.0.0" -> 5
|
|
"240.0.0.0" -> 4
|
|
"224.0.0.0" -> 3
|
|
"192.0.0.0" -> 2
|
|
"128.0.0.0" -> 1
|
|
else -> 0
|
|
}
|
|
}
|
|
|
|
private val CIDR2MASK = intArrayOf(
|
|
0x00000000,
|
|
-0x80000000,
|
|
-0x40000000,
|
|
-0x20000000,
|
|
-0x10000000,
|
|
-0x8000000,
|
|
-0x4000000,
|
|
-0x2000000,
|
|
-0x1000000,
|
|
-0x800000,
|
|
-0x400000,
|
|
-0x200000,
|
|
-0x100000,
|
|
-0x80000,
|
|
-0x40000,
|
|
-0x20000,
|
|
-0x10000,
|
|
-0x8000,
|
|
-0x4000,
|
|
-0x2000,
|
|
-0x1000,
|
|
-0x800,
|
|
-0x400,
|
|
-0x200,
|
|
-0x100,
|
|
-0x80,
|
|
-0x40,
|
|
-0x20,
|
|
-0x10,
|
|
-0x8,
|
|
-0x4,
|
|
-0x2,
|
|
-0x1)
|
|
|
|
fun range2Cidr(startIp: String, endIp: String): List<String> {
|
|
var start = toInt(startIp).toLong()
|
|
val end = toInt(endIp).toLong()
|
|
|
|
val pairs: MutableList<String> = ArrayList()
|
|
|
|
while (end >= start) {
|
|
var maxsize = 32.toByte()
|
|
while (maxsize > 0) {
|
|
val mask = CIDR2MASK[maxsize - 1].toLong()
|
|
val maskedBase = start and mask
|
|
if (maskedBase != start) {
|
|
break
|
|
}
|
|
maxsize--
|
|
}
|
|
|
|
val x = ln(end - start + 1.toDouble()) / ln(2.0)
|
|
val maxDiff = (32 - floor(x)).toByte()
|
|
if (maxsize < maxDiff) {
|
|
maxsize = maxDiff
|
|
}
|
|
|
|
val ip = toString(start)
|
|
pairs.add("$ip/$maxsize")
|
|
start += 2.0.pow(32 - maxsize.toDouble()).toLong()
|
|
}
|
|
return pairs
|
|
}
|
|
|
|
|
|
/* Mask to convert unsigned int to a long (i.e. keep 32 bits) */
|
|
private const val UNSIGNED_INT_MASK = 0x0FFFFFFFFL
|
|
|
|
/**
|
|
* Check if the IP address is in the range of a specific IP/CIDR
|
|
*
|
|
* a prefix of 0 will ALWAYS return true
|
|
*
|
|
* @param address the address to check
|
|
* @param networkAddress the network address that will have the other address checked against
|
|
* @param networkPrefix 0-32 the network prefix (subnet) to use for the network address
|
|
*
|
|
* @return true if it is in range
|
|
*/
|
|
fun isInRange(address: Int, networkAddress: Int, networkPrefix: Int): Boolean {
|
|
// System.err.println(" ip: " + IP.toString(address));
|
|
// System.err.println(" networkAddress: " + IP.toString(networkAddress));
|
|
// System.err.println(" networkSubnetPrefix: " + networkPrefix);
|
|
if (networkPrefix == 0) {
|
|
// a prefix of 0 means it is always true (even though the broadcast address is '-1'). So we short-cut it here
|
|
return true
|
|
}
|
|
val netmask = ((1 shl 32 - networkPrefix) - 1).inv()
|
|
|
|
// Calculate base network address
|
|
val network = (networkAddress and netmask and UNSIGNED_INT_MASK.toInt()).toLong()
|
|
// System.err.println(" network " + IP.toString(network));
|
|
|
|
// Calculate broadcast address
|
|
val broadcast = network or netmask.inv().toLong() and UNSIGNED_INT_MASK
|
|
// System.err.println(" broadcast " + IP.toString(broadcast));
|
|
|
|
val addressLong = (address and UNSIGNED_INT_MASK.toInt()).toLong()
|
|
return addressLong in network..broadcast
|
|
}
|
|
|
|
|
|
fun toInt(ipBytes: ByteArray): Int {
|
|
return ipBytes[0].toInt() shl 24 or
|
|
(ipBytes[1].toInt() shl 16) or
|
|
(ipBytes[2].toInt() shl 8) or
|
|
(ipBytes[3].toInt())
|
|
}
|
|
|
|
/**
|
|
* Converts a 32-bit integer into an IPv4 address.
|
|
*/
|
|
fun toString(ipAddress: Int): String {
|
|
val buf = StringBuilder(15)
|
|
buf.append(ipAddress shr 24 and 0xFF)
|
|
buf.append('.')
|
|
buf.append(ipAddress shr 16 and 0xFF)
|
|
buf.append('.')
|
|
buf.append(ipAddress shr 8 and 0xF)
|
|
buf.append('.')
|
|
buf.append(ipAddress and 0xFF)
|
|
return buf.toString()
|
|
}
|
|
|
|
fun toString(ipBytes: ByteArray): String {
|
|
val buf = StringBuilder(15)
|
|
buf.append(ipBytes[0].toUByte())
|
|
buf.append('.')
|
|
buf.append(ipBytes[1].toUByte())
|
|
buf.append('.')
|
|
buf.append(ipBytes[2].toUByte())
|
|
buf.append('.')
|
|
buf.append(ipBytes[3].toUByte())
|
|
return buf.toString()
|
|
}
|
|
|
|
/**
|
|
* Returns the [String] representation of an [InetAddress]. Results are identical to [InetAddress.getHostAddress]
|
|
*
|
|
* @param ip [InetAddress] to be converted to an address string
|
|
*
|
|
* @return `String` containing the text-formatted IP address
|
|
*/
|
|
fun toString(ip: InetAddress): String {
|
|
return (ip as Inet4Address).hostAddress
|
|
}
|
|
|
|
|
|
@Throws(Exception::class)
|
|
fun writeString(ipAddress: Int, writer: Writer) {
|
|
writer.write((ipAddress shr 24 and 0x000000FF).toString())
|
|
writer.write('.'.toInt())
|
|
writer.write((ipAddress shr 16 and 0x000000FF).toString())
|
|
writer.write('.'.toInt())
|
|
writer.write((ipAddress shr 8 and 0x000000FF).toString())
|
|
writer.write('.'.toInt())
|
|
writer.write((ipAddress and 0x000000FF).toString())
|
|
}
|
|
|
|
fun toString(ipAddress: Long): String {
|
|
val ipString = StringBuilder(15)
|
|
ipString.append(ipAddress shr 24 and 0x000000FF)
|
|
ipString.append('.')
|
|
ipString.append(ipAddress shr 16 and 0x000000FF)
|
|
ipString.append('.')
|
|
ipString.append(ipAddress shr 8 and 0x000000FF)
|
|
ipString.append('.')
|
|
ipString.append(ipAddress and 0x000000FF)
|
|
return ipString.toString()
|
|
}
|
|
|
|
fun toBytes(bytes: Int): ByteArray {
|
|
return byteArrayOf((bytes ushr 24 and 0xFF).toByte(),
|
|
(bytes ushr 16 and 0xFF).toByte(),
|
|
(bytes ushr 8 and 0xFF).toByte(),
|
|
(bytes and 0xFF).toByte())
|
|
}
|
|
|
|
fun toInt(ipAsString: String): Int {
|
|
return if (isValid(ipAsString)) {
|
|
val bytes = toBytes(ipAsString)
|
|
|
|
var address = 0
|
|
address = address or (bytes[0].toInt() shl 24)
|
|
address = address or (bytes[1].toInt() shl 16)
|
|
address = address or (bytes[2].toInt() shl 8)
|
|
address = address or bytes[3].toInt()
|
|
|
|
address
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the [Inet4Address] representation of a [String] IP address.
|
|
*
|
|
* This method will treat all IPv4 type addresses as "IPv4 mapped" (see [.getByName])
|
|
*
|
|
* @param ip [String] IP address to be converted to a [Inet4Address]
|
|
* @return [Inet4Address] representation of the `ip` or `null` if not a valid IP address.
|
|
*/
|
|
fun getByName(ip: String): Inet4Address? {
|
|
return if (isValid(ip)) {
|
|
val asBytes = toBytes(ip)
|
|
return Inet4Address.getByAddress(ip, asBytes) as Inet4Address
|
|
} else {
|
|
null
|
|
}
|
|
}
|
|
}
|