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:
2026-09-30 06:38:01 +00:00
co-authored by Claude Opus 5.5
parent 1cc0dfd899
commit c80d212c90
17 changed files with 2518 additions and 11 deletions
+4 -2
View File
@@ -12,7 +12,7 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
| `src/model/node.ts` | `Model/TopologyNode.swift` | übertragen; Standard-Ports und -Namen wie Java; ein gegebener Name bleibt, auch leer (Java lässt Router unbenannt) |
| `src/model/link.ts`, `src/model/graph.ts` | `Model/TopologyLink.swift`, `Model/TopologyGraph.swift` | übertragen, bisher nur die Daten (Abfragen folgen mit dem Editor) |
| `src/model/documentation.ts` | `Model/TopologyDocumentation.swift` | übertragen; FNV-Kennung mit BigInt, gegen die IDs aus dem Swift-Import geprüft |
| `src/runtime/config.ts` | `State/TopologyEditorState.swift` (Laufzeit-Konfigurationen, WLAN eines Rechners, DHCP, Portweiterleitung, Remote Link ohne LAN-Transport, Programme und ihre Konfiguration, Firewall); dazu Echo-Server- und Client-Konfiguration, die Swift nicht speichert | übertragen; Java-Beschriftungen der Oberfläche weggelassen |
| `src/runtime/config.ts` | `State/TopologyEditorState.swift` (Laufzeit-Konfigurationen, WLAN eines Rechners, DHCP, Portweiterleitung, Remote Link ohne LAN-Transport, Programme und ihre Konfiguration, Firewall, DNS-Einträge aus `State/TopologyDNSResolver.swift`); dazu Echo-Server- und Client-Konfiguration, die Swift nicht speichert | übertragen; Java-Beschriftungen der Oberfläche weggelassen |
| `src/runtime/engine.ts` | `State/TopologyNetworkRuntimeEngine.swift` Z. ~313–3585, ~4753–4850 | übertragen: Warteschlange, Uhr, Ethernet, Switch, ARP, IPv4, ICMP, Routing, Firewall. Klasse statt Werttyp. Noch nicht übertragen (wirft `NotPortedError`): Gateway-NAT, UDP, TCP, DHCP, DNS, RIP, Remote Link, WLAN |
| `src/runtime/ipv4.ts` | Engine `parseIPv4`/`sameSubnet` | übertragen |
| `src/runtime/packets.ts` | Engine, Paketmodelle | übertragen; `Data` → `Uint8Array` |
@@ -37,14 +37,16 @@ Swift-Pfade relativ zu `filius-ipad/ios/FiliusPad/TopologyEditor/` (Stand
| `src/zip.ts` | `Persistence/TopologyFLSArchive.swift` (Fehler, Grenzen, Pfadregeln, ZIP-Leser/-Schreiber, CRC-32) | übertragen; DEFLATE über `fflate`, mit Apples Abschneide-Verhalten nachgebildet; Pfadfaltung (NFC, Breite, volles Case-Folding) an 41 Paaren gegen das Swift-Orakel gemessen |
| `src/archive.ts` | ebenda, Archiv-Teil von Import und Export | übertragen, ohne die XML-Ebene |
| `src/javabeans.ts`, `src/javaDefaults.json` | — | neu: XMLEncoder-Leser nach den Regeln von `java.beans.XMLDecoder` (Swift liest elementweise und verliert geteilte Werte); Getter-Standardwerte und statische Konstanten mit `tools/java-oracle` gemessen |
| `src/importProject.ts` | `Persistence/TopologyProjectStore.swift` (Knoten, Ports, Kabel, IP-Konfiguration, Switch/WLAN, Routen, RIP, Firewall, DHCP, Portweiterleitung, Modem, Dateisysteme, Programme, Doku-Elemente des Imports) | übertragen, Eingabe ist Javas Sicht: Name und Port-Anzahl wie Java, abgelehnte Adresse → Swift-Standard, Router-Standardgateway wie Java (Swift verliert es), jede Router-Firewall eingetragen, Konfigurationstexte wie in Swift getrimmt (`swiftTrim`), Rechner ohne Dateisystem leer wie in Java (Swift: Demo-Dateien), alle Programme bleiben (Swift verwirft sechs), Webserver-Dateien bleiben in `/webserver` (Swift: `/www`), keine versteckten Zustandsdateien im Dateisystem; gegen Swifts importiertes Modell auf 13 Dateien geprüft |
| `src/importProject.ts` | `Persistence/TopologyProjectStore.swift` (Knoten, Ports, Kabel, IP-Konfiguration, Switch/WLAN, Routen, RIP, Firewall, DHCP, Portweiterleitung, Modem, Dateisysteme, Programme, Doku-Elemente des Imports) | übertragen, Eingabe ist Javas Sicht: Name und Port-Anzahl wie Java, abgelehnte Adresse → Swift-Standard, Router-Standardgateway wie Java (Swift verliert es), jede Router-Firewall eingetragen, Konfigurationstexte wie in Swift getrimmt (`swiftTrim`), Rechner ohne Dateisystem leer wie in Java (Swift: Demo-Dateien), alle Programme bleiben (Swift verwirft sechs), Webserver-Dateien bleiben in `/webserver` (Swift: `/www`), keine versteckten Zustandsdateien im Dateisystem, DNS-Einträge so, wie Javas Server sie aus `/dns/hosts` beantwortet; gegen Swifts importiertes Modell auf 13 Dateien geprüft |
| `test/importProject.test.ts` | — | neu: Swifts importiertes Modell (13 Dateien, zwei begründete Abweichungen), Vollständigkeit aller 23, Pings im Kern (über einen Router; über drei Router mit manuellen Routen und Router-Gateways) |
| `src/systemSoftware.ts` | Java `SwitchFirmware`, `Betriebssystem`, `VermittlungsrechnerBetriebssystem`, `GatewayFirmware`, `Weiterleitungstabelle`, `Firewall`, `FirewallRule`, `DHCPServer`, `ModemFirmware`, `Dateisystem`, `Datei` | neu nach Java: SSID, Lernzeit, RIP, manuelle Routen, Router-Firewall (an allen Stellen, an denen die Datei sie erreicht), DHCP, statische NAT, Modem, Dateisystem und installierte Programme von Rechner und Notebook; `Character.isWhitespace` am JDK gemessen; gegen Java geprüft |
| `src/javaIp.ts` | Java `IP.inetAton`/`ipCheck` | neu nach Java; an 46 Fällen mit Java gemessen |
| `src/dnsHosts.ts` | Java `DNSServer.leseRecordListe`, `ResourceRecord`, `Dateisystem.holeDatei`; Swift `State/TopologyDNSResolver.swift` (`TopologyDNSHostsFileCodec`) als Vergleich | neu nach Java: Felder nur an einzelnen Leerzeichen, keine Kommentare, ungültige TTL → 3600, Doppelte bleiben; ins Modell kommt nur, was Java beantwortet (Typ genau A/MX/NS, Name nicht leer); an 76 Fällen mit Java und Swift gemessen |
| `src/topology.ts` | Java `SzenarioVerwaltung.netzwerkLaden`, Konstruktoren und Setter der Geräte; Swift `Persistence/TopologyProjectStore.swift` (Parser) als Vergleich | neu nach Java statt Übertragung: Geräteart aus der Klasse statt aus der Beschriftung, Geräte ohne Koordinaten bei 0,0 statt verworfen, Werte durch Javas Setter |
| `test/javabeans.test.ts` | — | neu: je XMLDecoder-Regel ein Test; alle 23 Korpus-Dateien, Klasse und Position jedes Geräts wie Java |
| `test/topology.test.ts` | — | neu: alle 23 Korpus-Dateien Feld für Feld wie Java (Geräte, Schnittstellen, Kabel, Doku-Elemente), dazu die Regeln, die der Korpus nicht berührt |
| `test/javaIp.test.ts`, `test/ip-check.*` | — | neu: `IP.ipCheck`-Fälle, Ergebnis aus Java |
| `test/dnsHosts.test.ts`, `test/dns-hosts.*` | — | neu: `/dns/hosts`-Fälle, Javas Sicht wie Java, das Modell wie Swift bis auf 33 benannte Fälle in zwölf Gründen |
| `test/zip.test.ts` | `TopologyProjectPersistenceTests.swift` (Archiv-Ablehnungstests, `makeMinimalZIP`, `makePayloadZIP`) | übertragen, ergänzt um DEFLATE-Grenzfälle |
| `test/corpus.test.ts` | — | neu: 23 Korpus-Dateien, gegengeprüft mit `fflate.unzipSync` |
| `test/pathFolding.test.ts`, `test/path-folding.*.json` | — | neu: welche Pfade dieselbe Datei meinen, Ergebnis aus dem Swift-Kern |
+400
View File
@@ -266,6 +266,26 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"filius.de",
"MX",
3600,
"smtp.filius.de."
],
[
"dns-root",
"A",
3600,
"141.99.5.13"
],
[
".",
"NS",
3600,
"dns-root."
]
],
"recursiveResolutionEnabled" : true
},
"installedPrograms" : [
@@ -464,6 +484,20 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"dns.de",
"A",
3600,
"141.99.5.14"
],
[
"de",
"NS",
3600,
"dns.de."
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -678,6 +712,20 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"dns.filius.de",
"A",
3600,
"141.99.5.15"
],
[
"filius.de",
"NS",
3600,
"dns.filius.de."
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -727,6 +775,14 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.5.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -1219,6 +1275,14 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"mywebserver.de",
"A",
3600,
"160.10.20.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -1886,6 +1950,14 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.2.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -3480,6 +3552,26 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"filius.de",
"MX",
3600,
"smtp.filius.de."
],
[
"dns-root",
"A",
3600,
"141.99.5.13"
],
[
".",
"NS",
3600,
"dns-root."
]
],
"recursiveResolutionEnabled" : true
},
"installedPrograms" : [
@@ -3678,6 +3770,20 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"dns.de",
"A",
3600,
"141.99.5.14"
],
[
"de",
"NS",
3600,
"dns.de."
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -3892,6 +3998,20 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"dns.filius.de",
"A",
3600,
"141.99.5.15"
],
[
"filius.de",
"NS",
3600,
"dns.filius.de."
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -3941,6 +4061,14 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.5.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -4475,6 +4603,20 @@
},
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.mein-server.de",
"A",
3600,
"192.168.0.100"
],
[
"xy",
"A",
3600,
"192.168.0.100"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -5868,6 +6010,38 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.50.231"
],
[
"mail-filius.de",
"A",
3600,
"141.99.50.232"
],
[
"mail-senior.de",
"A",
3600,
"141.99.50.233"
],
[
"filius.de",
"MX",
3600,
"mail-filius.de."
],
[
"senior.de",
"MX",
3600,
"mail-senior.de."
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -6958,6 +7132,44 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"webserver.com",
"A",
3600,
"120.130.140.80"
],
[
"gmx.com",
"MX",
3600,
"mail.gmx.com."
],
[
"mail.gmx.com",
"A",
3600,
"130.100.200.25"
],
[
"mail.webmail.com",
"A",
3600,
"140.100.200.25"
],
[
"webmail.com",
"MX",
3600,
"mail.webmail.com."
],
[
"myschool.org",
"A",
3600,
"42.0.0.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -7198,6 +7410,14 @@
},
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"meine-schule.de",
"A",
3600,
"10.0.0.1"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -8687,6 +8907,44 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"webserver.de",
"A",
3600,
"120.130.140.80"
],
[
"gmx.de",
"MX",
3600,
"mail.gmx.de."
],
[
"mail.gmx.de",
"A",
3600,
"130.100.200.25"
],
[
"mail.webmail.de",
"A",
3600,
"140.100.200.25"
],
[
"webmail.de",
"MX",
3600,
"mail.webmail.de."
],
[
"meine-schule.de",
"A",
3600,
"42.0.0.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -8927,6 +9185,14 @@
},
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"meine-schule.de",
"A",
3600,
"10.0.0.1"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -10361,6 +10627,14 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.5.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -11142,6 +11416,32 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"mail-senior.de",
"A",
3600,
"141.99.50.233"
],
[
"mail-filius.de",
"A",
3600,
"141.99.50.232"
],
[
"filius.de",
"MX",
3600,
"mail-filius.de."
],
[
"senior.de",
"MX",
3600,
"mail-senior.de."
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -11409,6 +11709,38 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.50.231"
],
[
"filius.de",
"MX",
3600,
"mail-filius.de."
],
[
"senior.de",
"MX",
3600,
"mail-senior.de."
],
[
"mail-filius.de",
"A",
3600,
"141.99.50.184"
],
[
"mail-senior.de",
"A",
3600,
"141.99.50.184"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -12923,6 +13255,20 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.5.10"
],
[
"www.jupiter.de",
"A",
3600,
"141.99.4.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -14481,6 +14827,14 @@
},
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.2.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -15260,6 +15614,14 @@
},
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"42.0.0.2"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -15417,6 +15779,14 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"webserver.de",
"A",
3600,
"120.130.140.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -15956,6 +16326,14 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"mywebserver.de",
"A",
3600,
"160.10.20.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -16939,6 +17317,14 @@
"system" : {
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.filius.de",
"A",
3600,
"141.99.2.10"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
@@ -17670,6 +18056,20 @@
},
"dnsServer" : {
"forwardingServerIPAddress" : null,
"records" : [
[
"www.mein-server.de",
"A",
3600,
"192.168.0.100"
],
[
"xy",
"A",
3600,
"192.168.0.100"
]
],
"recursiveResolutionEnabled" : false
},
"installedPrograms" : [
+16 -1
View File
@@ -180,10 +180,25 @@ export interface GnutellaConfiguration {
readonly maximumKnownPeers: number;
}
/** The DNS records follow with the hosts file (Java's /dns/hosts). */
export type DnsRecordType = 'A' | 'MX' | 'NS';
/**
* One record a DNS server answers with (Swift's TopologyDNSResourceRecord):
* `name` in lower case without the final dot (the root is "."), `target` an
* IPv4 address for A, an absolute name ("mail.filius.de.") for MX and NS.
*/
export interface DnsResourceRecord {
readonly name: string;
readonly type: DnsRecordType;
readonly ttlSeconds: number;
readonly target: string;
}
export interface DnsServerConfiguration {
readonly recursiveResolutionEnabled: boolean;
readonly forwardingServerIPAddress?: string;
/** In the order of Java's /dns/hosts, which stays in the file system as it is. */
readonly records: readonly DnsResourceRecord[];
}
/**
+153
View File
@@ -0,0 +1,153 @@
// Java's DNS server keeps its records in the text file /dns/hosts and reads
// them on every query (DNSServer.leseRecordListe, ResourceRecord(String)).
// Measured on test/dns-hosts.cases.txt against Java (dns-hosts.java.json)
// and Swift (dns-hosts.swift.json); where the two differ, Java decides:
// - fields are split at single spaces only: a tab or NBSP joins two fields;
// - there are no comments, no "IN" and no zone-file order;
// - a TTL that is not a Java int silently becomes 3600;
// - a name Java does not accept becomes the empty name, and the record
// still counts - it just never answers, like any record whose type is
// not exactly A, MX or NS (lower-case "a" included);
// - duplicates stay.
import type { DnsResourceRecord } from '@mdg-filius/core';
import { javaIpCheck, javaParseInt } from './javaIp.js';
import type { FlsFileTree } from './systemSoftware.js';
/** A record as Java's ResourceRecord holds it (the getters). */
export interface JavaResourceRecord {
readonly domainName: string;
readonly type: string;
readonly ttl: number;
readonly rdata: string;
}
export const DNS_HOSTS_PATH = '/dns/hosts';
// EingabenUeberpruefung.musterDomain, including its quirks: labels start with
// a letter, labels after the first may hold a backslash, and the empty
// string is a valid domain.
const JAVA_DOMAIN = /^([a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*)?\.?$/;
// EingabenUeberpruefung.musterIpAdresse.
const JAVA_IP_ADDRESS =
/^(0*([1-9]?[0-9]?|1[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\.((0*(1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))\.){2}(0*(1?[0-9]{1,2}|2[0-4]?[0-9]|25[0-5]))$/;
/** `String.trim()`: every UTF-16 unit up to U+0020 off both ends. */
function javaTrim(text: string): string {
return text.replace(/^[\u0000-\u0020]+|[\u0000-\u0020]+$/g, '');
}
/**
* The content of /dns/hosts as `Dateisystem.holeDatei` finds it: the first
* node in preorder whose path equals it ignoring case, and only if that node
* is a file. Undefined when there is none; a file without content reads as
* empty (Java would fail on it).
*/
export function javaHostsFileContent(root: FlsFileTree | undefined): string | undefined {
if (root === undefined) return undefined;
const found = firstInPreorder(root, '', (path) => javaEqualsIgnoreCase(path, DNS_HOSTS_PATH));
return found?.kind === 'file' ? (found.content ?? '') : undefined;
}
/** Preorder below the root; a node's path is "/" plus each name after the root's. */
function firstInPreorder(node: FlsFileTree, path: string, matches: (path: string) => boolean): FlsFileTree | undefined {
for (const child of node.children) {
const childPath = `${path}/${child.kind === 'other' ? child.className : String(child.name)}`;
if (matches(childPath)) return child;
const found = firstInPreorder(child, childPath, matches);
if (found !== undefined) return found;
}
return undefined;
}
/**
* `String.equalsIgnoreCase` against an ASCII path, unit by unit: equal after
* Character.toUpperCase, so the long s (U+017F) is an "s". Java also
* compares the lower case of both; for the letters of /dns/hosts that adds
* no unit (measured over the BMP).
*/
function javaEqualsIgnoreCase(text: string, asciiPath: string): boolean {
if (text.length !== asciiPath.length) return false;
for (let index = 0; index < text.length; index++) {
if (text[index]!.toUpperCase() !== asciiPath[index]!.toUpperCase()) return false;
}
return true;
}
/** `DNSServer.leseRecordListe` on the content of /dns/hosts. */
export function readJavaDnsHosts(content: string): JavaResourceRecord[] {
const records: JavaResourceRecord[] = [];
for (const token of content.split('\n')) {
const line = javaTrim(token);
// line.split(" ", 5).length < 4: fewer than three single spaces.
if (line === '' || line.split(' ').length < 4) continue;
const record = javaResourceRecord(line);
if (isValidJavaRecord(record)) records.push(record);
}
return records;
}
/** `new ResourceRecord(line)`: up to four space-separated fields. */
function javaResourceRecord(line: string): JavaResourceRecord {
const fields = line.split(' ').filter((field) => field !== '').map(javaTrim);
let domainName = '';
let type = 'A';
let ttl = 3600;
let rdata = '';
if (fields[0] !== undefined && JAVA_DOMAIN.test(fields[0])) {
domainName = fields[0].endsWith('.') ? fields[0] : `${fields[0]}.`;
}
if (fields[1] !== undefined) type = fields[1];
if (fields[2] !== undefined) ttl = javaParseInt(fields[2]) ?? ttl;
if (fields[3] !== undefined) {
// Java tests ".*\." (no line terminator before the dot); a target with
// one fails the domain check anyway, so endsWith says the same.
const needsDot = (type === 'NS' || type === 'MX') && !fields[3].endsWith('.');
rdata = needsDot ? `${fields[3]}.` : fields[3];
}
return { domainName, type, ttl, rdata };
}
function isValidJavaRecord(record: JavaResourceRecord): boolean {
switch (record.type) {
case 'A':
return JAVA_DOMAIN.test(record.domainName) && JAVA_IP_ADDRESS.test(record.rdata);
case 'MX':
case 'NS':
return JAVA_DOMAIN.test(record.domainName) && JAVA_DOMAIN.test(record.rdata);
default:
return true;
}
}
/**
* The records Java answers with, in the model's form: Java looks them up by
* type (exactly "A", "MX", "NS") and by name ignoring case, so a record with
* the empty name or another type never answers and is left out. The file
* itself stays as it is.
*/
export function dnsRecordsOf(records: readonly JavaResourceRecord[], warn: (message: string) => void): DnsResourceRecord[] {
const answered: DnsResourceRecord[] = [];
for (const record of records) {
if (record.domainName === '') continue;
const name = record.domainName === '.' ? '.' : record.domainName.slice(0, -1).toLowerCase();
switch (record.type) {
case 'A': {
const address = javaIpCheck(record.rdata);
if (address === null) {
warn(`DNS record '${name} A ${record.rdata}' has no usable IPv4 address, left out (it stays in ${DNS_HOSTS_PATH})`);
continue;
}
answered.push({ name, type: 'A', ttlSeconds: record.ttl, target: address });
break;
}
case 'MX':
case 'NS':
answered.push({ name, type: record.type, ttlSeconds: record.ttl, target: record.rdata.toLowerCase() });
break;
default:
break;
}
}
return answered;
}
+11 -3
View File
@@ -33,7 +33,10 @@
// - a web server's files stay in /webserver (its document root), where Swift
// moves them to /www; and no program state is written into a host's files
// (Swift keeps Gnutella and e-mail state there as hidden JSON files).
// Not here yet: DNS records (from /dns/hosts) and e-mail accounts.
// - a DNS server's records are what Java's server answers from /dns/hosts
// (see dnsHosts.ts); Swift reads the file its own way — tabs, comments,
// "IN", names with a leading digit — and drops Java's duplicates.
// Not here yet: e-mail accounts.
import {
canonicalMacAddress,
@@ -88,6 +91,7 @@ import {
type TopologyNode,
type VirtualFileContent,
} from '@mdg-filius/core';
import { dnsRecordsOf, javaHostsFileContent, readJavaDnsHosts } from './dnsHosts.js';
import type { FlsApplicationClass, FlsFileTree, FlsFirewall } from './systemSoftware.js';
import type { FlsDocumentationItem, FlsInterface, FlsNode, FlsNodeClass, FlsTopology } from './topology.js';
@@ -415,11 +419,15 @@ function registerPrograms(source: FlsNode, node: TopologyNode, system: SystemCon
const forwarder = trimmedOrEmpty(source.interfaces[0]?.dns);
const primary = trimmedOrEmpty(source.interfaces[0]?.ip);
const forwarding = recursive && forwarder !== '' && forwarder !== primary;
const hosts = javaHostsFileContent(source.system.fileSystem);
const records = dnsRecordsOf(readJavaDnsHosts(hosts ?? ''), (message) =>
warnings.push(`node '${node.displayName}': ${message}`),
);
system.dnsServerConfigurations.set(
node.id,
forwarding
? { recursiveResolutionEnabled: recursive, forwardingServerIPAddress: forwarder }
: { recursiveResolutionEnabled: recursive },
? { recursiveResolutionEnabled: recursive, forwardingServerIPAddress: forwarder, records }
: { recursiveResolutionEnabled: recursive, records },
);
break;
}
+1
View File
@@ -1,5 +1,6 @@
// Public surface of the .fls codec.
export * from './archive.js';
export * from './dnsHosts.js';
export * from './importProject.js';
export * from './javaIp.js';
export * from './javabeans.js';
+76
View File
@@ -0,0 +1,76 @@
www.filius.de. A 3600 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4
WWW.Filius.DE A 3600 1.2.3.4\u000A
www.filius.de a 3600 1.2.3.4\u000A
filius.de mx 3600 mail.filius.de\u000A
www.filius.de A 3600 1.2.3.4\u000A
www.filius.de\u0009A\u00093600\u00091.2.3.4\u000A
www.filius.de A 3600 1.2.3.4 \u000A
www.filius.de A 3600 1.2.3.4 # comment\u000A
# www.filius.de A 3600 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4#x\u000A
www.filius.de A IN 3600 1.2.3.4\u000A
www.filius.de 3600 IN A 1.2.3.4\u000A
www.filius.de A x 1.2.3.4\u000A
www.filius.de A -5 1.2.3.4\u000A
www.filius.de A 0 1.2.3.4\u000A
www.filius.de A +5 1.2.3.4\u000A
www.filius.de A 4294967295 1.2.3.4\u000A
www.filius.de A 99999999999 1.2.3.4\u000A
www.filius.de A 3600 010.0.0.1\u000A
www.filius.de A 3600 1.2.3\u000A
www.filius.de A 3600 256.0.0.1\u000A
www.filius.de A 3600 1.2.3.4.\u000A
www.filius.de A 3600 1.2.3.4 extra\u000A
www.filius.de A 3600\u000A
1und1.de A 3600 1.2.3.4\u000A
a_b.de A 3600 1.2.3.4\u000A
-a.de A 3600 1.2.3.4\u000A
a-.de A 3600 1.2.3.4\u000A
www..de A 3600 1.2.3.4\u000A
www.filius.de.. A 3600 1.2.3.4\u000A
www.1und1.de A 3600 1.2.3.4\u000A
www.a\u005Cb.de A 3600 1.2.3.4\u000A
m\u00FCller.de A 3600 1.2.3.4\u000A
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.de A 3600 1.2.3.4\u000A
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.de A 3600 1.2.3.4\u000A
localhost A 3600 127.0.0.1\u000A
. NS 3600 dns-root.\u000A
de NS 3600 dns.de\u000A
de. NS 3600 .\u000A
filius.de MX 3600 mail.filius.de\u000A
filius.de MX 3600 10 mail.filius.de\u000A
filius.de MX 3600 1.2.3.4\u000A
filius.de CNAME 3600 www.filius.de\u000A
filius.de TXT 3600 hello\u000A
www.filius.de A 3600 1.2.3.4\u000Awww.filius.de A 3600 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4\u000Awww.filius.de A 7200 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4\u000AWWW.filius.de. A 3600 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4\u000Awww.filius.de A 3600 1.2.3.5\u000A
www.filius.de A 3600 1.2.3.4\u000D\u000Amail.filius.de A 3600 1.2.3.5\u000D\u000A
www.filius.de A 3600 1.2.3.4\u000Dmail.filius.de A 3600 1.2.3.5\u000D
www.filius.de A 3600 1.2.3.4\u2028mail.filius.de A 3600 1.2.3.5\u000A
www.filius.de\u00A0A 3600 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4\u0085\u000A
\u000A\u000Awww.filius.de A 3600 1.2.3.4\u000A\u000A
filius.de. MX 3600 smtp.filius.de.\u000Adns-root. A 3600 141.99.5.13\u000A. NS 3600 dns-root.\u000A
www.filius.de A \u0661\u0660 1.2.3.4\u000A
www.filius.de A 2147483647 1.2.3.4\u000A
www.filius.de A -2147483648 1.2.3.4\u000A
www.filius.de A 2147483648 1.2.3.4\u000A
www.filius.de \u0009 A 3600 1.2.3.4\u000A
www.filius.de A \u0009 3600 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4 \u0009\u000A
www.filius.de A 3600 1.2.3.4\u000A
www.filius.de A 3600 1.2.3.4\u000A
www.filius.de A 3600 0.0.0.0\u000A
www.filius.de A 3600 00000000000000000001.2.3.4\u000A
filius.de NS 3600 dns..de\u000A
filius.de NS 3600 dns.filius.de..\u000A
filius.de. MX 3600 Mail.Filius.DE.\u000A
www.filius.de A 3600 .1.2.3\u000A
www.filius.de A 3600 1.2.3.4\u0001\u000A
www.filius.de A 3600 1.2.3.4\u00A0\u000A
\u001Fwww.filius.de A 3600 1.2.3.4\u000A
+754
View File
@@ -0,0 +1,754 @@
[
[
"www.filius.de. A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"WWW.Filius.DE A 3600 1.2.3.4\\u000A",
[
[
"WWW.Filius.DE.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de a 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"a",
3600,
"1.2.3.4"
]
]
],
[
"filius.de mx 3600 mail.filius.de\\u000A",
[
[
"filius.de.",
"mx",
3600,
"mail.filius.de"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de\\u0009A\\u00093600\\u00091.2.3.4\\u000A",
[]
],
[
" www.filius.de A 3600 1.2.3.4 \\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4 # comment\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"# www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"",
"www.filius.de",
3600,
"3600"
]
]
],
[
"www.filius.de A 3600 1.2.3.4#x\\u000A",
[]
],
[
"www.filius.de A IN 3600 1.2.3.4\\u000A",
[]
],
[
"www.filius.de 3600 IN A 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"3600",
3600,
"A"
]
]
],
[
"www.filius.de A x 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A -5 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
-5,
"1.2.3.4"
]
]
],
[
"www.filius.de A 0 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
0,
"1.2.3.4"
]
]
],
[
"www.filius.de A +5 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
5,
"1.2.3.4"
]
]
],
[
"www.filius.de A 4294967295 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 99999999999 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 010.0.0.1\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"010.0.0.1"
]
]
],
[
"www.filius.de A 3600 1.2.3\\u000A",
[]
],
[
"www.filius.de A 3600 256.0.0.1\\u000A",
[]
],
[
"www.filius.de A 3600 1.2.3.4.\\u000A",
[]
],
[
"www.filius.de A 3600 1.2.3.4 extra\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600\\u000A",
[]
],
[
"1und1.de A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"a_b.de A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"-a.de A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"a-.de A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www..de A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de.. A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.1und1.de A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.a\\u005Cb.de A 3600 1.2.3.4\\u000A",
[
[
"www.a\\b.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"m\\u00FCller.de A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.de A 3600 1.2.3.4\\u000A",
[
[
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.de A 3600 1.2.3.4\\u000A",
[
[
"",
"A",
3600,
"1.2.3.4"
]
]
],
[
"localhost A 3600 127.0.0.1\\u000A",
[
[
"localhost.",
"A",
3600,
"127.0.0.1"
]
]
],
[
". NS 3600 dns-root.\\u000A",
[
[
".",
"NS",
3600,
"dns-root."
]
]
],
[
"de NS 3600 dns.de\\u000A",
[
[
"de.",
"NS",
3600,
"dns.de."
]
]
],
[
"de. NS 3600 .\\u000A",
[
[
"de.",
"NS",
3600,
"."
]
]
],
[
"filius.de MX 3600 mail.filius.de\\u000A",
[
[
"filius.de.",
"MX",
3600,
"mail.filius.de."
]
]
],
[
"filius.de MX 3600 10 mail.filius.de\\u000A",
[]
],
[
"filius.de MX 3600 1.2.3.4\\u000A",
[]
],
[
"filius.de CNAME 3600 www.filius.de\\u000A",
[
[
"filius.de.",
"CNAME",
3600,
"www.filius.de"
]
]
],
[
"filius.de TXT 3600 hello\\u000A",
[
[
"filius.de.",
"TXT",
3600,
"hello"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000Awww.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
],
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000Awww.filius.de A 7200 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
],
[
"www.filius.de.",
"A",
7200,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000AWWW.filius.de. A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
],
[
"WWW.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000Awww.filius.de A 3600 1.2.3.5\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
],
[
"www.filius.de.",
"A",
3600,
"1.2.3.5"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000D\\u000Amail.filius.de A 3600 1.2.3.5\\u000D\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
],
[
"mail.filius.de.",
"A",
3600,
"1.2.3.5"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000Dmail.filius.de A 3600 1.2.3.5\\u000D",
[]
],
[
"www.filius.de A 3600 1.2.3.4\\u2028mail.filius.de A 3600 1.2.3.5\\u000A",
[]
],
[
"www.filius.de\\u00A0A 3600 1.2.3.4\\u000A",
[]
],
[
"www.filius.de A 3600 1.2.3.4\\u0085\\u000A",
[]
],
[
"",
[]
],
[
"\\u000A\\u000Awww.filius.de A 3600 1.2.3.4\\u000A\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"filius.de. MX 3600 smtp.filius.de.\\u000Adns-root. A 3600 141.99.5.13\\u000A. NS 3600 dns-root.\\u000A",
[
[
"filius.de.",
"MX",
3600,
"smtp.filius.de."
],
[
"dns-root.",
"A",
3600,
"141.99.5.13"
],
[
".",
"NS",
3600,
"dns-root."
]
]
],
[
"www.filius.de A \\u0661\\u0660 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
10,
"1.2.3.4"
]
]
],
[
"www.filius.de A 2147483647 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
2147483647,
"1.2.3.4"
]
]
],
[
"www.filius.de A -2147483648 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
-2147483648,
"1.2.3.4"
]
]
],
[
"www.filius.de A 2147483648 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de \\u0009 A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"",
3600,
"3600"
]
]
],
[
"www.filius.de A \\u0009 3600 1.2.3.4\\u000A",
[]
],
[
"www.filius.de A 3600 1.2.3.4 \\u0009\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 0.0.0.0\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"0.0.0.0"
]
]
],
[
"www.filius.de A 3600 00000000000000000001.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"00000000000000000001.2.3.4"
]
]
],
[
"filius.de NS 3600 dns..de\\u000A",
[]
],
[
"filius.de NS 3600 dns.filius.de..\\u000A",
[]
],
[
"filius.de. MX 3600 Mail.Filius.DE.\\u000A",
[
[
"filius.de.",
"MX",
3600,
"Mail.Filius.DE."
]
]
],
[
"www.filius.de A 3600 .1.2.3\\u000A",
[
[
"www.filius.de.",
"A",
3600,
".1.2.3"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u0001\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u00A0\\u000A",
[]
],
[
"\\u001Fwww.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de.",
"A",
3600,
"1.2.3.4"
]
]
]
]
+749
View File
@@ -0,0 +1,749 @@
[
[
"www.filius.de. A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"WWW.Filius.DE A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de a 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"filius.de mx 3600 mail.filius.de\\u000A",
[
[
"filius.de",
"MX",
3600,
"mail.filius.de."
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de\\u0009A\\u00093600\\u00091.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
" www.filius.de A 3600 1.2.3.4 \\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4 # comment\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"# www.filius.de A 3600 1.2.3.4\\u000A",
[
]
],
[
"www.filius.de A 3600 1.2.3.4#x\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A IN 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de 3600 IN A 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A x 1.2.3.4\\u000A",
[
]
],
[
"www.filius.de A -5 1.2.3.4\\u000A",
[
]
],
[
"www.filius.de A 0 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
0,
"1.2.3.4"
]
]
],
[
"www.filius.de A +5 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
5,
"1.2.3.4"
]
]
],
[
"www.filius.de A 4294967295 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
4294967295,
"1.2.3.4"
]
]
],
[
"www.filius.de A 99999999999 1.2.3.4\\u000A",
[
]
],
[
"www.filius.de A 3600 010.0.0.1\\u000A",
[
[
"www.filius.de",
"A",
3600,
"10.0.0.1"
]
]
],
[
"www.filius.de A 3600 1.2.3\\u000A",
[
]
],
[
"www.filius.de A 3600 256.0.0.1\\u000A",
[
]
],
[
"www.filius.de A 3600 1.2.3.4.\\u000A",
[
]
],
[
"www.filius.de A 3600 1.2.3.4 extra\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600\\u000A",
[
]
],
[
"1und1.de A 3600 1.2.3.4\\u000A",
[
[
"1und1.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"a_b.de A 3600 1.2.3.4\\u000A",
[
]
],
[
"-a.de A 3600 1.2.3.4\\u000A",
[
]
],
[
"a-.de A 3600 1.2.3.4\\u000A",
[
]
],
[
"www..de A 3600 1.2.3.4\\u000A",
[
]
],
[
"www.filius.de.. A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.1und1.de A 3600 1.2.3.4\\u000A",
[
[
"www.1und1.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.a\\u005Cb.de A 3600 1.2.3.4\\u000A",
[
]
],
[
"m\\u00FCller.de A 3600 1.2.3.4\\u000A",
[
]
],
[
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.de A 3600 1.2.3.4\\u000A",
[
[
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.de A 3600 1.2.3.4\\u000A",
[
]
],
[
"localhost A 3600 127.0.0.1\\u000A",
[
[
"localhost",
"A",
3600,
"127.0.0.1"
]
]
],
[
". NS 3600 dns-root.\\u000A",
[
[
".",
"NS",
3600,
"dns-root."
]
]
],
[
"de NS 3600 dns.de\\u000A",
[
[
"de",
"NS",
3600,
"dns.de."
]
]
],
[
"de. NS 3600 .\\u000A",
[
]
],
[
"filius.de MX 3600 mail.filius.de\\u000A",
[
[
"filius.de",
"MX",
3600,
"mail.filius.de."
]
]
],
[
"filius.de MX 3600 10 mail.filius.de\\u000A",
[
[
"filius.de",
"MX",
3600,
"10."
]
]
],
[
"filius.de MX 3600 1.2.3.4\\u000A",
[
[
"filius.de",
"MX",
3600,
"1.2.3.4."
]
]
],
[
"filius.de CNAME 3600 www.filius.de\\u000A",
[
]
],
[
"filius.de TXT 3600 hello\\u000A",
[
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000Awww.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000Awww.filius.de A 7200 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000AWWW.filius.de. A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000Awww.filius.de A 3600 1.2.3.5\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
],
[
"www.filius.de",
"A",
3600,
"1.2.3.5"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000D\\u000Amail.filius.de A 3600 1.2.3.5\\u000D\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
],
[
"mail.filius.de",
"A",
3600,
"1.2.3.5"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000Dmail.filius.de A 3600 1.2.3.5\\u000D",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
],
[
"mail.filius.de",
"A",
3600,
"1.2.3.5"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u2028mail.filius.de A 3600 1.2.3.5\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
],
[
"mail.filius.de",
"A",
3600,
"1.2.3.5"
]
]
],
[
"www.filius.de\\u00A0A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u0085\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"",
[
]
],
[
"\\u000A\\u000Awww.filius.de A 3600 1.2.3.4\\u000A\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"filius.de. MX 3600 smtp.filius.de.\\u000Adns-root. A 3600 141.99.5.13\\u000A. NS 3600 dns-root.\\u000A",
[
[
"filius.de",
"MX",
3600,
"smtp.filius.de."
],
[
"dns-root",
"A",
3600,
"141.99.5.13"
],
[
".",
"NS",
3600,
"dns-root."
]
]
],
[
"www.filius.de A \\u0661\\u0660 1.2.3.4\\u000A",
[
]
],
[
"www.filius.de A 2147483647 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
2147483647,
"1.2.3.4"
]
]
],
[
"www.filius.de A -2147483648 1.2.3.4\\u000A",
[
]
],
[
"www.filius.de A 2147483648 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
2147483648,
"1.2.3.4"
]
]
],
[
"www.filius.de \\u0009 A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A \\u0009 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4 \\u0009\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 1.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"www.filius.de A 3600 0.0.0.0\\u000A",
[
[
"www.filius.de",
"A",
3600,
"0.0.0.0"
]
]
],
[
"www.filius.de A 3600 00000000000000000001.2.3.4\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"filius.de NS 3600 dns..de\\u000A",
[
]
],
[
"filius.de NS 3600 dns.filius.de..\\u000A",
[
[
"filius.de",
"NS",
3600,
"dns.filius.de."
]
]
],
[
"filius.de. MX 3600 Mail.Filius.DE.\\u000A",
[
[
"filius.de",
"MX",
3600,
"mail.filius.de."
]
]
],
[
"www.filius.de A 3600 .1.2.3\\u000A",
[
]
],
[
"www.filius.de A 3600 1.2.3.4\\u0001\\u000A",
[
]
],
[
"www.filius.de A 3600 1.2.3.4\\u00A0\\u000A",
[
[
"www.filius.de",
"A",
3600,
"1.2.3.4"
]
]
],
[
"\\u001Fwww.filius.de A 3600 1.2.3.4\\u000A",
[
]
]
]
+209
View File
@@ -0,0 +1,209 @@
// Java's reading of /dns/hosts (DNSServer.leseRecordListe): every case in
// dns-hosts.cases.txt was run through Java (dns-hosts.java.json) and through
// Swift's hosts-file codec (dns-hosts.swift.json, tools/swift-oracle). The
// Java view must match Java exactly; the records the model keeps must match
// Swift except where Java answers differently — each case named below.
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import type { FlsFileTree } from '../src/index.js';
import { dnsRecordsOf, javaHostsFileContent, readJavaDnsHosts } from '../src/dnsHosts.js';
const here = dirname(fileURLToPath(import.meta.url));
type Row = [string, string, number, string];
const javaMeasured = JSON.parse(readFileSync(join(here, 'dns-hosts.java.json'), 'utf8')) as [string, Row[]][];
const swiftMeasured = JSON.parse(readFileSync(join(here, 'dns-hosts.swift.json'), 'utf8')) as [string, Row[]][];
/** The cases file's escapes: a backslash, `u` and four hex digits for one UTF-16 unit. */
function unescape(line: string): string {
return line.replace(/\\u([0-9a-fA-F]{4})/g, (_, hex: string) => String.fromCharCode(Number.parseInt(hex, 16)));
}
function modelRows(line: string, warnings: string[] = []): Row[] {
return dnsRecordsOf(readJavaDnsHosts(unescape(line)), (message) => warnings.push(message)).map((record) => [
record.name,
record.type,
record.ttlSeconds,
record.target,
]);
}
/** Lines Swift reads as records that Java never answers. */
const SWIFT_READS_JAVA_NEVER_ANSWERS: { reason: string; lines: string[] }[] = [
{
reason: 'a type in lower case is another type in Java, which never answers',
lines: [
String.raw`www.filius.de a 3600 1.2.3.4\u000A`,
String.raw`filius.de mx 3600 mail.filius.de\u000A`,
],
},
{
reason: 'Java splits fields at single spaces only: a tab or NBSP is part of a field',
lines: [
String.raw`www.filius.de\u0009A\u00093600\u00091.2.3.4\u000A`,
String.raw`www.filius.de\u00A0A 3600 1.2.3.4\u000A`,
String.raw`www.filius.de A 3600 1.2.3.4\u00A0\u000A`,
String.raw`www.filius.de \u0009 A 3600 1.2.3.4\u000A`,
String.raw`www.filius.de A \u0009 3600 1.2.3.4\u000A`,
],
},
{
reason: 'Java splits lines at LF only: CR, LS and NEL stay in the line',
lines: [
String.raw`www.filius.de A 3600 1.2.3.4\u000Dmail.filius.de A 3600 1.2.3.5\u000D`,
String.raw`www.filius.de A 3600 1.2.3.4\u2028mail.filius.de A 3600 1.2.3.5\u000A`,
String.raw`www.filius.de A 3600 1.2.3.4\u0085\u000A`,
],
},
{
reason: 'Java has no comments, no "IN" and no zone-file order',
lines: [
String.raw`www.filius.de A 3600 1.2.3.4#x\u000A`,
String.raw`www.filius.de A IN 3600 1.2.3.4\u000A`,
String.raw`www.filius.de 3600 IN A 1.2.3.4\u000A`,
],
},
{
reason: 'a label that starts with a digit, or two final dots, make the name empty in Java',
lines: [
String.raw`1und1.de A 3600 1.2.3.4\u000A`,
String.raw`www.1und1.de A 3600 1.2.3.4\u000A`,
String.raw`www.filius.de.. A 3600 1.2.3.4\u000A`,
],
},
{
reason: 'an MX or NS target must be a name Java accepts: no preference, no address, one final dot',
lines: [
String.raw`filius.de MX 3600 10 mail.filius.de\u000A`,
String.raw`filius.de MX 3600 1.2.3.4\u000A`,
String.raw`filius.de NS 3600 dns.filius.de..\u000A`,
],
},
];
/** Lines Java answers that Swift drops or reads otherwise. */
const JAVA_ANSWERS_SWIFT_DROPS: { reason: string; lines: string[] }[] = [
{
reason: 'a TTL that is no Java int is 3600',
lines: [
String.raw`www.filius.de A x 1.2.3.4\u000A`,
String.raw`www.filius.de A 4294967295 1.2.3.4\u000A`,
String.raw`www.filius.de A 99999999999 1.2.3.4\u000A`,
String.raw`www.filius.de A 2147483648 1.2.3.4\u000A`,
],
},
{
reason: 'a TTL is Java\'s Integer.parseInt: negative, and in any script\'s digits',
lines: [
String.raw`www.filius.de A -5 1.2.3.4\u000A`,
String.raw`www.filius.de A -2147483648 1.2.3.4\u000A`,
String.raw`www.filius.de A \u0661\u0660 1.2.3.4\u000A`,
],
},
{
reason: 'labels after the first may hold a backslash (Java\'s domain pattern)',
lines: [
String.raw`www.a\u005Cb.de A 3600 1.2.3.4\u000A`,
],
},
{
reason: 'Java trims control characters off a line (String.trim)',
lines: [
String.raw`www.filius.de A 3600 1.2.3.4\u0001\u000A`,
String.raw`\u001Fwww.filius.de A 3600 1.2.3.4\u000A`,
],
},
{
reason: 'an NS record may name the root',
lines: [
String.raw`de. NS 3600 .\u000A`,
],
},
{
reason: 'duplicates stay, as in Java\'s list',
lines: [
String.raw`www.filius.de A 3600 1.2.3.4\u000Awww.filius.de A 3600 1.2.3.4\u000A`,
String.raw`www.filius.de A 3600 1.2.3.4\u000Awww.filius.de A 7200 1.2.3.4\u000A`,
String.raw`www.filius.de A 3600 1.2.3.4\u000AWWW.filius.de. A 3600 1.2.3.4\u000A`,
],
},
];
const departures = new Map(
[...SWIFT_READS_JAVA_NEVER_ANSWERS, ...JAVA_ANSWERS_SWIFT_DROPS].flatMap(({ reason, lines }) =>
lines.map((line) => [line, reason] as const),
),
);
describe('readJavaDnsHosts against Java', () => {
it('covers every line of the cases file, in both oracles', () => {
const lines = readFileSync(join(here, 'dns-hosts.cases.txt'), 'ascii').split('\n').slice(0, -1);
expect(javaMeasured.map(([line]) => line)).toEqual(lines);
expect(swiftMeasured.map(([line]) => line)).toEqual(lines);
});
for (const [line, expected] of javaMeasured) {
it(JSON.stringify(line), () => {
const records = readJavaDnsHosts(unescape(line));
expect(records.map((record) => [record.domainName, record.type, record.ttl, record.rdata])).toEqual(expected);
});
}
});
describe('the records the model keeps, against Swift', () => {
it('names only cases of the cases file as departures', () => {
const lines = new Set(swiftMeasured.map(([line]) => line));
expect([...departures.keys()].filter((line) => !lines.has(line))).toEqual([]);
});
for (const [line, swift] of swiftMeasured) {
const reason = departures.get(line);
it(reason === undefined ? JSON.stringify(line) : `${JSON.stringify(line)} (departs: ${reason})`, () => {
if (reason === undefined) expect(modelRows(line)).toEqual(swift);
else expect(modelRows(line)).not.toEqual(swift);
});
}
});
describe('the records Java answers', () => {
it('leave out records Java never answers: an empty name, or a type other than A, MX, NS', () => {
expect(modelRows(String.raw`1und1.de A 3600 1.2.3.4\u000Afilius.de CNAME 3600 www.filius.de\u000Afilius.de a 3600 1.2.3.4`)).toEqual([]);
});
it('take names in lower case without the final dot, A targets as FILIUS normalises addresses', () => {
expect(modelRows(String.raw`WWW.Filius.DE. A 60 010.000.0.1\u000Afilius.de MX 3600 Mail.Filius.DE\u000A. NS 3600 dns-root`)).toEqual([
['www.filius.de', 'A', 60, '10.0.0.1'],
['filius.de', 'MX', 3600, 'mail.filius.de.'],
['.', 'NS', 3600, 'dns-root.'],
]);
});
it('warn about an A record Java answers with an address FILIUS cannot use', () => {
const warnings: string[] = [];
expect(modelRows(String.raw`www.filius.de A 3600 .1.2.3`, warnings)).toEqual([]);
expect(warnings).toEqual(["DNS record 'www.filius.de A .1.2.3' has no usable IPv4 address, left out (it stays in /dns/hosts)"]);
});
});
describe('javaHostsFileContent', () => {
const file = (name: string, content: string | null): FlsFileTree => ({ kind: 'file', name, type: 'text', content, children: [] });
const directory = (name: string, ...children: FlsFileTree[]): FlsFileTree => ({ kind: 'directory', name, children });
it('finds /dns/hosts ignoring case, as Java does', () => {
expect(javaHostsFileContent(directory('root', directory('DNS', file('Hosts', 'x'))))).toBe('x');
expect(javaHostsFileContent(directory('root', directory('dns', file('ho\u017Fts', 'long s'))))).toBe('long s');
});
it('takes the first match in preorder, and nothing when that is a directory', () => {
expect(javaHostsFileContent(directory('root', directory('dns', file('hosts', 'first')), directory('DNS', file('hosts', 'second'))))).toBe('first');
expect(javaHostsFileContent(directory('root', directory('dns', directory('hosts'), file('HOSTS', 'later'))))).toBeUndefined();
});
it('is undefined without the file, and empty for a file without content', () => {
expect(javaHostsFileContent(undefined)).toBeUndefined();
expect(javaHostsFileContent(directory('root', file('hosts', 'x')))).toBeUndefined();
expect(javaHostsFileContent(directory('root', directory('dns', file('hosts', null))))).toBe('');
});
});
+31 -3
View File
@@ -145,6 +145,7 @@ function systemOf(project: ImportedProject, nodeId: Id) {
result.dnsServer = {
recursiveResolutionEnabled: dns.recursiveResolutionEnabled,
forwardingServerIPAddress: dns.forwardingServerIPAddress ?? null,
records: dns.records.map((record) => [record.name, record.type, record.ttlSeconds, record.target]),
};
}
const remoteLink = project.remoteLinkConfigurations.get(nodeId);
@@ -787,12 +788,39 @@ describe('importProject rules', () => {
expect(project.simpleClientConfigurations.get(notebook!)).toEqual({ targetAddress: '10.0.0.9', targetPort: 7 });
expect(project.firewallConfigurationsByNodeId.get(notebook!)).toMatchObject({ isActive: true, defaultPolicy: 'accept' });
expect([forwarding, self, iterative].map((id) => project.dnsServerConfigurations.get(id!))).toEqual([
{ recursiveResolutionEnabled: true, forwardingServerIPAddress: '8.8.8.8' },
{ recursiveResolutionEnabled: true },
{ recursiveResolutionEnabled: false },
{ recursiveResolutionEnabled: true, forwardingServerIPAddress: '8.8.8.8', records: [] },
{ recursiveResolutionEnabled: true, records: [] },
{ recursiveResolutionEnabled: false, records: [] },
]);
});
it("takes a DNS server's records from its /dns/hosts, and names the node in a warning", () => {
const hosts = 'www.filius.de A 3600 141.99.2.10\nfilius.de MX 3600 mail.filius.de\nbroken.de A 3600 .1.2.3\n';
const server = flsNode('Rechner', {
name: 'DNS',
interfaces: [nic('10.0.0.1')],
system: {
applications: [{ key: 'dns', javaClass: 'filius.software.dns.DNSServer', source: flsNode('Rechner').source, isActive: true }],
fileSystem: {
kind: 'directory',
name: 'root',
children: [
{ kind: 'directory', name: 'dns', children: [{ kind: 'file', name: 'hosts', type: 'text', content: hosts, children: [] }] },
],
},
},
});
const project = importProject(topology({ nodes: [server] }), counter());
const [id] = project.graph.nodes.map((node) => node.id);
expect(project.dnsServerConfigurations.get(id!)?.records).toEqual([
{ name: 'www.filius.de', type: 'A', ttlSeconds: 3600, target: '141.99.2.10' },
{ name: 'filius.de', type: 'MX', ttlSeconds: 3600, target: 'mail.filius.de.' },
]);
expect(project.virtualFileSystems.get(id!)?.textFile('/dns/hosts')).toBe(hosts);
expect(project.warnings).toEqual(["node 'DNS': DNS record 'broken.de A .1.2.3' has no usable IPv4 address, left out (it stays in /dns/hosts)"]);
});
it('leaves out a cable to itself and one to a port that already has a cable', () => {
const project = importProject(
topology({
+13
View File
@@ -64,3 +64,16 @@ Java-Filius schickt jede IP-Adresse und Maske beim Laden durch
zählen nicht (`10..0.0.1`), Vorzeichen und führende Nullen gehen, ebenso
Ziffern jeder Schrift in der BMP (Thai, Devanagari, Vollbreite) — aber kein
Leerzeichen und kein 𝟏 (Ersatzpaar).
## `/dns/hosts`
Javas DNS-Server liest seine Einträge bei jeder Anfrage aus der Textdatei
`/dns/hosts`. `DnsHostsCases` legt für jede Zeile von
`packages/fls/test/dns-hosts.cases.txt` (eine ganze Datei je Zeile, ASCII,
Zeilenumbruch als `\u000A`) einen Rechner mit dieser Datei an und schreibt,
was `DNSServer.holeResourceRecords()` liefert, nach `dns-hosts.java.json`.
Gemessen: Felder trennt nur ein einzelnes Leerzeichen (Tab und NBSP nicht),
Zeilen nur LF; ein Name, den Javas Muster ablehnt (`1und1.de`, `müller.de`),
wird leer, der Eintrag bleibt trotzdem; Typen außer A/MX/NS (auch `a`,
`CNAME`, eine Kommentarzeile) gelten als gültig; eine TTL, die kein `int`
ist, wird still 3600.
+8 -1
View File
@@ -21,7 +21,7 @@ if [ ! -f "$upstream/target/filius.jar" ]; then
fi
classpath="$upstream/target/filius.jar:$upstream/target/lib/*"
mkdir -p "$here/.build"
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java"
javac -encoding UTF-8 -d "$here/.build" -cp "$classpath" "$here/src/FlsOracle.java" "$here/src/GetterDefaults.java" "$here/src/IpCheckCases.java" "$here/src/DnsHostsCases.java"
# FILIUS writes its settings below $HOME; keep them out of the real one.
mkdir -p "$here/.build/home"
# Removed first: a crashed run must not leave the previous result standing.
@@ -66,3 +66,10 @@ ipcheck="$root/packages/fls/test/ip-check.java.json"
rm -f "$ipcheck"
HOME="$here/.build/home" java -cp "$classpath:$here/.build" IpCheckCases "$cases" "$ipcheck" 2>&1 | grep -v 'logback\|^$' || true
test -s "$ipcheck"
# DNSServer's own reading of /dns/hosts on hand-picked files (packages/fls/src/dnsHosts.ts).
cases="$root/packages/fls/test/dns-hosts.cases.txt"
dnshosts="$root/packages/fls/test/dns-hosts.java.json"
rm -f "$dnshosts"
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" DnsHostsCases "$cases" "$dnshosts" 2>&1 | grep -v 'logback\|^$' || true
test -s "$dnshosts"
+50
View File
@@ -0,0 +1,50 @@
// Runs FILIUS's own reading of /dns/hosts (DNSServer.holeResourceRecords)
// on every case of a list and writes the records Java keeps — the reference
// for packages/fls/src/dnsHosts.ts. One case per line: a whole hosts file,
// ASCII with Java-style unicode escapes (backslash, u, four hex digits), so
// line breaks and odd spaces survive editors and hooks.
//
// Usage: java -cp filius.jar:lib/*:classes DnsHostsCases <cases txt> <output json>
import filius.hardware.knoten.Rechner;
import filius.software.dns.DNSServer;
import filius.software.dns.ResourceRecord;
import filius.software.system.Datei;
import filius.software.system.Dateisystem;
import filius.software.system.InternetKnotenBetriebssystem;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DnsHostsCases {
public static void main(String[] args) throws Exception {
List<Object> result = new ArrayList<>();
for (String line : Files.readAllLines(Path.of(args[0]), StandardCharsets.US_ASCII)) {
InternetKnotenBetriebssystem system = (InternetKnotenBetriebssystem) new Rechner().getSystemSoftware();
Dateisystem fileSystem = system.getDateisystem();
fileSystem.erstelleVerzeichnis(fileSystem.getRoot(), "dns");
Datei hosts = new Datei();
hosts.setName("hosts");
hosts.setDateiInhalt(IpCheckCases.unescape(line));
fileSystem.speicherDatei(fileSystem.holeRootPfad() + Dateisystem.FILE_SEPARATOR + "dns", hosts);
DNSServer server = new DNSServer();
server.setSystemSoftware(system);
List<Object> records = new ArrayList<>();
for (ResourceRecord record : server.holeResourceRecords()) {
records.add(Arrays.asList(record.getDomainname(), record.getType(), record.getTtl(), record.getRdata()));
}
result.add(Arrays.asList(line, records));
}
try (FileOutputStream out = new FileOutputStream(args[1])) {
out.write((FlsOracle.Json.write(result, "") + "\n").getBytes(StandardCharsets.UTF_8));
}
System.out.println("dns hosts cases: " + result.size());
System.exit(0);
}
}
+5
View File
@@ -38,6 +38,11 @@ Sortierung, Base64 — und die ganzen Mengen `whitespacesAndNewlines` und
unter Linux; ihre Steuerzeichen-Tabelle hat in Ebene 14 einen Fehler, den
der Port nicht übernimmt.
`--dns-hosts` liest dieselben `/dns/hosts`-Fälle wie das Java-Orakel
(`packages/fls/test/dns-hosts.cases.txt`) mit Swifts
`TopologyDNSHostsFileCodec`; unter `state` stehen die DNS-Einträge jedes
Servers (`dnsServer.records`).
## Wie es gebaut wird
`fetch-upstream.sh` holt `filius-ipad` am festen Stand `3b5ded0` und kopiert
@@ -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])
+2 -1
View File
@@ -14,4 +14,5 @@ docker run --rm -v "$root:/work" -w /work/tools/swift-oracle swift:6.1-jammy sh
.build/debug/Oracle --corpus /work/corpus /work/corpus/corpus.swift.json
.build/debug/Oracle --vfs /work/packages/core/test/vfs.cases.json /work/packages/core/test/vfs.swift.json
.build/debug/Oracle --strings /work/packages/core/test/strings.cases.json /work/packages/core/test/strings.swift.json
.build/debug/Oracle --path-folding /work/packages/fls/test/path-folding.cases.json /work/packages/fls/test/path-folding.swift.json'
.build/debug/Oracle --path-folding /work/packages/fls/test/path-folding.cases.json /work/packages/fls/test/path-folding.swift.json
.build/debug/Oracle --dns-hosts /work/packages/fls/test/dns-hosts.cases.txt /work/packages/fls/test/dns-hosts.swift.json'