feat(fls): DNS records from /dns/hosts, as Java's server answers them
Java's DNS server reads its records from the text file /dns/hosts on every query. dnsHosts.ts ports that reading (DNSServer.leseRecordListe, ResourceRecord, Dateisystem.holeDatei) and keeps in the model the records Java answers: type exactly A, MX or NS, and a name Java accepted. The file stays in the file system as it is. Both oracles read 76 hand-picked hosts files (dns-hosts.cases.txt): the Java oracle through DNSServer.holeResourceRecords (DnsHostsCases), the Swift oracle through TopologyDNSHostsFileCodec (--dns-hosts). The Java view matches Java on every case; the model matches Swift except for 33 named cases in twelve reasons: Java splits at single spaces and LF only, has no comments or "IN", turns a TTL that is no int into 3600, keeps duplicates, and empties names with a leading digit. The Swift dump now carries each server's records; the corpus comparison covers them on 16 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -401,6 +401,7 @@ func describeSystem(_ state: TopologyEditorState, _ node: TopologyNode) -> JSON
|
||||
json["dnsServer"] = [
|
||||
"recursiveResolutionEnabled": dns.recursiveResolutionEnabled,
|
||||
"forwardingServerIPAddress": dns.forwardingServerIPAddress ?? NSNull(),
|
||||
"records": dns.typedRecords.map { dnsRecordJSON($0) },
|
||||
] as JSON
|
||||
}
|
||||
if let link = state.remoteLinkConfigurationsByNodeID[node.id] {
|
||||
@@ -614,6 +615,32 @@ func foldPaths(_ cases: [PathCase]) -> [JSON] {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - /dns/hosts cases (packages/fls/test/dns-hosts.cases.txt)
|
||||
|
||||
func dnsRecordJSON(_ record: TopologyDNSResourceRecord) -> [Any] {
|
||||
[record.name.rawValue, record.type.rawValue, Int(record.ttlSeconds), record.target]
|
||||
}
|
||||
|
||||
/// One case per line: a whole hosts file, ASCII with Java-style unicode
|
||||
/// escapes (backslash, u, four hex digits) — the file the Java oracle reads.
|
||||
func readDNSHostsCases(_ url: URL) throws -> [(line: String, text: String)] {
|
||||
let lines = try String(contentsOf: url, encoding: .ascii).components(separatedBy: "\n")
|
||||
return lines.dropLast().map { line in
|
||||
var units: [UInt16] = []
|
||||
var rest = Substring(line)
|
||||
while let first = rest.first {
|
||||
if rest.hasPrefix("\\u"), rest.count >= 6, let unit = UInt16(rest.dropFirst(2).prefix(4), radix: 16) {
|
||||
units.append(unit)
|
||||
rest = rest.dropFirst(6)
|
||||
} else {
|
||||
units.append(contentsOf: String(first).utf16)
|
||||
rest = rest.dropFirst()
|
||||
}
|
||||
}
|
||||
return (line, String(decoding: units, as: UTF16.self))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Main
|
||||
|
||||
if CommandLine.arguments.count == 4, CommandLine.arguments[1] == "--path-folding" {
|
||||
@@ -641,6 +668,15 @@ if CommandLine.arguments.count == 4, CommandLine.arguments[1] == "--strings" {
|
||||
exit(0)
|
||||
}
|
||||
|
||||
if CommandLine.arguments.count == 4, CommandLine.arguments[1] == "--dns-hosts" {
|
||||
let cases = try readDNSHostsCases(URL(fileURLWithPath: CommandLine.arguments[2]))
|
||||
let output: [[Any]] = cases.map { [$0.line, TopologyDNSHostsFileCodec.records(from: $0.text).map { dnsRecordJSON($0) }] }
|
||||
let data = try JSONSerialization.data(withJSONObject: output, options: [.prettyPrinted, .sortedKeys])
|
||||
try (data + Data("\n".utf8)).write(to: URL(fileURLWithPath: CommandLine.arguments[3]))
|
||||
print("dns hosts: \(cases.count) cases")
|
||||
exit(0)
|
||||
}
|
||||
|
||||
if CommandLine.arguments.count == 4, CommandLine.arguments[1] == "--corpus" {
|
||||
let summary = try readCorpus(root: URL(fileURLWithPath: CommandLine.arguments[2]))
|
||||
let data = try JSONSerialization.data(withJSONObject: summary, options: [.prettyPrinted, .sortedKeys])
|
||||
|
||||
Reference in New Issue
Block a user