You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
4.3 KiB

package tv.anypoint.proxy.util
import jakarta.servlet.http.HttpServletRequest
import kotlinx.coroutines.*
import java.net.Inet4Address
import java.net.InetAddress
import java.net.NetworkInterface
import java.util.*
object NetworkUtil {
fun getPrivateIpV4(setTopBoxAddress: InetAddress): String = getPrivateIpV4List()
.chooseSimilarIp(setTopBoxAddress)?.hostAddress
?: throw RuntimeException("셋톱과 공유하는 private IP가 없습니다")
/**
* Gives the private IP of this device
* @return The private IP
*/
fun getPrivateIpV4List(): List<InetAddress> {
val result = mutableListOf<InetAddress>()
NetworkInterface.getNetworkInterfaces().forEach { n ->
result += n.inetAddresses.filter { it is Inet4Address && it.isSiteLocalAddress }
}
return result
}
/**
* Gives the list of all open IPs of the private network
* in which this device is connected to.
*
* For the time saving, this method performs multithreaded
* ping test for 5 seconds in each thread, excluding
* 192.168.0.1 and this device's private IP.
*
* @return List of all open IPs as [InetAddress]
*/
fun findAllOpenPrivateIps(skipSelf: Boolean = false) = runBlocking {
val todos = mutableListOf<Deferred<Unit?>>()
val privateIpList = getPrivateIpV4List()
val available2dArray = Array(privateIpList.size) { arrayOfNulls<InetAddress>(256) }
privateIpList.forEachIndexed { i, inetAddress ->
todos += Inet4Iterator(inetAddress, skipSelf)
.map {
async(Dispatchers.IO) {
if (it.second.isReachable(1000)) available2dArray[i][it.first] = it.second else null
}
}
}
todos.awaitAll()
return@runBlocking available2dArray.flatten().filterNotNull()
}
/**
* The iterator that iterates all near IPs from the given [originalIp]
*
* If the given IP is a.b.c.d, then it will iterate from
* a.b.c.2 to a.b.c.255, when start=2 and [end]=255 (default)
*/
class Inet4Iterator(
private val originalIp: InetAddress,
private val skipOriginal: Boolean = true,
start: UInt = 2u,
private val end: UInt = 255u
) : Iterator<Pair<Int, InetAddress>>, Iterable<Pair<Int, InetAddress>> {
private val a: UByte
private val b: UByte
private val c: UByte
private val skip: UByte
private var d = start
init {
val addressBytes = originalIp.address
a = addressBytes[0].toUByte()
b = addressBytes[1].toUByte()
c = addressBytes[2].toUByte()
skip = addressBytes[3].toUByte()
}
override fun hasNext(): Boolean {
return if (skipOriginal && skip == 0xFFu.toUByte()) d < end else d < (end + 1u)
}
override fun next(): Pair<Int, InetAddress> {
val result = Pair(d.toInt(), Inet4Util.fromIpClasses(a, b, c, d.toUByte()))
d += if (d == skip - 1u) 2u else 1u
return result
}
override fun iterator() = this
}
object Inet4Util {
fun fromIpClasses(a: UByte, b: UByte, c: UByte, d: UByte): InetAddress {
return Inet4Address.getByName("$a.$b.$c.$d")
}
}
fun InetAddress.getIpClassSimilarity(other: InetAddress): Int {
if (this::class != other::class) return 0
val ipClasses = this.address
val otherIpClasses = other.address
for (i in 0 until ipClasses.size.coerceAtMost(otherIpClasses.size)) {
if (ipClasses[i] != otherIpClasses[i]) return i
}
return ipClasses.size
}
fun List<InetAddress>.chooseSimilarIp(other: InetAddress) = this.maxByOrNull { it.getIpClassSimilarity(other) }
fun <T> Enumeration<T>.forEach(block: (T) -> Unit) {
for (i in this) block(i)
}
fun <T> Enumeration<T>.filter(block: (T) -> Boolean): List<T> {
val result = mutableListOf<T>()
for (i in this) if (block(i)) result += i
return result
}
}
fun HttpServletRequest.getIpAddressString(): String = this.getHeader("X-FORWARDED-FOR") ?: this.remoteAddr
fun HttpServletRequest.getIpAddress(): InetAddress = InetAddress.getByName(this.getIpAddressString())