test(java-oracle): DnsRun records DNS as Java FILIUS runs it

A resolver's lookup on the new dns-lan network (A, MX, a referral by NS,
an unknown name, a missing server): every frame and the call's result.
ScenarioNetwork installs a scenario's DNS servers. The Swift comparison
reads only scenarios with a Swift result.

Measured: Java's text messages (ID=... QR=... header, question and record
lines), no question in the answer, RCODE 0 always, a referral in the answer
section, an iterative resolver waiting 5 RTT per query.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 16:46:07 +00:00
co-authored by Claude Opus 5.5
parent efc7c440f6
commit 24ebcea67f
11 changed files with 914 additions and 3 deletions
+25
View File
@@ -222,3 +222,28 @@ Gemessen (30.09.2026, 90 %, RTT 2750 ms), was Swift anders macht:
SYN+ACK nie.
* Startnummern aus einem Zähler über alle TCP-Sockets (1000000, 2000000, …),
wie im Kern; der freie Port ist zufällig.
## DNS zur Laufzeit
`DnsRun` ruft auf dem Netz von `dns-lan` den Resolver eines Clients auf, wie
Filius' Programme es tun, und schreibt nach `dns-java-<ablauf>.java.json` die
Frames jeder Karte und das Ergebnis des Aufrufs. `ScenarioNetwork` richtet
dazu die `dnsServers` des Szenarios ein: installiert, Einträge über
`hinzuRecord` in `/dns/hosts` (Zeilen `name TYP ziel`, TTL 3600),
eingeschaltet. Server 192.168.0.10 kennt `web.filius.de`, den Mailserver
von `filius.de` und verweist `schule.de` an 192.168.0.11, der
`www.schule.de` kennt.
* `address`, `mail`, `referral`, `unknown`: A, MX (samt A des Mailservers),
Verweis per NS, unbekannter Name.
* `noServer`: der DNS-Server des Clients (192.168.0.99) fehlt.
Gemessen (30.09.2026), Javas Format, das Swift durch ein eigenes ersetzt:
Jede Nachricht ist Text, `ID=… QR=… RCODE=… QDCOUNT=… ANCOUNT=… NSCOUNT=…
ARCOUNT=… ` und dann je Zeile eine Frage `name. TYP IN` oder ein Eintrag
`name. TYP ttl ziel`. Die ID würfelt Java (`Math.random`). Die Antwort
wiederholt die Frage nicht (`QDCOUNT=0`), RCODE ist immer 0, auch für einen
unbekannten Namen (leere Antwort), und ein Verweis steht mit NS und A des
Nameservers im Antwortteil. Der Resolver folgt dem Verweis selbst
(iterativ) und wartet je Anfrage 5 RTT (13750 ms bei 90 %); ohne Server
fragt ARP zweimal im Abstand einer RTT, dann scheitert der Aufruf.
+12 -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" "$here/src/DnsHostsCases.java" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.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" "$here/src/EmailFileCases.java" "$here/src/BeansDump.java" "$here/src/Base64Cases.java" "$here/src/ScenarioNetwork.java" "$here/src/DhcpRun.java" "$here/src/TcpRun.java" "$here/src/DnsRun.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.
@@ -130,3 +130,14 @@ for run in exchange:30000 closedPort:30000 lostData:45000; do
"$root/packages/core/test/scenarios/tcp-lan.scenario.json" "$story" "${run##*:}" "$output" 2>&1 | grep -v 'logback\|^$' || true
test -s "$output"
done
# DNS as Java FILIUS runs it: a resolver's lookup on dns-lan's network
# (DnsRun); frames and the call's result, for the core's DNS comparison.
for run in address:5000 mail:5000 referral:5000 unknown:5000 noServer:22000; do
story="${run%%:*}"
output="$root/packages/core/test/scenarios/dns-java-$story.java.json"
rm -f "$output"
HOME="$here/.build/home" xvfb-run -a java -cp "$classpath:$here/.build" DnsRun \
"$root/packages/core/test/scenarios/dns-lan.scenario.json" "$story" "${run##*:}" "$output" 2>&1 | grep -v 'logback\|^$' || true
test -s "$output"
done
+86
View File
@@ -0,0 +1,86 @@
// Runs a DNS lookup on dns-lan's network in Java FILIUS (ScenarioNetwork)
// and records what really happens: every frame each card saw, and when the
// resolver's call started and what it returned (or that it failed).
//
// DnsRun <scenario.json> <story> <milliseconds> <out.json>
//
// Server 192.168.0.10 knows web.filius.de, filius.de's mail exchange and
// refers schule.de to 192.168.0.11, which knows www.schule.de. The stories,
// each the resolver of a client (Resolver, as FILIUS' programs call it):
// address 192.168.0.20 asks for web.filius.de (A)
// mail 192.168.0.20 asks for filius.de's mail server (MX, then its A)
// referral 192.168.0.20 asks for www.schule.de (A), referred to .11
// unknown 192.168.0.20 asks for x.unbekannt.org, which no one knows
// noServer 192.168.0.21 asks its DNS server 192.168.0.99, which is not there
import filius.software.dns.Resolver;
import java.util.ArrayList;
import java.util.List;
public class DnsRun {
static final String CLIENT = "00000000-0000-0000-0000-000000000101";
static final String LOST_CLIENT = "00000000-0000-0000-0000-000000000104";
static ScenarioNetwork network;
static final List<Object[]> events = new ArrayList<>();
interface Call {
String run() throws Exception;
}
static void event(String what) {
events.add(new Object[] { System.currentTimeMillis() - network.start, what });
}
static void call(String label, Call call) {
event(label + " called");
try {
event(label + " returned " + call.run());
} catch (Exception e) {
event(label + " failed " + e.getClass().getSimpleName());
}
}
public static void main(String[] args) throws Exception {
network = ScenarioNetwork.read(args[0]);
String story = args[1];
long end = Long.parseLong(args[2]);
network.start();
Thread.sleep(500);
Resolver client = network.system(CLIENT).holeDNSClient();
switch (story) {
case "address":
call("holeIPAdresse web.filius.de", () -> client.holeIPAdresse("web.filius.de"));
break;
case "mail":
call("holeIPAdresseMailServer filius.de", () -> client.holeIPAdresseMailServer("filius.de"));
break;
case "referral":
call("holeIPAdresse www.schule.de", () -> client.holeIPAdresse("www.schule.de"));
break;
case "unknown":
call("holeIPAdresse x.unbekannt.org", () -> client.holeIPAdresse("x.unbekannt.org"));
break;
case "noServer":
Resolver lost = network.system(LOST_CLIENT).holeDNSClient();
call("holeIPAdresse web.filius.de", () -> lost.holeIPAdresse("web.filius.de"));
break;
default:
throw new IllegalArgumentException("unknown story " + story);
}
long rest = network.start + end - System.currentTimeMillis();
if (rest > 0) Thread.sleep(rest);
ScenarioNetwork.Json out = new ScenarioNetwork.Json();
out.open('{');
network.writeFrames(out);
out.key("events").open('[');
for (Object[] each : events) out.open('[').number((Long) each[0]).string((String) each[1]).close(']');
out.close(']');
out.close('}');
network.stop();
ScenarioNetwork.write(args[3], out);
System.exit(0);
}
}
@@ -18,6 +18,7 @@ import filius.hardware.knoten.Rechner;
import filius.hardware.knoten.Switch;
import filius.rahmenprogramm.nachrichten.Lauscher;
import filius.software.dhcp.DHCPServer;
import filius.software.dns.DNSServer;
import filius.software.netzzugangsschicht.EthernetFrame;
import filius.software.system.InternetKnotenBetriebssystem;
import filius.software.transportschicht.Segment;
@@ -121,6 +122,21 @@ public class ScenarioNetwork {
server.setAktiv(Boolean.TRUE.equals(config.get("active")));
servers.add(entry.getKey());
}
// A DNS server: installed, its records written to /dns/hosts (TTL 3600), switched on.
for (Map.Entry<String, Object> entry : map(scenario.getOrDefault("dnsServers", new HashMap<>())).entrySet()) {
Map<String, Object> config = map(entry.getValue());
InternetKnotenBetriebssystem os = system(nodes.get(entry.getKey()));
DNSServer dns = new DNSServer();
dns.setSystemSoftware(os);
os.getInstallierteAnwendungen().put("filius.software.dns.DNSServer", dns);
for (Object line : list(config.getOrDefault("records", new ArrayList<>()))) {
String[] parts = ((String) line).split(" ");
dns.hinzuRecord(parts[0], parts[1], parts[2]);
}
dns.setRecursiveResolutionEnabled(Boolean.TRUE.equals(config.get("recursive")));
dns.setAktiv(true);
servers.add(entry.getKey());
}
for (Object each : list(scenario.getOrDefault("dhcpClients", new ArrayList<>()))) {
system(nodes.get((String) each)).setDHCPKonfiguration(true);
}