Cleaned up host file configuration parsing

connection_type_change
Robinson 2021-04-08 19:58:31 +02:00
parent 6eee42cb35
commit 924fbb5e5d
3 changed files with 74 additions and 56 deletions

View File

@ -22,4 +22,24 @@ import java.net.Inet6Address
/**
* A container of hosts file entries
*/
data class HostsFileEntries(val ipv4Entries: Map<String, Inet4Address> = emptyMap(), val ipv6Entries: Map<String, Inet6Address> = emptyMap())
data class HostsFileEntries(val ipv4Entries: Map<String, Inet4Address> = emptyMap(),
val ipv6Entries: Map<String, Inet6Address> = emptyMap()) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as HostsFileEntries
if (ipv4Entries != other.ipv4Entries) return false
if (ipv6Entries != other.ipv6Entries) return false
return true
}
override fun hashCode(): Int {
var result = ipv4Entries.hashCode()
result = 31 * result + ipv6Entries.hashCode()
return result
}
}

View File

@ -18,7 +18,8 @@ package dorkbox.netUtil.dnsUtils
import dorkbox.netUtil.Common.OS_WINDOWS
import dorkbox.netUtil.Common.logger
import dorkbox.netUtil.IP.toBytes
import dorkbox.netUtil.IPv4
import dorkbox.netUtil.IPv6
import java.io.*
import java.net.Inet4Address
import java.net.Inet6Address
@ -89,12 +90,14 @@ object HostsFileParser {
* @return a [HostsFileEntries]
*/
fun parse(file: File, vararg charsets: Charset): HostsFileEntries {
val hostsFileEntries = HostsFileEntries()
try {
if (file.exists() && file.isFile) {
for (charset in charsets) {
BufferedReader(InputStreamReader(FileInputStream(file), charset)).use { reader ->
val entries = parse(reader)
if (entries != HostsFileEntries()) {
if (entries != hostsFileEntries) {
return entries
}
}
@ -104,7 +107,7 @@ object HostsFileParser {
logger.warn("Failed to load and parse hosts file at " + file.path, e)
}
return HostsFileEntries()
return hostsFileEntries
}
/**
@ -115,74 +118,70 @@ object HostsFileParser {
* @return a [HostsFileEntries]
*/
fun parse(reader: Reader): HostsFileEntries {
val buff = BufferedReader(reader)
return try {
val ipv4Entries = mutableMapOf<String, Inet4Address>()
val ipv6Entries = mutableMapOf<String, Inet6Address>()
val ipv4Entries = mutableMapOf<String, Inet4Address>()
val ipv6Entries = mutableMapOf<String, Inet6Address>()
var line: String
while (buff.readLine().also { line = it } != null) {
// remove comment
reader.useLines { lines ->
lines.map { line ->
// remove comments
val commentPosition = line.indexOf('#')
if (commentPosition != -1) {
line = line.substring(0, commentPosition)
line.substring(0, commentPosition)
} else {
line
}
}.map { line ->
// trim lines
line.trim()
}.filter { line ->
// skip empty lines
line = line.trim { it <= ' ' }
if (line.isEmpty()) {
continue
}
line.isNotEmpty()
}.map { line ->
// split
val lineParts: MutableList<String> = ArrayList()
val lineParts = mutableListOf<String>()
for (s in WHITESPACES.split(line)) {
if (s.isNotEmpty()) {
lineParts.add(s)
}
}
lineParts
}.filter { lineParts ->
// a valid line should be [IP, hostname, alias*]
if (lineParts.size < 2) {
// skip invalid line
continue
// skip invalid lines!
lineParts.size >= 2
}.forEach { lineParts ->
val ip = lineParts[0]
val ipBytes = when {
IPv4.isValid(ip) -> IPv4.toBytes(ip)
IPv6.isValid(ip) -> IPv6.toBytes(ip)
else -> null
}
val ipBytes = toBytes(lineParts[0])
if (ipBytes.isEmpty()) {
// skip invalid IP
continue
}
// loop over hostname and aliases
for (i in 1 until lineParts.size) {
val hostname = lineParts[i]
val hostnameLower = hostname.toLowerCase(Locale.ENGLISH)
val address = InetAddress.getByAddress(hostname, ipBytes)
if (address is Inet4Address) {
val previous = ipv4Entries.put(hostnameLower, address)
if (previous != null) {
// restore, we want to keep the first entry
ipv4Entries[hostnameLower] = previous
}
} else {
val previous = ipv6Entries.put(hostnameLower, address as Inet6Address)
if (previous != null) {
// restore, we want to keep the first entry
ipv6Entries[hostnameLower] = previous
if (ipBytes != null) {
// loop over hostname and aliases, skip invalid IP
for (i in 1 until lineParts.size) {
val hostname = lineParts[i]
val hostnameLower = hostname.toLowerCase(Locale.ENGLISH)
val address = InetAddress.getByAddress(hostname, ipBytes)
if (address is Inet4Address) {
val previous = ipv4Entries.put(hostnameLower, address)
if (previous != null) {
// restore, we want to keep the first entry
ipv4Entries[hostnameLower] = previous
}
} else {
val previous = ipv6Entries.put(hostnameLower, address as Inet6Address)
if (previous != null) {
// restore, we want to keep the first entry
ipv6Entries[hostnameLower] = previous
}
}
}
}
}
HostsFileEntries(ipv4Entries, ipv6Entries)
} finally {
try {
buff.close()
} catch (e: IOException) {
logger
.warn("Failed to close a reader", e)
}
}
return HostsFileEntries(ipv4Entries, ipv6Entries)
}
}

View File

@ -5,8 +5,7 @@ import com.sun.jna.Pointer
import com.sun.jna.platform.win32.WinError
import dorkbox.netUtil.Common
import dorkbox.netUtil.Dns
import dorkbox.netUtil.IPv4
import dorkbox.netUtil.IPv6
import dorkbox.netUtil.IP
import dorkbox.netUtil.jna.windows.IPHlpAPI
import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_ADDRESSES_LH
import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_DNS_SERVER_ADDRESS_XP
@ -189,7 +188,7 @@ object ResolveConf {
var maybeIP = line.substring(i)
// There MIGHT be a port appended onto the IP address so we attempt to extract it.
if (!IPv4.isValid(maybeIP) && !IPv6.isValid(maybeIP)) {
if (!IP.isValid(maybeIP)) {
i = maybeIP.lastIndexOf('.')
require(i + 1 >= maybeIP.length) {
"error parsing label ${NAMESERVER_ROW_LABEL} in file $path. invalid IP value: $line"