2020-08-08 20:23:27 +02:00
/ *
* 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
2021-04-05 23:08:10 +02:00
import java.net.*
2020-08-08 20:23:27 +02:00
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.
* /
2020-08-20 22:38:18 +02:00
@Suppress ( " EXPERIMENTAL_API_USAGE " )
2020-08-08 20:23:27 +02:00
object IPv4 {
2021-04-06 23:42:45 +02:00
/ * *
* Gets the version number .
* /
2022-03-03 00:39:25 +01:00
const val version = " 2.9.1 "
2021-04-06 23:42:45 +02:00
2020-08-08 20:23:27 +02:00
/ * *
* 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 )
2020-09-10 00:22:47 +02:00
/ * *
* true if there is an IPv4 network interface available
* /
val isAvailable : Boolean by lazy {
// Retrieve the list of available network interfaces.
val netInterfaces = mutableListOf < NetworkInterface > ( )
try {
val interfaces = NetworkInterface . getNetworkInterfaces ( )
if ( interfaces != null ) {
while ( interfaces . hasMoreElements ( ) ) {
val iface : NetworkInterface = interfaces . nextElement ( )
// Use the interface with proper INET addresses only.
if ( SocketUtils . addressesFromNetworkInterface ( iface ) . hasMoreElements ( ) ) {
netInterfaces . add ( iface )
}
}
}
} catch ( e : SocketException ) {
Common . logger . warn ( " Failed to retrieve the list of available network interfaces " , e )
}
// check if IPv4 is possible.
var ipv4Possible = false
loop @ for ( iface in netInterfaces ) {
val i = SocketUtils . addressesFromNetworkInterface ( iface )
while ( i . hasMoreElements ( ) ) {
val addr : InetAddress = i . nextElement ( )
if ( addr is Inet4Address ) {
ipv4Possible = true
break @loop
}
}
}
ipv4Possible
}
2020-08-08 20:23:27 +02:00
/ * *
* The [ Inet4Address ] that represents the IPv4 loopback address ' 127.0 . 0.1 '
* /
val LOCALHOST : Inet4Address by lazy {
2020-09-08 23:54:16 +02:00
// Create IPv4 address, this will ALWAYS work
2020-08-08 20:23:27 +02:00
InetAddress . getByAddress ( " localhost " , byteArrayOf ( 127 , 0 , 0 , 1 ) ) as Inet4Address
}
2020-09-08 23:54:16 +02:00
/ * *
* The [ Inet4Address ] that represents the IPv4 wildcard address ' 0.0 . 0.0 '
* /
val WILDCARD : Inet4Address by lazy {
// Create IPv4 address, this will ALWAYS work
2020-09-18 03:01:51 +02:00
InetAddress . getByAddress ( null , byteArrayOf ( 0 , 0 , 0 , 0 ) ) as Inet4Address
2020-09-08 23:54:16 +02:00
}
2021-04-08 19:59:25 +02:00
/ * *
* the length of an address in this particular family .
* /
val length = 4
2020-08-08 20:23:27 +02:00
2021-04-08 19:59:25 +02:00
private val SLASH _REGEX = " \\ . " . toRegex ( )
2020-08-08 20:23:27 +02:00
2020-08-11 16:24:02 +02:00
private val private10 = toInt ( " 10.0.0.0 " )
private val private172 = toInt ( " 172.16.0.0 " )
private val private192 = toInt ( " 192.168.0.0 " )
2020-08-20 17:15:18 +02:00
private val loopback127 = toInt ( " 127.0.0.0 " )
2020-08-11 16:24:02 +02:00
2021-04-08 19:59:25 +02:00
/ * *
* @return if the specified address is of this family type
* /
fun isFamily ( address : InetAddress ) : Boolean {
return address is Inet4Address
}
2020-08-11 16:24:02 +02:00
/ * *
* Determines if this IP address is a private address or not .
*
* Private addresses are defined as
* 10.0 . 0.0 / 8
* 172.16 . 0.0 / 12
* 192.168 . 0.0 / 16
* /
2021-04-05 23:08:10 +02:00
fun isSiteLocal ( ipAsString : String ) : Boolean {
return isSiteLocal ( toInt ( ipAsString ) )
2020-08-11 16:24:02 +02:00
}
/ * *
* Determines if this IP address is a private address or not .
*
* Private addresses are defined as
* 10.0 . 0.0 / 8
* 172.16 . 0.0 / 12
* 192.168 . 0.0 / 16
* /
2021-04-05 23:08:10 +02:00
fun isSiteLocal ( ipAsInt : Int ) : Boolean {
2020-08-11 16:24:02 +02:00
// check from smaller to larger
return isInRange ( ipAsInt , private192 , 16 ) || isInRange ( ipAsInt , private172 , 12 ) || isInRange ( ipAsInt , private10 , 8 )
}
2020-08-18 01:51:57 +02:00
/ * *
* A loopback address is defined as
* 127.0 . 0.0 / 8
* /
fun isLoopback ( ipAsString : String ) : Boolean {
2020-08-20 17:15:18 +02:00
return isInRange ( toInt ( ipAsString ) , loopback127 , 8 )
2020-08-18 01:51:57 +02:00
}
/ * *
* A loopback address is defined as
* 127.0 . 0.0 / 8
* /
fun isLoopback ( ipAsInt : Int ) : Boolean {
2020-08-20 17:15:18 +02:00
return isInRange ( ipAsInt , loopback127 , 8 )
2020-08-18 01:51:57 +02:00
}
2020-08-08 20:23:27 +02:00
/ * *
* 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 ( !is Valid ( 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
}
2020-08-08 22:26:49 +02:00
@Suppress ( " NAME_SHADOWING " )
2020-08-08 20:23:27 +02:00
var from = from
var i = ip . indexOf ( '.' , from )
if ( i <= 0 || !is ValidIpV4Word ( ip , from , i ) ) {
return false
}
from = i + 1
i = ip . indexOf ( '.' , from )
if ( i <= 0 || !is ValidIpV4Word ( ip , from , i ) ) {
return false
}
from = i + 1
i = ip . indexOf ( '.' , from )
if ( i <= 0 || !is ValidIpV4Word ( ip , from , i ) ) {
return false
}
if ( i <= 0 || !is ValidIpV4Word ( ip , i + 1 , toExcluded ) ) {
return false
}
return true
}
private fun isValidIpV4Word ( word : CharSequence , from : Int , toExclusive : Int ) : Boolean {
val len = toExclusive - from
var c0 = ' '
2020-08-08 22:26:49 +02:00
var c1 : Char
2020-08-08 20:23:27 +02:00
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'
}
2021-04-06 23:42:45 +02:00
fun toBytesOrNull ( ip : String ) : ByteArray ? {
2020-08-08 20:23:27 +02:00
if ( !is Valid ( ip ) ) {
return null
}
2020-08-08 22:26:49 +02:00
var i : Int
2020-08-20 00:27:51 +02:00
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 ) )
2020-08-08 20:23:27 +02:00
}
2021-04-08 15:26:53 +02:00
/ * *
2021-04-08 19:59:25 +02:00
* Creates an byte [ ] based on an ip Address String . No error handling is performed here .
2021-04-08 15:26:53 +02:00
* /
2020-08-08 20:23:27 +02:00
fun toBytes ( ip : String ) : ByteArray {
2020-08-08 22:26:49 +02:00
var i : Int
2020-08-20 00:27:51 +02:00
var index = ip . indexOf ( '.' , 1 )
val a = ipv4WordToByte ( ip , 0 , index )
i = index
index = ip . indexOf ( '.' , index + 2 )
val b = ipv4WordToByte ( ip , i + 1 , index )
i = index
index = ip . indexOf ( '.' , index + 2 )
val c = ipv4WordToByte ( ip , i + 1 , index )
i = index
return byteArrayOf ( a , b , c , ipv4WordToByte ( ip , i + 1 , ip . length ) )
2020-08-08 20:23:27 +02:00
}
2021-04-08 19:59:25 +02:00
/ * *
* Creates an int [ ] based on an ip address String . No error handling is performed here .
* /
fun toInts ( ip : String ) : IntArray {
var i : Int
var index = ip . indexOf ( '.' , 1 )
val a = ipv4WordToInt ( ip , 0 , index )
i = index
index = ip . indexOf ( '.' , index + 2 )
val b = ipv4WordToInt ( ip , i + 1 , index )
i = index
index = ip . indexOf ( '.' , index + 2 )
val c = ipv4WordToInt ( ip , i + 1 , index )
i = index
return intArrayOf ( a , b , c , ipv4WordToInt ( ip , i + 1 , ip . length ) )
}
2020-08-08 20:23:27 +02:00
private fun decimalDigit ( str : CharSequence , pos : Int ) : Int {
return str [ pos ] - '0'
}
private fun ipv4WordToByte ( ip : CharSequence , from : Int , toExclusive : Int ) : Byte {
2021-04-08 19:59:25 +02:00
return ipv4WordToInt ( ip , from , toExclusive ) . toByte ( )
}
private fun ipv4WordToInt ( ip : CharSequence , from : Int , toExclusive : Int ) : Int {
2020-08-08 20:23:27 +02:00
var newFrom = from
2021-04-08 19:59:25 +02:00
2020-08-08 20:23:27 +02:00
var ret = decimalDigit ( ip , newFrom )
newFrom ++
if ( newFrom == toExclusive ) {
2021-04-08 19:59:25 +02:00
return ret
2020-08-08 20:23:27 +02:00
}
ret = ret * 10 + decimalDigit ( ip , newFrom )
newFrom ++
return if ( newFrom == toExclusive ) {
2021-04-08 19:59:25 +02:00
ret
} else {
ret * 10 + decimalDigit ( ip , newFrom )
}
2020-08-08 20:23:27 +02:00
}
2021-04-08 15:26:53 +02:00
/ * *
* Finds the first available subnet ( as CIDR ) with a corresponding gateway
* /
fun findFreeSubnet24Cidr ( ) : ByteArray ? {
2020-08-08 20:23:27 +02:00
// 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 "
2020-08-20 00:27:51 +02:00
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 "
2020-08-08 20:23:27 +02:00
else -> " 0.0.0.0 "
}
}
/ * *
2021-04-26 23:22:24 +02:00
* @param cidrPrefix the CIDR notation , ie : / 24 , / 16 , etc that we want to convert into a netmask
2020-08-08 20:23:27 +02:00
*
* @return the netmask ( as a signed int ) , or if there were errors , the default / 0 netmask
* /
2021-04-26 23:22:24 +02:00
fun cidrPrefixToSubnetMask ( cidrPrefix : Int ) : Int {
/ * *
* Perform the shift on a long and downcast it to int afterwards .
* This is necessary to handle a cidrPrefix of zero correctly .
* The left shift operator on an int only uses the five least
* significant bits of the right - hand operand . Thus - 1 < < 32 evaluates
* to - 1 instead of 0. The left shift operator applied on a long
* uses the six least significant bits .
*
* Also see https : //github.com/netty/netty/issues/2767
* /
return ( - 1L shl 32 - cidrPrefix and - 0x1 ) . toInt ( )
}
fun cidrPrefixFromSubnetMask ( mask : String ) : Int {
2020-08-08 20:23:27 +02:00
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
}
}
2020-08-20 00:27:51 +02:00
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 )
2020-08-08 20:23:27 +02:00
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 )
2020-08-18 22:34:13 +02:00
val maxDiff = ( 32 - floor ( x ) ) . toInt ( ) . toByte ( )
2020-08-08 20:23:27 +02:00
if ( maxsize < maxDiff ) {
maxsize = maxDiff
}
val ip = toString ( start )
pairs . add ( " $ip / $maxsize " )
start += 2.0 . pow ( 32 - maxsize . toDouble ( ) ) . toLong ( )
}
return pairs
}
/ * *
* 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
* /
2020-08-20 22:38:18 +02:00
fun isInRange ( address : String , networkAddress : String , networkPrefix : Int ) : Boolean {
return isInRange ( toInt ( address ) , toInt ( networkAddress ) , networkPrefix )
}
/ * *
* 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
* /
2020-08-08 20:23:27 +02:00
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
2020-08-20 17:33:34 +02:00
val network = ( networkAddress and netmask ) . toUInt ( )
// System.err.println(" network " + IPv4.toString(network.toInt()))
2020-08-08 20:23:27 +02:00
// Calculate broadcast address
2020-08-20 17:33:34 +02:00
val broadcast = network or netmask . inv ( ) . toUInt ( )
// System.err.println(" broadcast " + IPv4.toString(broadcast.toInt()))
2020-08-08 20:23:27 +02:00
2020-08-20 17:33:34 +02:00
// val addressLong = (address.toLong() and UNSIGNED_INT_MASK)
return address . toUInt ( ) in network .. broadcast
2020-08-08 20:23:27 +02:00
}
2021-04-08 19:59:25 +02:00
/ * *
2021-04-26 23:22:24 +02:00
* Converts a byte array into a 32 - bit integer
2021-04-08 19:59:25 +02:00
* /
2020-08-08 20:23:27 +02:00
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 ( ) )
}
2021-04-26 23:22:24 +02:00
/ * *
* Converts an IP address into a 32 - bit integer
* /
fun toInt ( ipAsString : String ) : Int {
return if ( isValid ( ipAsString ) ) {
toIntUnsafe ( ipAsString )
} else {
0
}
}
/ * *
* Converts an IP address into a 32 - bit integer , no validity checks are performed
* /
fun toIntUnsafe ( ipAsString : String ) : Int {
val bytes = toBytes ( ipAsString )
var address = 0
for ( element in bytes ) {
address = address shl 8
address = address or ( element . toInt ( ) and 0xff )
}
return address
}
2020-08-08 20:23:27 +02:00
/ * *
2021-04-08 19:59:25 +02:00
* Converts a 32 - bit integer into a dotted - quad IPv4 address .
2020-08-08 20:23:27 +02:00
* /
fun toString ( ipAddress : Int ) : String {
val buf = StringBuilder ( 15 )
2021-04-08 19:59:25 +02:00
toString ( ipAddress , buf )
return buf . toString ( )
}
/ * *
* Converts a 32 - bit integer into a dotted - quad IPv4 address .
* /
fun toString ( ipAddress : Int , buf : StringBuilder ) {
2020-08-08 20:23:27 +02:00
buf . append ( ipAddress shr 24 and 0xFF )
buf . append ( '.' )
buf . append ( ipAddress shr 16 and 0xFF )
buf . append ( '.' )
2020-08-20 00:27:51 +02:00
buf . append ( ipAddress shr 8 and 0xFF )
2020-08-08 20:23:27 +02:00
buf . append ( '.' )
buf . append ( ipAddress and 0xFF )
}
2021-04-08 19:59:25 +02:00
/ * *
* Converts a byte array into a dotted - quad IPv4 address .
* /
2020-08-08 20:23:27 +02:00
fun toString ( ipBytes : ByteArray ) : String {
val buf = StringBuilder ( 15 )
2021-04-08 19:59:25 +02:00
toString ( ipBytes , buf )
return buf . toString ( )
}
/ * *
* Converts a byte array into a dotted - quad IPv4 address .
* /
fun toString ( ipBytes : ByteArray , buf : StringBuilder ) {
2020-08-08 22:26:49 +02:00
buf . append ( ipBytes [ 0 ] . toInt ( ) and 0xFF )
2020-08-08 20:23:27 +02:00
buf . append ( '.' )
2020-08-08 22:26:49 +02:00
buf . append ( ipBytes [ 1 ] . toInt ( ) and 0xFF )
2020-08-08 20:23:27 +02:00
buf . append ( '.' )
2020-08-08 22:26:49 +02:00
buf . append ( ipBytes [ 2 ] . toInt ( ) and 0xFF )
2020-08-08 20:23:27 +02:00
buf . append ( '.' )
2020-08-08 22:26:49 +02:00
buf . append ( ipBytes [ 3 ] . toInt ( ) and 0xFF )
2021-04-08 19:59:25 +02:00
}
/ * *
* Converts an int array into a dotted - quad IPv4 address .
* /
fun toString ( ipInts : IntArray ) : String {
val buf = StringBuilder ( 15 )
toString ( ipInts , buf )
2020-08-08 20:23:27 +02:00
return buf . toString ( )
}
2021-04-08 19:59:25 +02:00
/ * *
* Converts an int array into a dotted - quad IPv4 address .
* /
fun toString ( ipInts : IntArray , buf : StringBuilder ) {
buf . append ( ipInts [ 0 ] )
buf . append ( '.' )
buf . append ( ipInts [ 1 ] )
buf . append ( '.' )
buf . append ( ipInts [ 2 ] )
buf . append ( '.' )
buf . append ( ipInts [ 3 ] )
}
2020-08-08 20:23:27 +02:00
/ * *
* 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
* /
2020-09-08 23:54:16 +02:00
fun toString ( ip : Inet4Address ) : String {
return ip . hostAddress
2020-08-08 20:23:27 +02:00
}
@Throws ( Exception :: class )
fun writeString ( ipAddress : Int , writer : Writer ) {
2020-08-20 00:27:51 +02:00
writer . write ( ( ipAddress shr 24 and 0xFF ) . toString ( ) )
2020-08-08 20:23:27 +02:00
writer . write ( '.' . toInt ( ) )
2020-08-20 00:27:51 +02:00
writer . write ( ( ipAddress shr 16 and 0xFF ) . toString ( ) )
2020-08-08 20:23:27 +02:00
writer . write ( '.' . toInt ( ) )
2020-08-20 00:27:51 +02:00
writer . write ( ( ipAddress shr 8 and 0xFF ) . toString ( ) )
2020-08-08 20:23:27 +02:00
writer . write ( '.' . toInt ( ) )
2020-08-20 00:27:51 +02:00
writer . write ( ( ipAddress and 0xFF ) . toString ( ) )
2020-08-08 20:23:27 +02:00
}
fun toString ( ipAddress : Long ) : String {
val ipString = StringBuilder ( 15 )
2020-08-20 00:27:51 +02:00
ipString . append ( ipAddress shr 24 and 0xFF )
2020-08-08 20:23:27 +02:00
ipString . append ( '.' )
2020-08-20 00:27:51 +02:00
ipString . append ( ipAddress shr 16 and 0xFF )
2020-08-08 20:23:27 +02:00
ipString . append ( '.' )
2020-08-20 00:27:51 +02:00
ipString . append ( ipAddress shr 8 and 0xFF )
2020-08-08 20:23:27 +02:00
ipString . append ( '.' )
2020-08-20 00:27:51 +02:00
ipString . append ( ipAddress and 0xFF )
2020-08-08 20:23:27 +02:00
return ipString . toString ( )
}
fun toBytes ( bytes : Int ) : ByteArray {
2020-08-20 00:27:51 +02:00
return byteArrayOf ( ( bytes shr 24 and 0xFF ) . toByte ( ) ,
( bytes shr 16 and 0xFF ) . toByte ( ) ,
( bytes shr 8 and 0xFF ) . toByte ( ) ,
2020-08-08 20:23:27 +02:00
( bytes and 0xFF ) . toByte ( ) )
}
2020-09-08 23:02:34 +02:00
/ * *
* 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 ]
2021-04-29 01:20:09 +02:00
* @return [ Inet4Address ] representation of the `ip` or an exception if not a valid IP address .
2020-09-08 23:02:34 +02:00
* /
2021-04-29 01:20:09 +02:00
@Throws ( UnknownHostException :: class )
2021-04-27 01:01:33 +02:00
fun toAddressUnsafe ( ip : String ) : Inet4Address {
2020-09-08 23:02:34 +02:00
val asBytes = toBytes ( ip )
2020-09-18 03:01:51 +02:00
return Inet4Address . getByAddress ( null , asBytes ) as Inet4Address
2020-09-08 23:02:34 +02:00
}
2020-08-08 20:23:27 +02:00
/ * *
* 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 .
* /
2021-04-27 01:01:33 +02:00
fun toAddress ( ip : String ) : Inet4Address ? {
2020-08-08 20:23:27 +02:00
return if ( isValid ( ip ) ) {
2021-04-27 01:01:33 +02:00
return toAddressUnsafe ( ip )
2020-08-08 20:23:27 +02:00
} else {
null
}
}
2021-04-08 19:59:25 +02:00
/ * *
* Truncates an address to the specified number of bits . For example ,
* truncating the address 10.1 . 2.3 to 8 bits would yield 10.0 . 0.0 .
*
* @param address The source address
* @param maskLength The number of bits to truncate the address to .
* /
fun truncate ( address : Inet4Address , maskLength : Int ) : InetAddress ? {
val maxMaskLength = IPv6 . length * 8
require ( ! ( maskLength < 0 || maskLength > maxMaskLength ) ) { " invalid mask length " }
if ( maskLength == maxMaskLength ) {
return address
}
val bytes = address . address
for ( i in maskLength / 8 + 1 until bytes . size ) {
bytes [ i ] = 0
}
val maskBits = maskLength % 8
var bitmask = 0
for ( i in 0 until maskBits ) {
bitmask = bitmask or ( 1 shl 7 - i )
}
bytes [ maskLength / 8 ] = ( bytes [ maskLength / 8 ] . toInt ( ) and bitmask ) . toByte ( )
return try {
InetAddress . getByAddress ( bytes )
} catch ( e : UnknownHostException ) {
throw IllegalArgumentException ( " invalid address " )
}
}
2020-08-08 20:23:27 +02:00
}