SMTP on port 25 stores mails for the domain's accounts as Java reads them anew and tells a local sender of unknown recipients; POP3 on port 110 hands them out. The mailboxes are in the snapshot and go back into the project. EmailRun plays four stories over the network; MailCases feeds Java's workers without one (26 cases, de and en). 18 cases name Java bugs that the core does not take. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
728 lines
41 KiB
Markdown
728 lines
41 KiB
Markdown
# Java-Orakel
|
||
|
||
Das Orakel misst Javas **echtes** Verhalten, Fehler eingeschlossen. Der
|
||
TypeScript-Leser folgt Java, übernimmt aber keine Java-Fehler (Datenverlust,
|
||
Absturz, eigene Ausgabe nicht wieder lesbar, Tippfehler, Verstoß gegen den
|
||
Standard); jede solche Abweichung steht in den Tests namentlich mit Grund
|
||
(`JAVA_BUGS`). Was Java selbst schreibt — der Korpus — wird exakt wie in
|
||
Java gelesen.
|
||
|
||
Java-Filius selbst liest den Korpus (`corpus/`) und schreibt, was es gefunden
|
||
hat, nach `corpus/corpus.java.json`. **Java ist der Schiedsrichter für das
|
||
Dateiformat** — der Swift-Leser verliert in 10 von 23 Dateien Geräte (Plan
|
||
§13, M2).
|
||
|
||
```bash
|
||
npm run oracle:java # = tools/java-oracle/run.sh
|
||
```
|
||
|
||
Braucht JDK 17+, Maven und `xvfb-run`. Filius öffnet beim Laden sein
|
||
Hauptfenster (`Information.reset` → `JMainFrame`), deshalb läuft es an einem
|
||
virtuellen Bildschirm statt „headless".
|
||
|
||
* Gelesen wird so, wie Filius eine Datei öffnet: `SzenarioVerwaltung.laden`
|
||
entpackt das Archiv, kopiert die eigenen Anwendungen
|
||
(`projekt/anwendungen`) dorthin, wo Filius' Klassenlader sucht, und lädt
|
||
dann die Konfiguration — Javas Weg, kein Nachbau (seit 30.09.2026, M8).
|
||
`--network` lädt nur die Konfiguration über die private Methode
|
||
`netzwerkLaden` (per Reflection), wie vorher. Der Unterschied am Korpus:
|
||
In `anwendung_komplett.fls` und `multicast_server.fls` sind dann die
|
||
eigenen Anwendungen `Accumulator` und `Broadcast` installiert. Filius'
|
||
Klassenlader ist ein Singleton: Eine einmal geladene eigene Klasse bleibt
|
||
für die übrigen Dateien desselben Laufs geladen. Am Korpus stört das
|
||
nicht, denn keine Datei nutzt eine Klasse, die sie nicht mitbringt.
|
||
* Stand: `gitlab.com/filius1/filius` @ `6640354` (2.14.0), wird beim ersten
|
||
Lauf nach `.upstream/` geholt und gebaut.
|
||
* **Gewürfeltes:** Was nicht in der Datei steht, würfelt Filius zum Teil
|
||
(MAC-Adressen, die SSID eines Switches). Jede Datei wird deshalb **zweimal**
|
||
geladen; jeder Wert, der sich zwischen beiden Läufen unterscheidet,
|
||
erscheint als `null`. Zwei Läufe des Werkzeugs sind byte-gleich. (Bis
|
||
29.09.2026 suchte es die MAC im Dateitext und hielt dabei acht
|
||
kleingeschriebene für gewürfelt.)
|
||
* Bisher ausgelesen: Geräte (Klasse, Name, Beschriftung, Position,
|
||
Schnittstellen samt Funk-Eigenschaft `wireless`, bei Rechner/Notebook `useIPAsName`/`useMACAsName`), unter
|
||
`system` SSID, Lernzeit des Switches, RIP, manuelle Routen, die Firewall
|
||
von Router und Heimrouter, DHCP-Client und -Server, die statische
|
||
NAT-Tabelle des Heimrouters, die Verbindung eines Modems und das
|
||
Dateisystem (Inhalte als Länge und SHA-256, damit die Referenz klein
|
||
bleibt; bei Router und Heimrouter stehen vorn die Dateien, die Javas
|
||
Konstruktor aus eigenen Vorlagen erzeugt, dahinter die aus der Datei) und
|
||
die installierten Programme eines Rechners mit ihren Einstellungen (die
|
||
persönliche Firewall samt Regeln; je Programm `onItsSystem`: ob es am
|
||
Betriebssystem seines Knotens hängt) (Klassen, die Filius
|
||
nicht mitbringt — eigene Anwendungen im Archiv unter `projekt/anwendungen`
|
||
— fehlen, weil das Werkzeug nur `konfiguration.xml` lädt); E-Mail: Konten des Servers aus der Bean-Liste und aus `/mailserver/konten.txt` (`kontenLaden`), Ordner und Adressbuch des Clients und sein Konto aus `/konten.txt` (`laden`); ein Empfänger, den eine ältere Datei als Zeichenkette hält, samt dem, was Javas `AddressEntry(String)` daraus macht; Kabel (Enden als Gerät/Schnittstelle),
|
||
Doku-Elemente.
|
||
|
||
## Getter-Standardwerte
|
||
|
||
XMLEncoder teilt gleiche `Integer`/`Boolean`-Objekte. Ein Wert, der einmal als
|
||
Ergebnis eines Getters geschrieben wurde (`<void id="Integer0"
|
||
property="iconTextGap"/>`), taucht später nur noch als Verweis auf — etwa als
|
||
x-Koordinate eines Geräts. Daran scheitert der Swift-Leser (Plan §13, M2).
|
||
|
||
`GetterDefaults` erzeugt die betroffenen Klassen wie XMLDecoder (ohne
|
||
Argumente) und ruft den Getter auf; das Ergebnis steht in
|
||
`packages/fls/src/javaDefaults.json`. Gemessen, nicht geraten: Filius' Knopf
|
||
setzt `verticalTextPosition` auf 3 (BOTTOM), nicht auf Swings 0 (CENTER).
|
||
|
||
Dasselbe gilt für **statische Konstanten**: Die erste `0` einer Datei kann als
|
||
`<object class="javax.swing.SwingConstants" field="CENTER"/>` stehen, und jede
|
||
spätere `0` — etwa die Rot-, Grün- und Blauwerte einer schwarzen Doku-Farbe —
|
||
verweist darauf. Schreibweise im Aufruf: `klasse::FELD`, Ergebnis
|
||
`"SwingConstants.CENTER": 0`.
|
||
|
||
## `IP.ipCheck`
|
||
|
||
Java-Filius schickt jede IP-Adresse und Maske beim Laden durch
|
||
`IP.ipCheck`. `IpCheckCases` ruft es für jede Zeile von
|
||
`packages/fls/test/ip-check.cases.txt` auf (ASCII, Nicht-ASCII als
|
||
`\uXXXX`) und schreibt `ip-check.java.json`. Gemessen: leere Abschnitte
|
||
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.
|
||
|
||
## Was Java aus unseren Dateien lädt
|
||
|
||
`run.sh` lädt auch die Dateien unter `packages/fls/test/written/`, die
|
||
`topologyWriter.test.ts` mit `UPDATE_WRITTEN=1` schreibt, und unter
|
||
`packages/fls/test/exported/`, die `exportProject.test.ts` mit
|
||
`UPDATE_EXPORTED=1` schreibt (beide nicht eingecheckt), mit derselben
|
||
Lademethode wie den Korpus, und schreibt `written.java.json` bzw.
|
||
`exported.java.json` daneben, samt SHA-256 jeder Datei
|
||
(`--sha256`), damit der Test merkt, wenn der Befund nicht mehr zur Ausgabe
|
||
passt. Je Kabel steht auch `panel`: die Geräte, zwischen denen die
|
||
Oberfläche es zeichnet (`JCablePanel.ziel1/ziel2`) — im Korpus bei allen 183
|
||
Kabeln die Geräte, die es verbindet.
|
||
|
||
## Wie Java Base64 schreibt
|
||
|
||
`Base64Cases` ruft Filius' eigenes `Base64.encodeBytes` (iHarder 2.x) auf
|
||
Byte-Folgen von 19 Längen rund um die Zeilenlänge auf
|
||
(`packages/fls/test/base64.java.json`). Befund: nach je 76 Zeichen ein
|
||
`\n`, auch nach der letzten vollen Zeile, dann der aufgefüllte Rest ohne
|
||
Umbruch. So stehen Bilder in den Dateien, auch die, die Javas Konstruktoren
|
||
selbst anlegen.
|
||
|
||
## Was Java aus unserem XML macht
|
||
|
||
`BeansDump` liest jede `*.xml` unter `packages/fls/test/beans/` mit
|
||
`XMLDecoder` und beschreibt, was dabei herauskommt (Beans über ihre
|
||
Eigenschaften mit Lese- und Schreibmethode, Listen, Maps, Arrays,
|
||
`Rectangle`, `Color`; ein zweites Mal gesehenes Objekt als Verweis) —
|
||
`beans.java.json`. Fehler, die der Decoder sonst nur ins Log schreibt,
|
||
sammelt ein `ExceptionListener`. Zeichenketten stehen als ASCII mit
|
||
`\uXXXX`, weil ein einsames Surrogat in UTF-8 sonst zu „?“ würde.
|
||
|
||
## E-Mail-Kontodateien
|
||
|
||
`EmailFileCases server|client` legt für jede Zeile von
|
||
`packages/fls/test/email-server.cases.txt` bzw. `email-client.cases.txt`
|
||
einen Rechner mit dieser Datei an (`/mailserver/konten.txt` bzw.
|
||
`/konten.txt`) und schreibt, welche Konten `EmailServer.kontenLaden()` bzw.
|
||
`EmailAnwendung.laden()` daraus machen. Gemessen: Eine einzige Serverzeile
|
||
mit weniger als fünf Feldern lässt Java die ganze Datei verwerfen
|
||
(`NoSuchElementException` — die Simulation startet nicht); leere Felder
|
||
überspringt Java, statt sie zu zählen; eine Nachricht mit leerem Text geht
|
||
verloren, weil `split` leere Felder am Ende abschneidet. Beim Client gewinnt
|
||
die letzte Zeile, auch eine kaputte, die das Konto leert.
|
||
|
||
## DHCP zur Laufzeit
|
||
|
||
`DhcpRun` baut das Netz eines Kern-Szenarios (`packages/core/test/scenarios/*.scenario.json`:
|
||
Knoten, Kabel, Geräte, DHCP-Server und -Clients; die Schritte bleiben
|
||
unbeachtet) in Java-Filius nach und startet es wie der Aktionsmodus
|
||
(`starten()` jeder Systemsoftware). Nach der angegebenen Echtzeit schreibt es
|
||
nach `<name>.java.json`, was jede Netzwerkkarte gesehen hat (Filius' eigener
|
||
Mitschnitt, der `Lauscher`: Zeit, MACs, ARP-Operation und -Adressen,
|
||
IP/UDP-Adressen und -Ports samt DHCP-Text), und die Adressen jedes Rechners
|
||
am Ende. Die MAC eines Ports ist dieselbe wie im Kern (`mac`, sonst
|
||
`stableGeneratedMacAddress`).
|
||
|
||
* Switches und DHCP-Server starten zuerst: Javas Client wartet auf jeden
|
||
aktiven Server, findet sie aber über die Knotenliste der Oberfläche, die das
|
||
Werkzeug nicht hat.
|
||
* `GUIContainer.getGUIContainer()` wird vor dem Start einmal aufgerufen. Im
|
||
echten Filius steht das Fenster schon; ohne das legen zwei Clients den
|
||
Singleton gleichzeitig an (nicht synchronisiert), und einer von ihnen
|
||
sendet nie etwas (gemessen am Szenario `dhcp-lan`).
|
||
* Die Zeiten schwanken von Lauf zu Lauf um einige zehn Millisekunden (echte
|
||
Threads), die Link-Local-Adresse würfelt Java mit `Math.random`. Die
|
||
Reihenfolge der Nachrichten eines Clients und ihr Inhalt schwanken nicht.
|
||
|
||
Gemessen (30.09.2026), was Swift anders macht: Je Versuch sendet Java genau
|
||
ein DISCOVER bzw. REQUEST und wartet dann bis zu einer RTT (2,5 s) auf die
|
||
Antwort; eine unpassende Antwort (etwa das Angebot des zweiten Servers) liest
|
||
es und verwirft sie. Vor dem REQUEST prüft es die angebotene Adresse per ARP
|
||
und wartet dabei eine RTT; antwortet ein anderer Rechner, lehnt es mit
|
||
DHCPDECLINE ab. Javas Server übergeht das DECLINE und hält die Adresse
|
||
weiter als Angebot fest (4 RTT). Nach dem fünften Fehlversuch (Zeitablauf)
|
||
nimmt der Client eine Link-Local-Adresse `169.254.1.x/16`: ARP-Probe, eine
|
||
RTT warten, dann ein ARP-Reply an alle.
|
||
|
||
Die RTT ist in Java nicht fest: `Verbindung.holeRTT` = 50 Kabel ×
|
||
Verzögerung je Kabel × RTT-Faktor (Startoption, 1). Beim Anlegen des
|
||
Fensters setzt das Hauptmenü den Regler auf 90 %, also 55 ms je Kabel und
|
||
2750 ms RTT — so laufen die `DhcpRun`-Referenzen; gemessen sind die
|
||
Wartezeiten 2745 bis 2751 ms. Außerdem befördert ein Java-Kabel einen Frame
|
||
nach dem anderen (`SimplexVerbindung` schläft die Verzögerung mit gesperrtem
|
||
Puffer): Sendet eine Karte zwei Frames direkt nacheinander, kommt der zweite
|
||
eine Kabellänge später.
|
||
|
||
Filius' Mitschnitt selbst ist dabei nicht ganz zuverlässig: Starten zwei
|
||
Clients zugleich, fehlt auf der Karte des zweiten dessen eigenes DISCOVER
|
||
(vier von vier Läufen von `dhcp-lan`; der Server empfängt es).
|
||
|
||
## TCP zur Laufzeit
|
||
|
||
`TcpRun` spielt auf dem Netz von `tcp-lan` (Server 192.168.0.10, Clients .20
|
||
und .30) einen festen Ablauf von Socket-Aufrufen ab, wie Filius' Anwendungen
|
||
sie machen, den Server in einem eigenen Thread. Es schreibt nach
|
||
`tcp-java-<ablauf>.java.json` die Frames jeder Karte (mit TCP-Flags,
|
||
Sequenz- und Bestätigungsnummer), wann jeder Aufruf begann, zurückkam oder
|
||
scheiterte, und jeden Zustand jedes Sockets (alle 2 ms abgefragt). Netzbau
|
||
und Mitschnitt teilen `DhcpRun` und `TcpRun` in `ScenarioNetwork`.
|
||
|
||
* `exchange`: Verbindung zu Port 80, `GET /`, Antwort 3000 × `x` (drei
|
||
Segmente), der Client schließt, der Server eine Sekunde nach
|
||
`CLOSE_WAIT`.
|
||
* `closedPort`: Verbindung zu Port 81, auf dem niemand lauscht.
|
||
* `lostData`: verbunden, dann gehen alle Frames verloren
|
||
(`Verbindung.setDrop`, wie der Paketverlust des Kerns), während der Client
|
||
sendet; der Verlust endet, wenn `senden` scheitert.
|
||
* `lostSynAck`: das erste SYN+ACK des Servers geht verloren.
|
||
* `lostFin`: verbunden, der Client schließt; sein FIN geht verloren.
|
||
* `lostLastFin`: verbunden, der Client schließt; das FIN des Servers geht
|
||
verloren.
|
||
|
||
Einen einzelnen Frame verliert `loseFirst`: Per Reflection ersetzt es den
|
||
Ausgangspuffer der Karte (`Port.ausgangsPuffer`) durch einen, der den ersten
|
||
passenden Frame verwirft, statt ihn einzureihen. Das Kabel wartet am alten
|
||
Puffer und wird dort einmal geweckt. Der Lauscher des Absenders sieht den
|
||
Frame wie bei `setDrop` (er schreibt nach dem Einreihen); anders als dort
|
||
belegt der Frame das Kabel nicht.
|
||
|
||
Gemessen (30.09.2026, 90 %, RTT 2750 ms), was Swift anders macht:
|
||
|
||
* Jeder Versuch wartet 3 RTT (8250 ms): Daten gehen dreimal hinaus (1720,
|
||
9970, 18220 ms), `senden` scheitert 8250 ms nach dem dritten.
|
||
* Scheitert `senden`, schließt Java ordentlich: FIN, ACK, FIN, ACK. Swift
|
||
setzt den Socket ohne FIN auf CLOSED.
|
||
* `TIME_WAIT` dauert 3 RTT (8250 ms; oder bis zum nächsten Segment), nicht
|
||
eine RTT.
|
||
* Die Gegenseite schließt von selbst: Kommt ein FIN, geht Javas
|
||
Socket-Thread aus `listen` in `close` und sendet sein FIN sofort
|
||
(`LAST_ACK` rund 60 ms nach dem FIN), nicht erst, wenn die Anwendung
|
||
`schliessen` aufruft.
|
||
* Der Client sendet nur **ein** SYN: Nach dem ersten Zeitablauf setzt
|
||
`connectClientMode` `closeSocket`, und die Schleife über drei Versuche
|
||
endet (`verbinden` scheitert nach 3 RTT). Der Server-Socket wiederholt sein
|
||
SYN+ACK nie.
|
||
* Nach einem verlorenen SYN+ACK gibt der Server nach 3 RTT auf und schickt aus
|
||
`SYN_RCVD` ein FIN (`close`). In zwei von drei Läufen wartete er 6 RTT: Der
|
||
Vergleich `vergangen > defaultTimeout()` liegt genau auf der Grenze.
|
||
* Ein FIN geht einmal hinaus, nie wieder. Danach wartet `close` höchstens
|
||
fünfmal 3 RTT; jedes ankommende Segment beendet eine Wartezeit. Dann ist
|
||
der Socket weg, sein Zustand bleibt aber stehen (`FIN_WAIT_1`,
|
||
`FIN_WAIT_2`, `LAST_ACK`). Die Gegenseite eines verlorenen FIN bleibt
|
||
`ESTABLISHED`.
|
||
* 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.
|
||
|
||
Auf dem Netz von `dns-recursive` (sonst wie `dns-lan`) löst 192.168.0.10
|
||
rekursiv auf, sein eigener DNS-Server ist .11, und er führt die Zone
|
||
`filius.de` mit sich selbst als Nameserver. Abläufe
|
||
(`dns-recursive-java-<ablauf>.java.json`): `address` (eigener Eintrag),
|
||
`referral` (`www.schule.de` über den NS von `schule.de`), `unknown`
|
||
(`x.unbekannt.org`), `unknownInZone` (`nix.schule.de`, das .11 nicht kennt),
|
||
`unknownInOwnZone` (`nix.filius.de`).
|
||
|
||
Gemessen (30.09.2026): Findet der Server nichts in seinen Einträgen, fragt
|
||
er selbst, mit seinem eigenen Resolver (iterativ, 5 RTT je Anfrage): zuerst
|
||
den Nameserver der längsten passenden Zone aus seinen Einträgen, bringt das
|
||
nichts, seinen eigenen DNS-Server (nur wenn das nicht er selbst ist). Die
|
||
Antwort an den Client enthält, was er gelernt hat, sonst den Verweis (NS und
|
||
A). Bei `unknownInZone` fragt er .11 also zweimal. Bei `unknownInOwnZone`
|
||
fragt er als Nameserver der Zone sich selbst, ohne Ende: Jede Ebene erzeugt
|
||
die nächste (ohne Frames, die Anfragen bleiben im Rechner), der Client gibt
|
||
nach 5 RTT auf, dann laufen die inneren Ebenen nacheinander ab und fragen
|
||
ersatzweise .11, eine Anfrage alle 55 ms. Ein Java-Fehler; der Kern fragt
|
||
sich weder als Nameserver noch als DNS-Server selbst.
|
||
|
||
## ARP zur Laufzeit
|
||
|
||
`ArpRun` sendet auf dem Netz von `arp-routed` (10.0.0.2 über einen Switch am
|
||
Router 10.0.0.1, 20.0.0.2 am Router 20.0.0.1; `ScenarioNetwork` baut dazu
|
||
Router mit `setzeAnzahlAnschluesse` und den `interfaces` des Szenarios) an
|
||
Adressen, die keine Karte hat, und schreibt nach `arp-java-<ablauf>.java.json`
|
||
die Frames und die Aufrufe. Abläufe: `pingLan` (Ping an 10.0.0.9,
|
||
`ICMP.sendProbe`), `pingRouted` (Ping an 20.0.0.9), `udpTwice` (zwei
|
||
Datagramme an 10.0.0.9), `udpBehind` (eines an 10.0.0.9, dann eines an
|
||
20.0.0.2), `routedUdpBehind` (eines an 20.0.0.9, dann eines an 20.0.0.2:
|
||
der Router leitet beide weiter), `routedPingBesideUdp` (Ping an 20.0.0.2,
|
||
damit der Router es kennt, ein Datagramm an 20.0.0.9, 100 ms später wieder
|
||
ein Ping an 20.0.0.2) und `routedPingBehindPing` (ein Echo-Request an
|
||
20.0.0.9 ohne Warten auf Antwort, dann ein Ping an 20.0.0.2). ICMP-Frames
|
||
schreibt `ScenarioNetwork` mit Typ, Code, Adressen und Sequenznummer.
|
||
|
||
Gemessen (30.09.2026), was der Kern (nach Swift) anders macht:
|
||
|
||
* ARP fragt bis zu zweimal und wartet je eine RTT (`holeARPTabellenEintrag(ziel,
|
||
2, …)`); dann „Host unreachable" (ICMP 3/1). Einen Negativ-Cache gibt es
|
||
nicht: das nächste Paket fragt wieder zweimal.
|
||
* Wer wartet, ist ein Thread: beim Ping der Aufrufer (5,5 s, dann ICMP 3/1
|
||
vom eigenen Rechner, Absender 127.0.0.1), beim Weiterleiten der IP-Thread
|
||
des Routers, bei UDP und TCP der Transport-Thread je Protokoll und Knoten.
|
||
`senden` kehrt sofort zurück, aber der Transport-Thread hält seine
|
||
Warteschlange gesperrt, solange er wartet: das nächste `senden` blockiert
|
||
bis dahin (5,5 s), und ein Datagramm an einen erreichbaren Rechner geht
|
||
erst danach.
|
||
* Ein Ping wartet eine RTT auf die Antwort; das „Host unreachable" des
|
||
Routers (nach dessen 5,5 s) kommt zu spät.
|
||
|
||
Gemessen (30.09.2026) an den Router-Abläufen:
|
||
|
||
* Der Router leitet UDP und TCP in seinem IP-Thread weiter, ICMP in seinem
|
||
ICMP-Thread (`EthernetThread` sortiert nach Protokoll in zwei Puffer).
|
||
Beide halten ihren Puffer, solange ARP wartet: Ein Datagramm an 20.0.0.2
|
||
wartet 5,5 s hinter einem an 20.0.0.9, ein Echo-Request ebenso hinter
|
||
einem Echo-Request (der Ping kehrt nach einer RTT ohne Antwort zurück, die
|
||
Antwort kommt später). Ein Ping neben einem hängenden Datagramm geht sofort
|
||
durch. Auch das Echo-Reply eines Rechners schickt sein ICMP-Thread.
|
||
* Javas ARP weckt mit `arpTabelle.notify()` nur **einen** Wartenden, nicht
|
||
den, dessen Antwort kam. Warten zwei Threads (im Rechner der UDP-Thread und
|
||
der Ping-Aufrufer, im Router IP- und ICMP-Thread), wartet der andere bis
|
||
zum Ende seiner RTT; einer, der fälschlich geweckt wurde, verbraucht einen
|
||
Versuch und fragt sofort wieder (gemessen: das zweite ARP nach 371 ms
|
||
statt 2750, der ICMP-Thread schickte den Echo-Request eine RTT zu spät).
|
||
Ein Java-Fehler, den der Kern nicht übernimmt: Er weckt alle, die auf die
|
||
Adresse warten. Die Abläufe meiden ihn (nur ein Wartender zur Zeit).
|
||
* Ein Datagramm, das `senden` dem UDP-Thread übergibt, geht oft erst nach
|
||
einem Frame hinaus, den der Aufrufer gleich danach selbst schickt (der
|
||
Thread wacht später auf). `routedPingBesideUdp` wartet deshalb 100 ms.
|
||
|
||
Der Kern spielt alle sieben Abläufe wie Java (`packages/core/test/arpJava.test.ts`:
|
||
Frames, Abstände und die Zeiten, zu denen die Aufrufe zurückkehren). Die UDP-Abläufe
|
||
seit F2.4b-2: ein Senden kehrt sofort zurück wie in Java (Swifts Senden wartete, bis
|
||
alle Frames angekommen waren), und je Knoten und Protokoll sendet ein Transport-Thread
|
||
ein Segment nach dem anderen.
|
||
|
||
## RIP zur Laufzeit
|
||
|
||
`RipRun` startet das Netz von `rip-chain` (PC 10.0.1.2 – R1 – R2 – R3 – PC
|
||
10.0.3.2, jeder Router mit RIP; `ScenarioNetwork` schaltet RIP für die im
|
||
Feld `rip` genannten Router ein) und schreibt nach `rip-java-<ablauf>.java.json`
|
||
die Frames, jede Änderung jeder RIP-Tabelle (alle 2 ms abgefragt, Zeile
|
||
`netz/maske gateway schnittstelle hops; …`) und die Aufrufe. Java würfelt die
|
||
Pausen eines Routers aus einem neuen `Random`; `RipRun` setzt jedem Router
|
||
per Reflection (`VermittlungsrechnerBetriebssystem.ripbeacon`,
|
||
`RIPBeacon.rand`) den Startwert, den der Kern ihm gibt (`ripSeed`: Startwert
|
||
des Szenarios, `0x52495000` und FNV-1a der Router-ID). Dann würfeln beide
|
||
dieselben Pausen.
|
||
|
||
* `converge`: die Tabellen füllen sich, bei 45 s pingt 10.0.1.2 10.0.3.2.
|
||
* `expire`: bei 10 s schweigt R3 (`RIPTable.setNextBeacon(Long.MAX_VALUE)`),
|
||
bei 125 s der Ping noch einmal.
|
||
|
||
Gemessen (30.09.2026, dazu die Pausenformel am JDK):
|
||
|
||
* Die Pause ist `(int) (30000 * (rand.nextFloat() / 3 + 0.84))`; `nextFloat() / 3`
|
||
rechnet Java in `float`, in etwa einem von 10.000 Fällen eine Millisekunde
|
||
anders als in `double`.
|
||
* Eine gelernte Route bekommt 16 Hops erst beim nächsten Beacon ihres Routers
|
||
nach dem Verfallen (`RIPTable.check`): R3s Netz verfiel bei R2 nach rund
|
||
76,4 s, R2 setzte es bei seinem Beacon um 86,7 s auf 16; R1 hörte die 16 vom
|
||
selben Router und meldete sofort. Der Ping bekommt dann ICMP 3/0 von R1.
|
||
Der Kern setzt die Route seit 01.10.2026 genau beim Verfallen auf 16 und
|
||
meldet sofort (besser als Java, wo Java nur bei seinem Takt nachsieht);
|
||
`ripJava.test.ts` vergleicht `expire` bis dahin und prüft danach, dass R2
|
||
zur Zeit und R1 ein Kabel später auf 16 steht (`BETTER_THAN_JAVA_FROM`).
|
||
* Die Router starten nacheinander, rund 20 ms auseinander; wer einen
|
||
Nachbarn vor seinem ersten Beacon hört, sendet die gelernte Route gleich
|
||
mit. Senden mehrere zugleich, fehlt im Lauscher mancher eigene Frame.
|
||
|
||
## NAT zur Laufzeit
|
||
|
||
`NatRun` startet das Netz von `nat-home` (PC 192.168.0.10 am LAN-Port des
|
||
Heimrouters 192.168.0.1; sein WAN-Port 42.0.0.10/8 an einem Switch mit
|
||
42.0.0.20 und 42.0.0.30; Portweiterleitung TCP 8080 an 192.168.0.10:80;
|
||
`ScenarioNetwork` baut Heimrouter und das Feld `portForwarding` über
|
||
`GatewayFirmware.setStaticNAT`) und schreibt nach `nat-java-<ablauf>.java.json`
|
||
die Frames, die Aufrufe und am Ende die NAT-Tabelle des Heimrouters. Abläufe:
|
||
`pingOut`, `udpOut` (Antwort vom Server), `udpTwoRemotes` (danach vom selben
|
||
Port an 42.0.0.30), `tcpOut`, `forward` (von 42.0.0.20 an 42.0.0.10:8080),
|
||
`pingWan` (42.0.0.20 pingt die WAN-Adresse, dann 192.168.0.10).
|
||
|
||
Ein UDP-Server im Werkzeug ist ein aktiver Socket auf Port 7: ein passiver
|
||
trägt seinen Port erst ein, wenn Daten da sind, die vorher nicht ankommen.
|
||
|
||
`ScenarioNetwork` schreibt seit F2.6 die Frames **jeder** Karte eines Knotens
|
||
(die erste unter der Knoten-ID, weitere unter `id#index`); vorher nur die
|
||
erste, so dass bei Routern und Heimroutern die zweite Karte fehlte. Die
|
||
ARP-, RIP- und NAT-Läufe sind damit neu aufgenommen; die übrigen Knoten haben
|
||
nur eine Karte.
|
||
|
||
Gemessen (30.09.2026), was der Heimrouter tut (`NatGateway`, dessen
|
||
Threads zwischen Karte und Ethernet-Schicht sitzen):
|
||
|
||
* Ausgehend (Ziel außerhalb des LAN, nicht die WAN-Adresse) wird die
|
||
Absenderadresse zur WAN-Adresse, der Port zu einem freien Port des
|
||
Heimrouters (nach Javas Zufall); ICMP behält seine Kennung, die als Port
|
||
des Eintrags dient. Einen Eintrag legt ein TCP-SYN, jedes UDP-Datagramm
|
||
und jedes ICMP-Paket an, aber nur, wenn der LAN-Endpunkt noch keinen hat.
|
||
* Eingehend an die WAN-Adresse sucht der Heimrouter den Eintrag nach
|
||
Gegenstelle, WAN-Port und Protokoll, sonst nach der Portweiterleitung, die
|
||
dann einen dynamischen Eintrag für die Gegenstelle anlegt
|
||
(`DynamicEnryFromStatic`).
|
||
* Ein zweiter Server, an den derselbe LAN-Endpunkt sendet, bekommt das
|
||
Datagramm mit demselben WAN-Port, seine Antwort aber findet keinen Eintrag
|
||
(der gilt nur für die erste Gegenstelle) und kommt nicht ins LAN.
|
||
* Ein Ping an die WAN-Adresse beantwortet der Heimrouter, einer an eine
|
||
LAN-Adresse von außen verschwindet.
|
||
* Dynamische Einträge verfallen nach 300 s mal Javas Verzögerungsfaktor
|
||
(bei 90 % 55 Minuten), geprüft jede Sekunde, aufgefrischt nur von
|
||
eingehenden Paketen (aus dem Code, nicht gemessen). Der Kern verfällt sie
|
||
seit 01.10.2026 genau in diesem Alter statt beim nächsten Sekundentakt.
|
||
|
||
|
||
## WLAN zur Laufzeit
|
||
|
||
`WlanRun` startet das Netz von `wlan-lan` (Notebooks 192.168.1.11 und .12
|
||
mit Funkkarte, je über ein Funkkabel am Switch mit der SSID „schule";
|
||
PC 192.168.1.13 an einem Kabel; Notebook 192.168.1.14 mit Funkkarte und
|
||
SSID „schule", aber ohne Kabel) und schreibt nach `wlan-java-<ablauf>.java.json`
|
||
die Frames und die Aufrufe. `ScenarioNetwork` setzt dazu `Kabel.setWireless`
|
||
nach dem Feld `wireless` eines Kabels, die SSID des Switches (`switches`)
|
||
und Funkkarte und SSID der Rechner (`wireless`). Abläufe: `pingWired` (.11
|
||
pingt .13), `pingWireless` (.11 pingt .12), `pingWithoutCable` (.11 pingt
|
||
.14), `pingFromWithoutCable` (.14 pingt .11).
|
||
|
||
Gemessen (01.10.2026), wie im Code (`Kabel.wireless`, `Betriebssystem.ssid`
|
||
liest nur die Oberfläche):
|
||
|
||
* Ein Funkkabel ist zur Laufzeit ein Kabel wie jedes andere: dieselben
|
||
Frames zu denselben Zeiten über Funk wie über Kabel (Ping 470 ms).
|
||
* Eine SSID verbindet nichts. Das Notebook ohne Kabel bleibt unerreichbar
|
||
(zweimal ARP, nach 5,5 s „Host unreachable" beim Absender), und es selbst
|
||
erreicht niemanden. Das Funkkabel legt nur Javas Editor
|
||
(`JHostKonfiguration.updateWifiConnection`: das alte entfernen, an den
|
||
Switch mit der SSID anschließen); ändert sich die SSID eines Switches,
|
||
entfernt er dessen Funkkabel, ohne neu zu verbinden. So entsteht in einer
|
||
Datei ein Rechner mit SSID ohne Kabel.
|
||
|
||
Swift verbindet zur Laufzeit jeden Rechner mit eingeschaltetem WLAN mit
|
||
einem freien Port des Switches seiner SSID, auch ohne Kabel
|
||
(`wirelessAssociations`). Der Kern folgt Java: Er kennt nur die Kabel.
|
||
|
||
## Mitschnitt je Karte
|
||
|
||
Was `ScenarioNetwork.writeFrames` schreibt, ist Javas Mitschnitt selbst
|
||
(`Lauscher.datenEinheiten`): je Karte jeder Frame, den sie gesendet oder
|
||
empfangen hat. Seit F2.8a vergleicht der Kern seinen Mitschnitt Karte für
|
||
Karte damit (`expectSameCaptures` in `packages/core/test/javaRun.ts`), nicht
|
||
mehr nur jeden Frame dort, wo er zuerst auftaucht.
|
||
|
||
Dabei fiel auf (01.10.2026): `ScenarioNetwork` übernahm die Lernzeit eines
|
||
Switches (`switches.<id>.retention`) nicht; Java lief mit 300 s, der Kern
|
||
mit den 5 s der Szenarien `tcp-lan` und `dhcp-*`. Der alte Vergleich sah den
|
||
Unterschied nicht: Ein gefluteter Frame erscheint nur auf zusätzlichen
|
||
Karten. Seit F2.8a setzt das Werkzeug die Lernzeit
|
||
(`SwitchFirmware.setRetentionTime`), die TCP- und DHCP-Läufe sind neu
|
||
aufgenommen. Mit 5 s flutet auch Javas Switch in `lostData` den FIN nach
|
||
26 s an alle Karten, wie der Kern. `dhcp-lan` behält die alte Aufnahme: Die
|
||
neue unterscheidet sich nur darin, welcher der beiden Clients zuerst sendet;
|
||
nach 5 s gehen dort nur noch Broadcasts.
|
||
|
||
Javas Switch prüft seine Tabelle einmal je Sekunde ab dem Start
|
||
(`SwitchFirmware.checkSAT`), ein Eintrag verfällt also bis zu einer Sekunde
|
||
nach der Lernzeit; der Kern genau bei ihr. Das ist gewollt und wird nicht
|
||
gemessen: Wo Java nur in einem Takt nachsieht, macht der Kern es genauer
|
||
(Entscheidung 01.10.2026; dasselbe für die NAT-Einträge des Heimrouters).
|
||
|
||
## Tabelle des Mitschnitts
|
||
|
||
Seit F2.8b schreibt `ScenarioNetwork.writeFrames` neben den Frames Javas
|
||
fertige Tabelle jeder Karte (`Lauscher.getDaten`): `rows` mit Adresse:Port
|
||
(das Fenster „Datenaustausch"), `layeredRows` nur mit Ports (die
|
||
Schichtenansicht); je Zeile Nummer, Quelle, Ziel, Protokoll, Schicht,
|
||
Bemerkung, ohne die Uhrzeit. Java läuft dabei mit `Locale.GERMANY`
|
||
(`Information.getLocaleOrDefault`): deutsche Wörter, Tausenderpunkte. Alle
|
||
Laufzeit-Läufe sind dafür neu aufgenommen (bis auf `dhcp-lan`, s. oben);
|
||
`NatRun` hat den Ablauf `ttlOut` (Ping mit TTL 1 aus dem LAN: der Heimrouter
|
||
meldet „Zeit überschritten").
|
||
|
||
Gemessen (01.10.2026), wo der Kern abwich:
|
||
|
||
* Eine ARP-Anfrage trägt als Ziel-MAC `FF:FF:FF:FF:FF:FF`
|
||
(`ARP.sendeARPBroadcast`), der Kern trug Nullen (wie Swift).
|
||
* Eine ICMP-Fehlermeldung trägt Identifier und Sequenznummer 0, der Kern
|
||
kopierte die des Echos (wie Swift).
|
||
* „Host nicht erreichbar" vom Router trägt das Paket mit gesenkter TTL (wie
|
||
er es weitergeleitet hat), der Kern das angekommene.
|
||
|
||
Bewusst nicht übernommen: Javas Antwort an `0.0.0.0` schreibt die
|
||
Broadcast-Adresse klein (`ARP.sendArpReply`), der Kern immer groß; „ICMP
|
||
Time Exeeded" ist ein Tippfehler.
|
||
|
||
RIP: Der Anlauf entscheidet, wie viele Zufallspausen jeder Router zieht
|
||
(jede Nachricht zieht eine), und der hängt an Javas Startreihenfolge. Eine
|
||
Aufnahme, deren Anlauf von dem des Kerns (alle Router zugleich) abweicht,
|
||
liegt danach in allen Zeiten daneben; das Werkzeug wird dann wiederholt.
|
||
|
||
## Beispiele von Filius (Abnahme F2)
|
||
|
||
`ExampleRun` öffnet eine Beispieldatei aus `corpus/java-examples` so, wie
|
||
Filius eine Datei öffnet (`ScenarioNetwork.fromFile`:
|
||
`SzenarioVerwaltung.laden` in die Listen des Fensters), startet sie wie der
|
||
Aktionsmodus (jedes Gerät in der Reihenfolge der Datei), spielt einen Ablauf
|
||
und schreibt nach `packages/fls/test/examples/<ablauf>.java.json` die Frames
|
||
jeder Karte (unter ihrer MAC-Adresse), Javas Tabellen, die Zuordnung der
|
||
Karten (`cards`: MAC → `Gerät/Index`, ein wiederholter Name mit `#2`, `#3`, …),
|
||
die Aufrufe, die Adressen am Ende und jede Änderung der RIP-Tabellen. Abläufe:
|
||
`routing` (internetwork_komplett: Pings über drei Router, dann von einem
|
||
Notebook ohne Gateway), `dhcp` (einfaches_rechnernetz_komplett: vier
|
||
Clients), `dns` (dns_server: rekursiv über Wurzel, `de.`, `filius.de.`),
|
||
`nat` (nat_with_mapped_port: Pings durch zwei Heimrouter), `firewall`
|
||
(Internet_Komplett_…_DE: acht DHCP-Clients, dann Pings durch die Firewall).
|
||
|
||
Gemessen (01.10.2026):
|
||
|
||
* **`ipForwardingEnabled`** eines Rechners: Java speichert und beachtet den
|
||
Schalter (`meine-schule.de` leitet die Pakete der iPads weiter); Leser,
|
||
Modell, Kern und `FlsOracle` kannten ihn nicht. `FlsOracle` schreibt ihn
|
||
jetzt für jeden Knoten mit IP-Stapel; Korpus, Schreiber und Export neu
|
||
geprüft.
|
||
* **ARP-Lernregel** (`ARPThread`): Einen Eintrag lernt ein Gerät nur aus einer
|
||
Anfrage nach seiner eigenen Adresse oder aus einer Antwort, eine Antwort
|
||
gibt es nur auf eine Anfrage mit Ziel-MAC `ff:ff:ff:ff:ff:ff`, und ein
|
||
Paket von der Netzadresse wird verworfen. Der Kern lernte aus jedem Paket.
|
||
* Javas Firewall wendet ihre Regeln nur auf TCP und UDP an; ICMP lässt sie
|
||
durch, außer „ICMP verwerfen" ist an (`checkAcceptIcmp`). Der Kern ebenso.
|
||
* Ein Ping von einem Rechner ohne Gateway an ein fremdes Netz wirft in Java
|
||
eine `NullPointerException`; der Kern meldet „Netz nicht erreichbar".
|
||
* **Gleich lange RIP-Wege**: Hat ein Router zwei gleich lange Wege, nimmt
|
||
Java den, dessen Nachricht seine Threads zuerst verarbeiten; im
|
||
Firewall-Beispiel wechselte das zwischen Läufen (Router #3 zu 42.0.0.0
|
||
über Router `""` oder über #2). Der Ablauf meidet solche Ziele.
|
||
* Gleichzeitig startende DHCP-Clients bedient der Server in der Reihenfolge,
|
||
in der Javas Threads senden; mit acht Clients stauen sich die Broadcasts,
|
||
ein Frame erreicht die Karten bis zu rund 450 ms versetzt, und Javas
|
||
Mitschnitt verliert eigene Frames eines Clients.
|
||
|
||
## Echo-Server und einfacher Client (F3.1)
|
||
|
||
`EchoRun` installiert auf dem Netz von `tcp-lan` Filius' Echo-Server
|
||
(`ServerBaustein`) auf .10, eingeschaltet auf Port 55555, bevor die
|
||
Simulation startet (wie eine Datei ihn lädt), und den einfachen Client
|
||
(`ClientBaustein`) auf .20 und .30. Es macht die Aufrufe wie die Fenster:
|
||
`verbinden` und `trennen` gehen an den Thread des Programms, `senden` läuft
|
||
im Thread des Aufrufers, bis es zurückkehrt. Es schreibt nach
|
||
`echo-java-<ablauf>.java.json` die Frames jeder Karte, Javas Tabellen und
|
||
jede Zeile, die ein Programm in sein Fenster schreibt (seine Beobachter),
|
||
dazu die Aufrufe. Abläufe: `exchange`, `closedPort`, `noRoute`,
|
||
`notConnected`, `serverOff`, `reconnect`, `twoClients`, `otherPort`.
|
||
|
||
Gemessen (01.10.2026):
|
||
|
||
* `Anwendung.starten` meldet seinen Beobachtern `null`; das Fenster
|
||
überspringt es.
|
||
* Der Client merkt die Verbindung erst beim nächsten Blick
|
||
(`TCPSocket.verbinden` schläft 100 ms), der Server ebenso eine neue
|
||
Verbindung (`ServerSocket.oeffnen`) und dass er ausgeschaltet wurde
|
||
(„Server-Socket geschlossen“ rund 20 ms nach dem Ausschalten).
|
||
* Ohne Route zum Ziel stirbt der Socket-Thread des Clients an der
|
||
`RouteNotFoundException`; `verbinden` wartet ewig, das Fenster bleibt
|
||
stumm, kein Frame geht hinaus.
|
||
* Ausgeschaltet schließt der Server seine Verbindungen mit FIN, gibt aber
|
||
zugleich den Port frei (`ServerSocket.schliessen`); das FIN des Clients
|
||
findet keinen Socket mehr und bleibt ohne ACK.
|
||
* Aus- und sofort wieder eingeschaltet (ohne Pause) merkt Javas Schleife das
|
||
Ausschalten nicht und lauscht weiter auf dem alten Port, auch wenn er
|
||
geändert wurde. Wer im Fenster den Port wechselt, lässt Sekunden
|
||
dazwischen; so misst es `otherPort`.
|
||
* Der Client sendet nur ein SYN (wie bei `TcpRun`, `closedPort`).
|
||
|
||
## Webserver und Browser (F3.2a)
|
||
|
||
`WebRun` installiert auf dem Netz von `web-lan` (Browser 192.168.0.20,
|
||
DNS-Server .10, Webserver .30, ein Rechner ohne Webserver .31) Filius'
|
||
Webserver, eingeschaltet auf Port 80, mit den Dateien, die Filius ihm gibt
|
||
(`index.html` und ihr Bild), und eigenen (Seite mit Bildern, Text, Namen mit
|
||
Leerzeichen und Umlaut, virtuelle Hosts in `/www.conf/vhosts`, eine Datei
|
||
außerhalb von `/webserver`). Eine Adresse wird eingetippt wie ins Fenster und
|
||
wie dort zur URL (`erzeugeURL`). Es schreibt die Frames jeder Karte, Javas
|
||
Tabellen, die Fensterzeilen des Servers, was der Browser seinem Fenster
|
||
meldet (Seite mit Status, Inhaltsart und Text, oder „update“ für ein Bild),
|
||
und alle Dateien des Servers am Ende. Abläufe: `page`, `byName`,
|
||
`notFound`, `vhost`, `noServer`, `unresolved`, `post`, `encoded`,
|
||
`traversal`, `caseInsensitive`, `raw` (der einfache Client spricht mit dem
|
||
Server).
|
||
|
||
Gemessen (01.10.2026):
|
||
|
||
* Der Browser verbindet sich immer mit Port 80; die Adresszeile nimmt keinen
|
||
Port (Javas Muster für Hosts). Ein Schrägstrich am Ende fällt weg
|
||
(`String.split`), `/vhost1/` ist dann ein Ordner und 404.
|
||
* Je Seite eine Verbindung: Seite, dann jedes `<img>` nacheinander (das rohe
|
||
`src`, auch `IMG SRC='…'` und ohne Anführungszeichen, relativ ohne
|
||
führenden Schrägstrich), dann schließen. Ein Bild kommt als Javas Base64
|
||
mit Zeilenumbruch nach 76 Zeichen.
|
||
* Pfade gehen kodiert (`UriUtils.encodePath`: `%20`, `%C3%BC`), die Anfrage
|
||
ohne Abfrage (`getPfad`), mit dem Host, wie er eingetippt wurde.
|
||
* `GET /../geheim.txt` liefert `/geheim.txt`: Javas Dateisystem löst `..`
|
||
auf, der Server prüft nicht, ob es unter `/webserver` bleibt.
|
||
* Gesucht wird ohne Rücksicht auf Groß- und Kleinschreibung (Name und Host).
|
||
* „HALLO“ oder „GET /“ ohne Version: `HTTPNachricht` wirft, der Thread des
|
||
Servers schließt die Verbindung und meldet `null`.
|
||
* Beim Wechsel der Seite geht die DNS-Anfrage der neuen Seite vor dem FIN der
|
||
alten: `schliessen()` bittet den Socket-Thread nur.
|
||
* Die Fehlerseiten macht der Browser selbst (im Messaufbau Javas Ersatztext,
|
||
weil die Vorlage fehlt); der Vergleich sieht nur den Status.
|
||
|
||
## Seiten der Router und Heimrouter (F3.2b)
|
||
|
||
`RouterWebRun` lässt den Browser eines Rechners die Seiten eines Routers
|
||
oder Heimrouters holen: auf `router-web` (`arp-routed` mit Firewall auf dem
|
||
Router: ICMP verwerfen, TCP-Port 80 aus 10.0.0.0/24 erlauben) Index,
|
||
`routes`, `log` (nach einem verworfenen Ping und zwei Verbindungen) und
|
||
`konfig`; auf `nat-home` den Index des Heimrouters von innen und von außen
|
||
und `routes` (gibt es dort nicht: 404); auf `rip-chain` nach 40 s die
|
||
RIP-Tabelle (die Router wie bei `RipRun` gesetzt). `ScenarioNetwork` baut
|
||
dafür jetzt auch Firewalls nach dem Szenario-Feld `firewalls`.
|
||
|
||
Gemessen (01.10.2026):
|
||
|
||
* Router und Heimrouter schalten ihren Webserver beim Start ein
|
||
(`setAktiv(true)`, Fensterzeile „Annahme … gestartet“). Ob er lauscht, ist
|
||
ein Wettlauf: `setAktiv` weckt den Thread nur, wenn er schon wartet. In
|
||
einem von drei RIP-Läufen blieb das SYN an den Router unbeantwortet; in den
|
||
NAT-Aufnahmen hat er nie gelauscht (er nahm keine Startnummer).
|
||
* Index-Seiten aus Javas Vorlagen, zeilenweise gelesen und mit `\n`
|
||
wieder zusammengesetzt (auch die Windows-Zeilenenden der deutschen).
|
||
* `log`: ein Eintrag je Regeltreffer bei TCP und UDP und je verworfenem
|
||
ICMP-Paket; die deutsche Seite schließt den Titel mit `</titel>`.
|
||
* `routes` ohne RIP: Javas Weiterleitungstabelle (eigene Adressen, Netze,
|
||
localhost, manuelle Routen, Standardgateway), grün, was direkt ist, blau das
|
||
Standardgateway; mit RIP: Hops, Gültigkeit in ganzen Sekunden, nächster Hop
|
||
und die erste Adresse des meldenden Routers.
|
||
* Java zählt TCP-Sequenznummern und Segmente in Zeichen seiner Strings: Ein
|
||
„ä“ auf der Routen-Seite ist eine Stelle, nicht zwei Bytes.
|
||
|
||
|
||
## Firewall-Einstellungen der Router (F3.2c)
|
||
|
||
`KonfigCases` füttert Javas Seite `konfig` (`FirewallWebKonfig`) direkt mit
|
||
den Schritten aus `packages/core/test/firewall-settings.cases.txt` (eine
|
||
Anfrage ohne Daten oder die Daten eines Formulars) und schreibt je Schritt
|
||
die Seite und die Firewall danach, auf Deutsch und auf Englisch
|
||
(`firewall-settings.{de,en}.java.json`; das Gebietsschema wird vor dem
|
||
ersten Lesen des Nachrichtenbündels gesetzt). Jeder Fall beginnt mit einem
|
||
neuen Router, ohne Regeln, mit drei oder mit der einen Regel, die ein Fall
|
||
anlegt (die Seite, die Java zeigen würde, hätte es sie angenommen).
|
||
`RouterWebRun` spielt dazu auf `router-web` `konfigIcmp` und `konfigBlock`
|
||
über das Netz.
|
||
|
||
Gemessen (01.10.2026):
|
||
|
||
* Javas Browserfenster (Swing, `FormSubmitEvent.getData`) schickt jedes Feld
|
||
des Formulars in seiner Reihenfolge, UTF-8-kodiert, angehakte Kästchen und
|
||
die gewählte Option, dazu nur den gedrückten Knopf.
|
||
* Java prüft die Felder beim Lesen: die Quell-IP gegen die Maske, die noch
|
||
nicht gelesen ist (also leer), die Maske gegen die IP, ebenso. Eine Regel
|
||
mit Quell- oder Zieladresse nimmt die Seite deshalb nie an, in keiner
|
||
Reihenfolge der Felder.
|
||
* Ein Fehler bleibt im Plug-in stehen und erscheint auf jeder späteren Seite,
|
||
auch ohne Daten. Mit zwei Knöpfen zugleich (Speichern und Hinzufügen)
|
||
speichert Java und schreibt die Fehler als nackte Liste.
|
||
* Java dekodiert die Daten, bevor es sie an `&` und `=` zerlegt: Ein
|
||
getipptes `&` zerreißt das Formular. Eine kaputte Kodierung (`%zz`, `%` am
|
||
Ende) wirft im Dekodierer, Regel 0 nach unten wirft
|
||
(`ArrayIndexOutOfBoundsException`), ein `$` im wieder gefüllten Formular
|
||
lässt `replaceAll` scheitern (die Seite ist `null`, also 500).
|
||
* Die Maske prüft `isValidSubnetmask`: vier Zahlen (eine leere am Ende fällt
|
||
bei Javas `split` weg), `parseInt` mit Vorzeichen, die Bits Einsen, dann
|
||
Nullen, mindestens eine Eins (0.0.0.0 ist keine Maske).
|
||
* Über das Netz: „ICMP filtern“ aus, und der nächste Ping kommt durch; eine
|
||
Regel „TCP 80 verwerfen“ über die erlaubende geschoben, und das SYN des
|
||
Browsers bleibt an der Firewall hängen. Javas Router-Server lauschte auch
|
||
hier in einem Lauf nie (`run.sh` wiederholt).
|
||
|
||
## E-Mail-Server (F3.3a)
|
||
|
||
`EmailRun` lässt Javas einfachen Client (`ClientBaustein`) auf `mail-lan`
|
||
rohe SMTP- und POP3-Befehle an einen E-Mail-Server schicken (Domain
|
||
`filius.de`, Konten aus `/mailserver/konten.txt`: anna mit zwei Mails, bob
|
||
ohne). Aufgezeichnet werden die Frames, die Fensterzeilen von Server und
|
||
Client und am Ende die Konten (jede Mail als `Email.toString`, neu,
|
||
gelöscht). `MailCases` füttert Javas `SMTPMitarbeiter` und
|
||
`POP3Mitarbeiter` ohne Netz über einen Socket, der nur mitschreibt, mit den
|
||
Fällen aus `packages/core/test/email-server.cases.txt`, auf Deutsch und auf
|
||
Englisch. Die Netz-Abläufe bleiben bei dem, was Java richtig macht; Javas
|
||
Fehler zeigen die Fälle.
|
||
|
||
Gemessen (01.10.2026):
|
||
|
||
* Der Server startet POP3 (Port 110) vor SMTP (Port 25); POP3s Socket nimmt
|
||
die erste TCP-Startnummer. Die SMTP-Antworten sind übersetzt („220
|
||
Willkommen bei“), die POP3-Antworten englisch. SMTP schreibt seine
|
||
Fensterzeile nach dem Senden, POP3 davor.
|
||
* Eine angenommene Mail speichert Java so, wie es ihren Text neu liest
|
||
(`new Email(email.toString())`): Zeilenenden ohne `\r`, kein Bcc.
|
||
* SMTP-Fehler: Nach einer Mail beantwortet der Server den nächsten Befehl mit
|
||
503 und behält Absender, Empfänger und Text (die zweite Mail ging auch an
|
||
die erste Empfängerin, mit dem ersten Text); unbekannte oder verfrühte
|
||
Befehle bleiben ohne Antwort; ein `quit` im Mailtext beendet die
|
||
Verbindung; die Domain wird genau verglichen (`FILIUS.DE` ist fremd); ein
|
||
leerer Absender mit unbekanntem Empfänger und ein Empfänger ohne `@`
|
||
werfen im Mitarbeiter, die Verbindung endet; eine unbekannte Kopfzeile
|
||
beendet den Kopf (der Betreff wird Text), ohne Leerzeile fehlt die erste
|
||
Zeile; eine Benachrichtigung an eine Mail ohne From hat `To: null`.
|
||
* Die Benachrichtigung über unbekannte Empfänger trägt Javas Datum im
|
||
Gebietsschema der JVM (hier `10/1/26, 6:30 PM`) und die Adresse des
|
||
Servers.
|
||
* POP3 zählt Mails ab 0. Fehler: eine Mail lässt sich nur einmal abrufen;
|
||
`LIST` ohne Mails gibt `-ERR`, `LIST 0` listet alle; vor der Anmeldung
|
||
kommen leere Antworten; eine fehlende Mail kommt als leere `+OK`-Mail,
|
||
`DELE` darauf leer; ein fehlendes oder falsches Argument wirft, die
|
||
Verbindung endet; nach `DELE` nummeriert `LIST` neu, `RETR` nicht.
|
||
* Die Größe in `STAT` und `LIST` ist Javas eigene: die Länge von Text,
|
||
Absender, Betreff, Empfängerliste (`[a, b]`) und Datum.
|