Cleaned up host file configuration parsing

This commit is contained in:
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 * 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.OS_WINDOWS
import dorkbox.netUtil.Common.logger import dorkbox.netUtil.Common.logger
import dorkbox.netUtil.IP.toBytes import dorkbox.netUtil.IPv4
import dorkbox.netUtil.IPv6
import java.io.* import java.io.*
import java.net.Inet4Address import java.net.Inet4Address
import java.net.Inet6Address import java.net.Inet6Address
@ -89,12 +90,14 @@ object HostsFileParser {
* @return a [HostsFileEntries] * @return a [HostsFileEntries]
*/ */
fun parse(file: File, vararg charsets: Charset): HostsFileEntries { fun parse(file: File, vararg charsets: Charset): HostsFileEntries {
val hostsFileEntries = HostsFileEntries()
try { try {
if (file.exists() && file.isFile) { if (file.exists() && file.isFile) {
for (charset in charsets) { for (charset in charsets) {
BufferedReader(InputStreamReader(FileInputStream(file), charset)).use { reader -> BufferedReader(InputStreamReader(FileInputStream(file), charset)).use { reader ->
val entries = parse(reader) val entries = parse(reader)
if (entries != HostsFileEntries()) { if (entries != hostsFileEntries) {
return entries return entries
} }
} }
@ -104,7 +107,7 @@ object HostsFileParser {
logger.warn("Failed to load and parse hosts file at " + file.path, e) 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] * @return a [HostsFileEntries]
*/ */
fun parse(reader: Reader): HostsFileEntries { fun parse(reader: Reader): HostsFileEntries {
val buff = BufferedReader(reader)
return try { val ipv4Entries = mutableMapOf<String, Inet4Address>()
val ipv4Entries = mutableMapOf<String, Inet4Address>() val ipv6Entries = mutableMapOf<String, Inet6Address>()
val ipv6Entries = mutableMapOf<String, Inet6Address>()
var line: String reader.useLines { lines ->
while (buff.readLine().also { line = it } != null) { lines.map { line ->
// remove comment // remove comments
val commentPosition = line.indexOf('#') val commentPosition = line.indexOf('#')
if (commentPosition != -1) { 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 // skip empty lines
line = line.trim { it <= ' ' } line.isNotEmpty()
if (line.isEmpty()) { }.map { line ->
continue
}
// split // split
val lineParts: MutableList<String> = ArrayList() val lineParts = mutableListOf<String>()
for (s in WHITESPACES.split(line)) { for (s in WHITESPACES.split(line)) {
if (s.isNotEmpty()) { if (s.isNotEmpty()) {
lineParts.add(s) lineParts.add(s)
} }
} }
lineParts
}.filter { lineParts ->
// a valid line should be [IP, hostname, alias*] // a valid line should be [IP, hostname, alias*]
if (lineParts.size < 2) { // skip invalid lines!
// skip invalid line lineParts.size >= 2
continue }.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 != null) {
if (ipBytes.isEmpty()) { // loop over hostname and aliases, skip invalid IP
// skip invalid IP for (i in 1 until lineParts.size) {
continue val hostname = lineParts[i]
} val hostnameLower = hostname.toLowerCase(Locale.ENGLISH)
val address = InetAddress.getByAddress(hostname, ipBytes)
// loop over hostname and aliases if (address is Inet4Address) {
for (i in 1 until lineParts.size) { val previous = ipv4Entries.put(hostnameLower, address)
val hostname = lineParts[i] if (previous != null) {
val hostnameLower = hostname.toLowerCase(Locale.ENGLISH) // restore, we want to keep the first entry
val address = InetAddress.getByAddress(hostname, ipBytes) ipv4Entries[hostnameLower] = previous
if (address is Inet4Address) { }
val previous = ipv4Entries.put(hostnameLower, address) } else {
if (previous != null) { val previous = ipv6Entries.put(hostnameLower, address as Inet6Address)
// restore, we want to keep the first entry if (previous != null) {
ipv4Entries[hostnameLower] = previous // restore, we want to keep the first entry
} ipv6Entries[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 com.sun.jna.platform.win32.WinError
import dorkbox.netUtil.Common import dorkbox.netUtil.Common
import dorkbox.netUtil.Dns import dorkbox.netUtil.Dns
import dorkbox.netUtil.IPv4 import dorkbox.netUtil.IP
import dorkbox.netUtil.IPv6
import dorkbox.netUtil.jna.windows.IPHlpAPI import dorkbox.netUtil.jna.windows.IPHlpAPI
import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_ADDRESSES_LH import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_ADDRESSES_LH
import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_DNS_SERVER_ADDRESS_XP import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_DNS_SERVER_ADDRESS_XP
@ -189,7 +188,7 @@ object ResolveConf {
var maybeIP = line.substring(i) var maybeIP = line.substring(i)
// There MIGHT be a port appended onto the IP address so we attempt to extract it. // 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('.') i = maybeIP.lastIndexOf('.')
require(i + 1 >= maybeIP.length) { require(i + 1 >= maybeIP.length) {
"error parsing label ${NAMESERVER_ROW_LABEL} in file $path. invalid IP value: $line" "error parsing label ${NAMESERVER_ROW_LABEL} in file $path. invalid IP value: $line"