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 { val result = mutableListOf() 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>() val privateIpList = getPrivateIpV4List() val available2dArray = Array(privateIpList.size) { arrayOfNulls(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>, Iterable> { 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 { 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.chooseSimilarIp(other: InetAddress) = this.maxByOrNull { it.getIpClassSimilarity(other) } fun Enumeration.forEach(block: (T) -> Unit) { for (i in this) block(i) } fun Enumeration.filter(block: (T) -> Boolean): List { val result = mutableListOf() 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())