diff --git a/CHANGES.md b/CHANGES.md index a762bf1..1c08722 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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, Firewall) | ü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); 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,7 +37,7 @@ 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, 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); 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; 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 | diff --git a/corpus/corpus.swift.json b/corpus/corpus.swift.json index 2514a61..b4dc99f 100644 --- a/corpus/corpus.swift.json +++ b/corpus/corpus.swift.json @@ -264,7 +264,13 @@ 16 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : true + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -318,7 +324,14 @@ 83 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -404,7 +417,9 @@ 313 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -447,7 +462,13 @@ 25 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -655,7 +676,13 @@ 145 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -698,7 +725,13 @@ 276 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } } ] @@ -1036,7 +1069,10 @@ "hostWireless" : { "isEnabled" : true, "ssid" : "MyWiFi" - } + }, + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -1131,7 +1167,14 @@ 419 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -1174,7 +1217,13 @@ 422 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -1211,7 +1260,10 @@ "hostWireless" : { "isEnabled" : true, "ssid" : "MyWiFi" - } + }, + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -1832,7 +1884,19 @@ 19 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -1918,7 +1982,9 @@ 100 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -1977,7 +2043,15 @@ 235 ], "system" : { - + "installedPrograms" : [ + "webBrowser", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -3404,7 +3478,13 @@ 16 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : true + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -3458,7 +3538,14 @@ 83 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -3544,7 +3631,9 @@ 313 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -3587,7 +3676,13 @@ 25 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -3795,7 +3890,13 @@ 145 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -3838,7 +3939,13 @@ 276 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } } ] @@ -4015,7 +4122,9 @@ 97 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -4363,6 +4472,19 @@ ], "upperBoundIPAddress" : "192.168.0.29", "useOwnSettings" : false + }, + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false } } }, @@ -5325,7 +5447,14 @@ 147 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -5373,7 +5502,10 @@ 266 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -5421,7 +5553,10 @@ 375 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -5469,7 +5604,10 @@ 59 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -5517,7 +5655,10 @@ 161 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -5725,7 +5866,13 @@ 30 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -5773,7 +5920,9 @@ 285 ], "system" : { - + "installedPrograms" : [ + "emailServer" + ] } }, { @@ -5821,7 +5970,9 @@ 414 ], "system" : { - + "installedPrograms" : [ + "emailServer" + ] } } ] @@ -6250,7 +6401,10 @@ 169 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -6463,7 +6617,10 @@ 274 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -6799,7 +6956,13 @@ 606 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -6849,7 +7012,14 @@ 793 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -6897,7 +7067,9 @@ 417 ], "system" : { - + "installedPrograms" : [ + "emailServer" + ] } }, { @@ -6945,7 +7117,9 @@ 684 ], "system" : { - + "installedPrograms" : [ + "emailServer" + ] } }, { @@ -7021,6 +7195,19 @@ ], "upperBoundIPAddress" : "10.50.50.254", "useOwnSettings" : false + }, + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false } } }, @@ -7069,7 +7256,10 @@ 840 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -7195,7 +7385,10 @@ "hostWireless" : { "isEnabled" : true, "ssid" : "MyWiFiNetwork" - } + }, + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -7993,7 +8186,10 @@ 165 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -8489,7 +8685,13 @@ 606 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -8539,7 +8741,14 @@ 777 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -8587,7 +8796,9 @@ 417 ], "system" : { - + "installedPrograms" : [ + "emailServer" + ] } }, { @@ -8635,7 +8846,9 @@ 649 ], "system" : { - + "installedPrograms" : [ + "emailServer" + ] } }, { @@ -8711,6 +8924,19 @@ ], "upperBoundIPAddress" : "10.50.50.254", "useOwnSettings" : false + }, + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false } } }, @@ -8759,7 +8985,10 @@ 826 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -8885,7 +9114,10 @@ "hostWireless" : { "isEnabled" : true, "ssid" : "MyWiFiNetwork" - } + }, + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -10127,7 +10359,13 @@ 19 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -10177,7 +10415,15 @@ 235 ], "system" : { - + "installedPrograms" : [ + "webBrowser", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -10313,7 +10559,14 @@ 366 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -10822,7 +11075,10 @@ 264 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -10884,7 +11140,15 @@ 169 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "emailServer", + "webBrowser" + ] } }, { @@ -10932,7 +11196,10 @@ 120 ], "system" : { - + "installedPrograms" : [ + "emailClient", + "webBrowser" + ] } }, { @@ -11140,7 +11407,13 @@ 49 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -11188,7 +11461,9 @@ 382 ], "system" : { - + "installedPrograms" : [ + "emailServer" + ] } }, { @@ -11236,7 +11511,9 @@ 405 ], "system" : { - + "installedPrograms" : [ + "emailServer" + ] } } ] @@ -11327,7 +11604,14 @@ 46 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -11361,7 +11645,9 @@ 182 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -11595,10 +11881,16 @@ 175 ], "system" : { + "gnutella" : { + "maximumKnownPeers" : 3 + }, "hostWireless" : { "isEnabled" : true, "ssid" : "Filius Wi Fi" - } + }, + "installedPrograms" : [ + "gnutella" + ] } }, { @@ -12629,7 +12921,13 @@ 119 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -12663,7 +12961,9 @@ 54 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -12713,7 +13013,15 @@ 202 ], "system" : { - + "installedPrograms" : [ + "webBrowser", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -12763,7 +13071,14 @@ 307 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -12813,7 +13128,14 @@ 363 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -13263,7 +13585,15 @@ 135 ], "system" : { - + "installedPrograms" : [ + "webBrowser", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -13297,7 +13627,9 @@ 148 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -13852,7 +14184,9 @@ 100 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -13902,7 +14236,15 @@ 235 ], "system" : { - + "installedPrograms" : [ + "webBrowser", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -14136,6 +14478,19 @@ ], "upperBoundIPAddress" : "141.99.2.250", "useOwnSettings" : false + }, + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false } } } @@ -14614,7 +14969,9 @@ 100 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -14664,7 +15021,9 @@ 227 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -14898,6 +15257,19 @@ ], "upperBoundIPAddress" : "42.0.0.254", "useOwnSettings" : false + }, + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false } } }, @@ -14993,7 +15365,14 @@ 255 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -15036,7 +15415,13 @@ 72 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -15421,7 +15806,10 @@ "hostWireless" : { "isEnabled" : true, "ssid" : "MyWiFi" - } + }, + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -15516,7 +15904,14 @@ 419 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -15559,7 +15954,13 @@ 422 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer" + ] } }, { @@ -15596,7 +15997,10 @@ "hostWireless" : { "isEnabled" : true, "ssid" : "MyWiFi" - } + }, + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -15866,7 +16270,14 @@ 93 ], "system" : { - + "installedPrograms" : [ + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -15905,7 +16316,9 @@ 151 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -16524,7 +16937,19 @@ 19 ], "system" : { - + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -16610,7 +17035,9 @@ 100 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -16669,7 +17096,15 @@ 235 ], "system" : { - + "installedPrograms" : [ + "webBrowser", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false + } } }, { @@ -16882,7 +17317,9 @@ 97 ], "system" : { - + "installedPrograms" : [ + "webBrowser" + ] } }, { @@ -17230,6 +17667,19 @@ ], "upperBoundIPAddress" : "192.168.0.29", "useOwnSettings" : false + }, + "dnsServer" : { + "forwardingServerIPAddress" : null, + "recursiveResolutionEnabled" : false + }, + "installedPrograms" : [ + "dnsServer", + "webServer" + ], + "webServer" : { + "documentRoot" : "\/www", + "port" : 80, + "virtualHosts" : false } } }, diff --git a/packages/core/src/runtime/config.ts b/packages/core/src/runtime/config.ts index 7546e46..4f774d4 100644 --- a/packages/core/src/runtime/config.ts +++ b/packages/core/src/runtime/config.ts @@ -149,6 +149,56 @@ export interface RemoteLinkConfiguration { export const DEFAULT_REMOTE_LINK_LATENCY_MILLISECONDS = 20; +// ── Installed programs (Java installierteAnwendungen) ────────────────────── + +/** Swift's TopologyRuntimeInstallableProgram. */ +export type InstallableProgram = + | 'commandPrompt' + | 'fileExplorer' + | 'imageViewer' + | 'textEditor' + | 'webServer' + | 'webBrowser' + | 'echoServer' + | 'simpleClient' + | 'dnsServer' + | 'dhcpServer' + | 'firewall' + | 'emailClient' + | 'emailServer' + | 'gnutella'; + +export interface WebServerConfiguration { + readonly port: number; + /** Where the served files are: Java keeps them in /webserver, Swift moves them to /www. */ + readonly documentRoot: string; +} + +export const DEFAULT_WEB_SERVER_PORT = 80; + +export interface GnutellaConfiguration { + readonly maximumKnownPeers: number; +} + +/** The DNS records follow with the hosts file (Java's /dns/hosts). */ +export interface DnsServerConfiguration { + readonly recursiveResolutionEnabled: boolean; + readonly forwardingServerIPAddress?: string; +} + +/** + * Java's echo server (ServerBaustein) and generic client (ClientBaustein) + * keep their port and target in the file; Swift asks for them at run time. + */ +export interface EchoServerConfiguration { + readonly port: number; +} + +export interface SimpleClientConfiguration { + readonly targetAddress: string; + readonly targetPort: number; +} + // ── Firewall (Java filius.software.firewall.Firewall, FirewallRule) ───────── export const FIREWALL_PROTOCOL = { all: -1, icmp: 1, tcp: 6, udp: 17 } as const; diff --git a/packages/fls/src/importProject.ts b/packages/fls/src/importProject.ts index b76c5b9..36097a5 100644 --- a/packages/fls/src/importProject.ts +++ b/packages/fls/src/importProject.ts @@ -27,7 +27,13 @@ // - a PC or notebook whose file has no file system gets an empty one, as in // Java — Swift gives it the demo files of a new device; // - a file system's root is Java's root, whatever its user object says. -// Not here yet: applications (with them the web server's /webserver → /www). +// - every installed application of FILIUS is kept: Swift drops the command +// prompt, text editor, file explorer, image viewer, echo server and +// generic client, and their settings (port, target) have a place here; +// - 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. import { canonicalMacAddress, @@ -35,6 +41,7 @@ import { defaultSwitchConfiguration, dhcpServerConfiguration, DEFAULT_REMOTE_LINK_LATENCY_MILLISECONDS, + DEFAULT_WEB_SERVER_PORT, deterministicDocumentationUuid, documentationColor, documentationItem, @@ -54,6 +61,12 @@ import { type DeviceConfiguration, type DhcpClientConfiguration, type DhcpServerConfiguration, + type DnsServerConfiguration, + type EchoServerConfiguration, + type GnutellaConfiguration, + type InstallableProgram, + type SimpleClientConfiguration, + type WebServerConfiguration, type GatewayPortForwardingRow, type RemoteLinkConfiguration, type FirewallAction, @@ -75,7 +88,7 @@ import { type TopologyNode, type VirtualFileContent, } from '@mdg-filius/core'; -import type { FlsFileTree, FlsFirewall } from './systemSoftware.js'; +import type { FlsApplicationClass, FlsFileTree, FlsFirewall } from './systemSoftware.js'; import type { FlsDocumentationItem, FlsInterface, FlsNode, FlsNodeClass, FlsTopology } from './topology.js'; export interface ImportedProject { @@ -96,6 +109,14 @@ export interface ImportedProject { remoteLinkConfigurations: Map; /** Every PC and notebook. */ virtualFileSystems: Map; + installedPrograms: Map>; + /** Server programs Java had switched on (aktiv). */ + activePrograms: Map>; + webServerConfigurations: Map; + gnutellaConfigurations: Map; + dnsServerConfigurations: Map; + echoServerConfigurations: Map; + simpleClientConfigurations: Map; /** In render order: rectangles below text, then by order. */ documentationItems: DocumentationItem[]; /** The topology's warnings, then this step's. */ @@ -132,6 +153,13 @@ export function importProject(topology: FlsTopology, newId: () => Id): ImportedP portForwardingRowsByNodeId: new Map(), remoteLinkConfigurations: new Map(), virtualFileSystems: new Map(), + installedPrograms: new Map(), + activePrograms: new Map(), + webServerConfigurations: new Map(), + gnutellaConfigurations: new Map(), + dnsServerConfigurations: new Map(), + echoServerConfigurations: new Map(), + simpleClientConfigurations: new Map(), }; const nodes = topology.nodes.map((source) => { const node = importNode(source, newId, warnings); @@ -140,6 +168,7 @@ export function importProject(topology: FlsTopology, newId: () => Id): ImportedP registerServiceConfigurations(source, node, system, newId, warnings); if (node.kind === 'pc' || node.kind === 'notebook') { system.virtualFileSystems.set(node.id, importFileSystem(source.system.fileSystem, node.displayName, warnings)); + registerPrograms(source, node, system, warnings); } return node; }); @@ -331,8 +360,91 @@ type SystemConfigurations = Pick< | 'portForwardingRowsByNodeId' | 'remoteLinkConfigurations' | 'virtualFileSystems' + | 'installedPrograms' + | 'activePrograms' + | 'webServerConfigurations' + | 'gnutellaConfigurations' + | 'dnsServerConfigurations' + | 'echoServerConfigurations' + | 'simpleClientConfigurations' >; +/** Java's application classes as Swift's programs. */ +export const PROGRAM_OF_CLASS: Readonly> = { + 'filius.software.clientserver.ClientBaustein': 'simpleClient', + 'filius.software.clientserver.ServerBaustein': 'echoServer', + 'filius.software.dateiaustausch.PeerToPeerAnwendung': 'gnutella', + 'filius.software.dns.DNSServer': 'dnsServer', + 'filius.software.email.EmailAnwendung': 'emailClient', + 'filius.software.email.EmailServer': 'emailServer', + 'filius.software.firewall.Firewall': 'firewall', + 'filius.software.lokal.FileExplorer': 'fileExplorer', + 'filius.software.lokal.ImageViewer': 'imageViewer', + 'filius.software.lokal.Terminal': 'commandPrompt', + 'filius.software.lokal.TextEditor': 'textEditor', + 'filius.software.www.WebBrowser': 'webBrowser', + 'filius.software.www.WebServer': 'webServer', +}; + +/** The web server's document root: where Java keeps its files. */ +export const JAVA_WEB_ROOT = '/webserver'; + +/** The program parts of Swift's finalizeCurrentNodeCandidate, for a PC or notebook. */ +function registerPrograms(source: FlsNode, node: TopologyNode, system: SystemConfigurations, warnings: string[]): void { + const installed = new Set(); + const active = new Set(); + for (const application of source.system.applications ?? []) { + const program = PROGRAM_OF_CLASS[application.javaClass]; + installed.add(program); + if (application.isActive === true) active.add(program); + switch (program) { + case 'webServer': + system.webServerConfigurations.set(node.id, { + port: application.port ?? DEFAULT_WEB_SERVER_PORT, + documentRoot: JAVA_WEB_ROOT, + }); + break; + case 'gnutella': + // Without one in the file Java rolls 3, 4 or 5; Swift's default is 3. + system.gnutellaConfigurations.set(node.id, { maximumKnownPeers: application.maxPeers ?? 3 }); + break; + case 'dnsServer': { + // Java's DNSServer forwards to the host's own DNS server setting; + // only one that differs from the host's own address is a forwarder. + const recursive = application.recursiveResolutionEnabled === true; + const forwarder = trimmedOrEmpty(source.interfaces[0]?.dns); + const primary = trimmedOrEmpty(source.interfaces[0]?.ip); + const forwarding = recursive && forwarder !== '' && forwarder !== primary; + system.dnsServerConfigurations.set( + node.id, + forwarding + ? { recursiveResolutionEnabled: recursive, forwardingServerIPAddress: forwarder } + : { recursiveResolutionEnabled: recursive }, + ); + break; + } + case 'firewall': + if (application.firewall !== undefined) { + system.firewallConfigurationsByNodeId.set(node.id, firewallConfigurationOf(application.firewall, `node '${node.displayName}'`, warnings)); + } + break; + case 'echoServer': + system.echoServerConfigurations.set(node.id, { port: application.port ?? 55_555 }); + break; + case 'simpleClient': + system.simpleClientConfigurations.set(node.id, { + targetAddress: trimmedOrEmpty(application.targetAddress), + targetPort: application.targetPort ?? 55_555, + }); + break; + default: + break; + } + } + if (installed.size > 0) system.installedPrograms.set(node.id, installed); + if (active.size > 0) system.activePrograms.set(node.id, active); +} + /** A row of text cells, trimmed, or undefined unless it has exactly four. */ function fourCells(row: (string | null)[]): [string, string, string, string] | undefined { if (row.length !== 4 || row.some((cell) => cell === null)) return undefined; diff --git a/packages/fls/test/importProject.test.ts b/packages/fls/test/importProject.test.ts index ebdf25f..f79bdde 100644 --- a/packages/fls/test/importProject.test.ts +++ b/packages/fls/test/importProject.test.ts @@ -28,6 +28,7 @@ import { importProject, readFlsArchive, readTopology, + type FlsApplication, type FlsFileTree, type FlsNode, type FlsTopology, @@ -91,6 +92,9 @@ function fileEntriesOf(fileSystem: VirtualFileSystem | undefined): unknown { const DEFAULT_FILES = describeFileEntries(VirtualFileSystem.defaultForDevice().allEntries()); const EMPTY_FILES = describeFileEntries(new VirtualFileSystem().allEntries()); +/** Programs Swift's import drops and this one keeps (Java has them installed). */ +const PROGRAMS_SWIFT_DROPS = new Set(['commandPrompt', 'fileExplorer', 'imageViewer', 'textEditor', 'echoServer', 'simpleClient']); + /** * describeSystem's shape. A firewall as the core has it without one * (inactive, drop, no rules) is left out: Swift keeps a configuration only @@ -126,6 +130,23 @@ function systemOf(project: ImportedProject, nodeId: Id) { if (forwarding !== undefined) { result.portForwarding = forwarding.map((row) => [row.protocolValue, row.publicPortValue, row.lanIPAddress, row.lanPortValue]); } + const programs = project.installedPrograms.get(nodeId); + const kept = [...(programs ?? [])].filter((program) => !PROGRAMS_SWIFT_DROPS.has(program)).sort(); + if (kept.length > 0) result.installedPrograms = kept; + const web = project.webServerConfigurations.get(nodeId); + if (web !== undefined) { + // Swift serves the same files from /www after moving them there. + result.webServer = { port: web.port, documentRoot: web.documentRoot === '/webserver' ? '/www' : web.documentRoot, virtualHosts: false }; + } + const gnutella = project.gnutellaConfigurations.get(nodeId); + if (gnutella !== undefined) result.gnutella = gnutella; + const dns = project.dnsServerConfigurations.get(nodeId); + if (dns !== undefined) { + result.dnsServer = { + recursiveResolutionEnabled: dns.recursiveResolutionEnabled, + forwardingServerIPAddress: dns.forwardingServerIPAddress ?? null, + }; + } const remoteLink = project.remoteLinkConfigurations.get(nodeId); if (remoteLink !== undefined) result.remoteLink = remoteLink; const firewall = project.firewallConfigurationsByNodeId.get(nodeId); @@ -217,23 +238,27 @@ function withJavaDefaultNames(state: unknown): unknown { /** * Where the Swift import is wrong and Java (corpus.java.json) is right, per - * file and node: the corrected fields. + * file and node: the corrected node. */ -const SWIFT_ERRORS: Record> = { - // useIPAsName is ``, the shared result of a - // button's isEnabled() — true. Swift's reader cannot follow it: it shows - // the label text as a manual name instead of the IP address. +type SwiftNode = { displayName: string; hostLabelPolicy: string; system: Record }; +const shownByIp = (displayName: string) => (node: SwiftNode) => ({ ...node, displayName, hostLabelPolicy: 'ipAddress' }); +const SWIFT_ERRORS: Record SwiftNode>> = { 'java-examples/multicast_server.fls': { - 0: { displayName: 'Rechner', hostLabelPolicy: 'ipAddress' }, - 1: { displayName: 'Notebook', hostLabelPolicy: 'ipAddress' }, - 2: { displayName: 'Notebook', hostLabelPolicy: 'ipAddress' }, + // useIPAsName is ``, the shared result of a + // button's isEnabled() — true. Swift's reader cannot follow it: it shows + // the label text as a manual name instead of the IP address. + 0: shownByIp('Rechner'), + // maxTeilnehmerZahl is ``, the shared result of + // a button's getIconTextGap() — 4. Swift falls back to its default 3. + 1: (node) => ({ ...shownByIp('Notebook')(node), system: { ...node.system, gnutella: { maximumKnownPeers: 4 } } }), + 2: shownByIp('Notebook'), }, }; function withSwiftErrorsCorrected(name: string, state: unknown): unknown { const corrections = SWIFT_ERRORS[name] ?? {}; - const { nodes, ...rest } = state as { nodes: object[] }; - return { ...rest, nodes: nodes.map((node, index) => ({ ...node, ...corrections[index] })) }; + const { nodes, ...rest } = state as { nodes: SwiftNode[] }; + return { ...rest, nodes: nodes.map((node, index) => corrections[index]?.(node) ?? node) }; } /** @@ -716,6 +741,58 @@ describe('importProject rules', () => { expect(() => VirtualFileSystem.validateProjectQuotas(devices)).toThrow('totalSizeQuotaExceeded(100663296, 67108864)'); }); + it("keeps every installed program with its settings, and forwards DNS only to another server", () => { + const source = flsNode('Rechner').source; + const application = (javaClass: FlsApplication['javaClass'], fields: Partial = {}): FlsApplication => ({ + key: javaClass, + javaClass, + source, + ...fields, + }); + const host = (dns: string, recursive: boolean) => + flsNode('Rechner', { + interfaces: [{ ip: '10.0.0.1', mask: '255.255.255.0', mac: undefined, gateway: '', dns }], + system: { applications: [application('filius.software.dns.DNSServer', { isActive: true, port: 53, recursiveResolutionEnabled: recursive })] }, + }); + const project = importProject( + topology({ + nodes: [ + flsNode('Notebook', { + interfaces: [nic('10.0.0.2')], + system: { + applications: [ + application('filius.software.lokal.Terminal'), + application('filius.software.www.WebServer', { isActive: true, port: 8080, useVirtualHosts: false }), + application('filius.software.clientserver.ServerBaustein', { isActive: false, port: 7 }), + application('filius.software.clientserver.ClientBaustein', { targetAddress: ' 10.0.0.9 ', targetPort: 7 }), + application('filius.software.firewall.Firewall', { + firewall: { activated: true, defaultPolicy: 1, dropICMP: false, filterSYNSegmentsOnly: true, filterUdp: true, rules: [] }, + }), + ], + }, + }), + host('8.8.8.8', true), + host('10.0.0.1', true), + host('8.8.8.8', false), + ], + }), + counter(), + ); + const [notebook, forwarding, self, iterative] = project.graph.nodes.map((node) => node.id); + + expect([...project.installedPrograms.get(notebook!)!].sort()).toEqual(['commandPrompt', 'echoServer', 'firewall', 'simpleClient', 'webServer']); + expect([...project.activePrograms.get(notebook!)!]).toEqual(['webServer']); + expect(project.webServerConfigurations.get(notebook!)).toEqual({ port: 8080, documentRoot: '/webserver' }); + expect(project.echoServerConfigurations.get(notebook!)).toEqual({ port: 7 }); + 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 }, + ]); + }); + it('leaves out a cable to itself and one to a port that already has a cable', () => { const project = importProject( topology({ diff --git a/tools/swift-oracle/Sources/Oracle/main.swift b/tools/swift-oracle/Sources/Oracle/main.swift index 6990de3..2c88b13 100644 --- a/tools/swift-oracle/Sources/Oracle/main.swift +++ b/tools/swift-oracle/Sources/Oracle/main.swift @@ -384,6 +384,25 @@ func describeSystem(_ state: TopologyEditorState, _ node: TopologyNode) -> JSON if let rows = state.runtimePortForwardingRowsByNodeID[node.id] { json["portForwarding"] = rows.map { [$0.protocolValue, $0.publicPortValue, $0.lanIPAddress, $0.lanPortValue] } } + if let programs = state.runtimeInstalledProgramsByNodeID[node.id] { + json["installedPrograms"] = programs.map(\.rawValue).sorted() + } + if let web = state.runtimeWebServerConfigurationsByNodeID[node.id] { + json["webServer"] = [ + "port": web.port, + "documentRoot": web.documentRoot, + "virtualHosts": web.virtualHostConfiguration != nil, + ] as JSON + } + if let gnutella = state.runtimeGnutellaConfigurationsByNodeID[node.id] { + json["gnutella"] = ["maximumKnownPeers": gnutella.maximumKnownPeers] as JSON + } + if let dns = state.runtimeDNSServerConfigurationsByNodeID[node.id] { + json["dnsServer"] = [ + "recursiveResolutionEnabled": dns.recursiveResolutionEnabled, + "forwardingServerIPAddress": dns.forwardingServerIPAddress ?? NSNull(), + ] as JSON + } if let link = state.remoteLinkConfigurationsByNodeID[node.id] { json["remoteLink"] = [ "pairIdentifier": link.pairIdentifier,