feat: F4.6g3 a computer's firewall window, measured against Java's with traffic from two networks
The firewall-pc scenario and FirewallWindowRun use Java's window as a person does and send a ping, a TCP connection and a DNS question from the server's network and from behind the router after each step. The core adds and removes port rules (personalFirewall.ts) and writes both into the firewall's log, which now carries each line's time; the room takes the window's actions and sends its view and the log as lines; the editor shows Java's window with its two tabs. Not adopted from Java: any integer as a port, a port after a space refused without a word, a log one can type into. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1527,3 +1527,41 @@ Gemessen (03.10.2026):
|
||||
* Eine leere Maildomain wird genommen (`. MX 3600 x.`).
|
||||
* „Auswahl entfernen“ entfernt den ersten Eintrag des Namens, bei zwei
|
||||
gleichen Namen also womöglich den anderen.
|
||||
|
||||
### Firewall eines Rechners (F4.6g3)
|
||||
|
||||
`FirewallWindowRun` installiert auf dem Server von `firewall-pc` (zwei Netze
|
||||
hinter einem Router, der Server mit DNS- und Echo-Server, je ein Rechner im
|
||||
eigenen und im fremden Netz) Javas Firewall, öffnet ihr
|
||||
`GUIApplicationFirewallWindow` und benutzt es wie eine Person: die drei
|
||||
Kästchen, Port mit Protokoll- und Absenderliste und „Hinzufügen“ (auch mit
|
||||
dem, was keine Zahl oder kein Port ist), eine Zeile gewählt und „Löschen“.
|
||||
Je Schritt: die Kästchen (und ob sie sich ändern lassen), das Portfeld, die
|
||||
Tabelle, das Protokoll (und ob man hineintippen kann); nach den Schritten,
|
||||
die danach fragen, was vom eigenen und vom fremden Rechner beim Server
|
||||
ankommt: ein Ping, eine TCP-Verbindung zum Echo-Server, eine DNS-Frage
|
||||
(`packages/core/test/firewallWindow.java.json`). Ein Durchlauf dauert etwa
|
||||
sieben Minuten (Javas Zeitüberschreitungen).
|
||||
|
||||
Gemessen (03.10.2026):
|
||||
|
||||
* Frisch installiert ist die Firewall aktiv, ICMP erlaubt, der UDP-Filter
|
||||
an: Ein Ping geht durch, TCP und DNS nicht (Standardaktion verwerfen).
|
||||
* Eine Regel für alle Absender lässt beide Rechner durch, „Absender im
|
||||
gleichen Rechnernetz“ nur den eigenen. Jede greifende Regel steht im
|
||||
Protokoll („Firewall-Regel hat gegriffen: #1 (…) -> akzeptieren“), auch
|
||||
„Portregel wurde hinzugefügt: 55555/TCP“ und „Portregel wurde entfernt.“
|
||||
* Ohne ICMP-Haken wird ein Ping verworfen und protokolliert, ohne
|
||||
UDP-Filter geht DNS ohne Regel durch (ohne Zeile). Ist die Firewall aus,
|
||||
lassen sich die beiden Kästchen nicht ändern, und alles geht durch.
|
||||
* Javas Ungenauigkeiten:
|
||||
* Jede ganze Zahl ist ein Port: −1 ergibt eine Regel für jeden Port
|
||||
beider Protokolle (die Tabelle zeigt keinen Port), 0 und 70000 Regeln,
|
||||
die nie greifen. Eine Zahl nach einem Leerzeichen lehnt Java still ab.
|
||||
* Ins Protokoll kann man tippen.
|
||||
* Den Erklärtext (`firewall_msg2`) baut Javas Fenster, zeigt ihn aber nie.
|
||||
* Fallen der Sonde: Javas `ClientBaustein` hängt nach dem Trennen noch im
|
||||
Empfangen, sein nächstes „Verbinden“ läuft dahinter nie — die Sonde nimmt
|
||||
je Verbindung einen eigenen `TCPSocket`. Javas Echo-Server verträgt keine
|
||||
sofort wieder geschlossene Verbindung (die folgenden laufen in die
|
||||
Zeitüberschreitung) — die Sonde hält sie eine halbe Sekunde.
|
||||
|
||||
@@ -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" "$here/src/DnsRun.java" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java" "$here/src/NewDeviceRun.java" "$here/src/SheetRun.java" "$here/src/TerminalWindowRun.java" "$here/src/InstallRun.java" "$here/src/BrowserWindowRun.java" "$here/src/MailWindowRun.java" "$here/src/SaveAfterRunRun.java" "$here/src/TextEditorWindowRun.java" "$here/src/FileExplorerWindowRun.java" "$here/src/EchoWindowRun.java" "$here/src/DnsWindowRun.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" "$here/src/ArpRun.java" "$here/src/RipRun.java" "$here/src/NatRun.java" "$here/src/WlanRun.java" "$here/src/ExampleRun.java" "$here/src/EchoRun.java" "$here/src/WebRun.java" "$here/src/RouterWebRun.java" "$here/src/KonfigCases.java" "$here/src/EmailRun.java" "$here/src/MailCases.java" "$here/src/EmailClientRun.java" "$here/src/TerminalCases.java" "$here/src/TerminalRun.java" "$here/src/FileCases.java" "$here/src/GnutellaRun.java" "$here/src/NewDeviceRun.java" "$here/src/SheetRun.java" "$here/src/TerminalWindowRun.java" "$here/src/InstallRun.java" "$here/src/BrowserWindowRun.java" "$here/src/MailWindowRun.java" "$here/src/SaveAfterRunRun.java" "$here/src/TextEditorWindowRun.java" "$here/src/FileExplorerWindowRun.java" "$here/src/EchoWindowRun.java" "$here/src/DnsWindowRun.java" "$here/src/FirewallWindowRun.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.
|
||||
@@ -442,6 +442,17 @@ HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -
|
||||
"$root/packages/core/test/scenarios/mail-lan.scenario.json" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at ' || true
|
||||
test -s "$output"
|
||||
|
||||
# FILIUS' firewall window of a computer used as a person does — the three
|
||||
# boxes, the port field with its lists and "Hinzufügen" (also what is no
|
||||
# number or no port), "Löschen" — and after it a ping, a TCP connection and a
|
||||
# DNS question from the server's network and from behind the router
|
||||
# (FirewallWindowRun): the firewall's window (packages/core/test/firewallWindow.test.ts).
|
||||
output="$root/packages/core/test/firewallWindow.java.json"
|
||||
rm -f "$output"
|
||||
HOME="$here/.build/home" xvfb-run -a java -Duser.language=de -Duser.country=DE -cp "$classpath:$here/.build" FirewallWindowRun \
|
||||
"$root/packages/core/test/scenarios/firewall-pc.scenario.json" "$output" 2>&1 | grep -v 'logback\|^$\|^\s*at ' || true
|
||||
test -s "$output"
|
||||
|
||||
# FILIUS' own examples as Java FILIUS runs them, opened and started as FILIUS
|
||||
# does (ExampleRun): the acceptance of F2 (packages/fls/test/examplesJava.test.ts)
|
||||
# and the classics of F3 (web, email, gnutella; packages/fls/test/classicsJava.test.ts).
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
// Uses FILIUS' own firewall window of a computer (GUIApplicationFirewallWindow
|
||||
// on a GUIDesktopPanel) on firewall-pc's server as a person does: the three
|
||||
// boxes, the port field with its two lists and "Hinzufügen" (also with what
|
||||
// is no number, and numbers no port is), a row chosen and "Löschen". After
|
||||
// each step it writes what the window shows (the boxes and whether they can
|
||||
// be changed, the port field, the table, the log and whether it can be typed
|
||||
// into) and, where traffic is asked, what reaches the server from a computer
|
||||
// of its network and one behind the router: a ping, a TCP connection to its
|
||||
// echo server (a socket of the client's own), its DNS server asked for a name. The reference for the room's
|
||||
// firewall window (packages/core/test/firewallWindow.test.ts).
|
||||
//
|
||||
// Usage: java -cp filius.jar:lib/*:classes FirewallWindowRun <scenario.json> <output json>
|
||||
|
||||
import filius.gui.anwendungssicht.GUIApplicationFirewallWindow;
|
||||
import filius.gui.anwendungssicht.GUIDesktopPanel;
|
||||
import filius.software.clientserver.ServerBaustein;
|
||||
import filius.software.system.Betriebssystem;
|
||||
import filius.software.system.InternetKnotenBetriebssystem;
|
||||
import filius.software.transportschicht.TCPSocket;
|
||||
import filius.software.vermittlungsschicht.IcmpPaket;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.table.TableModel;
|
||||
|
||||
public class FirewallWindowRun {
|
||||
static final String LOCAL = "00000000-0000-0000-0000-000000000201";
|
||||
static final String REMOTE = "00000000-0000-0000-0000-000000000203";
|
||||
static final String SERVER = "00000000-0000-0000-0000-000000000204";
|
||||
static final String SERVER_ADDRESS = "10.0.0.10";
|
||||
static final String SERVER_CLASS = "filius.software.clientserver.ServerBaustein";
|
||||
|
||||
static final List<Object> steps = new ArrayList<>();
|
||||
static GUIApplicationFirewallWindow window;
|
||||
static ScenarioNetwork network;
|
||||
static int sequence = 1;
|
||||
|
||||
static Object field(Object object, String name) throws Exception {
|
||||
Field field = GUIApplicationFirewallWindow.class.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
return field.get(object);
|
||||
}
|
||||
|
||||
static void onEdt(Runnable action) throws Exception {
|
||||
SwingUtilities.invokeLater(action);
|
||||
Thread.sleep(500);
|
||||
}
|
||||
|
||||
static Map<String, Object> box(String name) throws Exception {
|
||||
JCheckBox box = (JCheckBox) field(window, name);
|
||||
Map<String, Object> shown = new LinkedHashMap<>();
|
||||
shown.put("selected", box.isSelected());
|
||||
shown.put("enabled", box.isEnabled());
|
||||
return shown;
|
||||
}
|
||||
|
||||
static List<List<String>> rows(JTable table) {
|
||||
List<List<String>> rows = new ArrayList<>();
|
||||
TableModel model = table.getModel();
|
||||
for (int row = 0; row < model.getRowCount(); row++) {
|
||||
List<String> cells = new ArrayList<>();
|
||||
for (int column = 0; column < model.getColumnCount(); column++) cells.add(String.valueOf(model.getValueAt(row, column)));
|
||||
rows.add(cells);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
static InternetKnotenBetriebssystem system(String id) {
|
||||
return (InternetKnotenBetriebssystem) network.nodes.get(id).getSystemSoftware();
|
||||
}
|
||||
|
||||
static String ping(String from) {
|
||||
IcmpPaket answer = system(from).holeICMP().sendProbe(SERVER_ADDRESS, 64, 1, sequence++);
|
||||
return answer == null ? "null" : "ICMP " + answer.getIcmpType() + "/" + answer.getIcmpCode() + " from " + answer.getSender();
|
||||
}
|
||||
|
||||
/** A TCP connection to the server's echo server, as its client opens one (a socket of its own, closed again): "connected" or Java's exception. */
|
||||
static String connect(String from) throws Exception {
|
||||
AtomicReference<String> result = new AtomicReference<>("(no answer)");
|
||||
Thread thread = new Thread(() -> {
|
||||
try {
|
||||
TCPSocket socket = new TCPSocket(system(from), SERVER_ADDRESS, 55555);
|
||||
socket.verbinden();
|
||||
result.set("connected");
|
||||
// Held a moment, as a person's client would: Java's echo server takes no connection closed at once (the next ones then time out).
|
||||
Thread.sleep(500);
|
||||
socket.schliessen();
|
||||
} catch (Exception error) {
|
||||
result.set(error.getClass().getSimpleName() + ": " + error.getMessage());
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
thread.join(60000);
|
||||
Thread.sleep(1000);
|
||||
return result.get();
|
||||
}
|
||||
|
||||
static String resolve(String from) throws Exception {
|
||||
AtomicReference<String> result = new AtomicReference<>("(no answer)");
|
||||
Thread thread = new Thread(() -> {
|
||||
try {
|
||||
result.set(String.valueOf(system(from).holeDNSClient().holeIPAdresse("www.filius.de")));
|
||||
} catch (Exception error) {
|
||||
result.set(error.getClass().getSimpleName());
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
thread.join(30000);
|
||||
return result.get();
|
||||
}
|
||||
|
||||
static void step(String name, boolean traffic) throws Exception {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("step", name);
|
||||
if (traffic) {
|
||||
Map<String, Object> reached = new LinkedHashMap<>();
|
||||
for (String[] each : new String[][] { { "local", LOCAL }, { "remote", REMOTE } }) {
|
||||
Map<String, Object> from = new LinkedHashMap<>();
|
||||
from.put("ping", ping(each[1]));
|
||||
from.put("tcp", connect(each[1]));
|
||||
from.put("dns", resolve(each[1]));
|
||||
reached.put(each[0], from);
|
||||
}
|
||||
result.put("traffic", reached);
|
||||
}
|
||||
result.put("active", box("cbEinAus"));
|
||||
result.put("icmp", box("cbIcmp"));
|
||||
result.put("udp", box("cbUdp"));
|
||||
result.put("port", ((JTextField) field(window, "tfPort")).getText());
|
||||
result.put("protocol", String.valueOf(((JComboBox<?>) field(window, "cbProtocol")).getSelectedItem()));
|
||||
result.put("sender", String.valueOf(((JComboBox<?>) field(window, "cbAlleAbsender")).getSelectedItem()));
|
||||
result.put("rows", rows((JTable) field(window, "tTabellePort")));
|
||||
JTextArea log = (JTextArea) field(window, "log");
|
||||
result.put("log", log.getText());
|
||||
result.put("logEditable", log.isEditable());
|
||||
steps.add(result);
|
||||
}
|
||||
|
||||
static void press(String text) throws Exception {
|
||||
List<JButton> found = new ArrayList<>();
|
||||
collect(window, text, found);
|
||||
JButton button = found.get(0);
|
||||
onEdt(button::doClick);
|
||||
}
|
||||
|
||||
static void collect(Container container, String text, List<JButton> found) {
|
||||
for (Component component : container.getComponents()) {
|
||||
if (component instanceof JButton && text.equals(((JButton) component).getText())) found.add((JButton) component);
|
||||
if (component instanceof Container) collect((Container) component, text, found);
|
||||
}
|
||||
}
|
||||
|
||||
static void click(String box) throws Exception {
|
||||
JCheckBox each = (JCheckBox) field(window, box);
|
||||
onEdt(each::doClick);
|
||||
}
|
||||
|
||||
/** The port typed, the protocol (0 TCP, 1 UDP, 2 *) and the sender (0 all, 1 the same network) chosen, "Hinzufügen". */
|
||||
static void add(String port, int protocol, int sender) throws Exception {
|
||||
JTextField field = (JTextField) field(window, "tfPort");
|
||||
JComboBox<?> protocols = (JComboBox<?>) field(window, "cbProtocol");
|
||||
JComboBox<?> senders = (JComboBox<?>) field(window, "cbAlleAbsender");
|
||||
onEdt(() -> {
|
||||
field.setText(port);
|
||||
protocols.setSelectedIndex(protocol);
|
||||
senders.setSelectedIndex(sender);
|
||||
});
|
||||
press("Hinzufügen");
|
||||
}
|
||||
|
||||
static void choose(int row) throws Exception {
|
||||
JTable table = (JTable) field(window, "tTabellePort");
|
||||
onEdt(() -> table.setRowSelectionInterval(row, row));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
try {
|
||||
run(args);
|
||||
} catch (Throwable error) {
|
||||
error.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
static void run(String[] args) throws Exception {
|
||||
network = ScenarioNetwork.read(args[0]);
|
||||
Betriebssystem server = (Betriebssystem) network.nodes.get(SERVER).getSystemSoftware();
|
||||
server.installApp("filius.software.firewall.Firewall", false);
|
||||
server.installApp(SERVER_CLASS, false);
|
||||
ServerBaustein echo = (ServerBaustein) server.holeSoftware(SERVER_CLASS);
|
||||
echo.setPort(55555);
|
||||
echo.setAktiv(true);
|
||||
network.start();
|
||||
Thread.sleep(500);
|
||||
|
||||
JFrame frame = new JFrame();
|
||||
GUIDesktopPanel desktop = new GUIDesktopPanel(server);
|
||||
frame.getContentPane().add(desktop);
|
||||
window = (GUIApplicationFirewallWindow) desktop.starteAnwendung("filius.software.firewall.Firewall");
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
});
|
||||
Thread.sleep(500);
|
||||
step("open", true);
|
||||
|
||||
// What Java's field does not take: nothing happens.
|
||||
add("abc", 0, 0);
|
||||
step("add port abc", false);
|
||||
add("", 0, 0);
|
||||
step("add an empty port", false);
|
||||
|
||||
add("55555", 0, 0);
|
||||
step("add 55555 TCP for all senders", true);
|
||||
add("53", 1, 1);
|
||||
step("add 53 UDP for the same network", true);
|
||||
|
||||
click("cbIcmp");
|
||||
step("ICMP no longer allowed", true);
|
||||
click("cbUdp");
|
||||
step("UDP filter off", true);
|
||||
|
||||
// Numbers that are no port.
|
||||
add("-1", 2, 0);
|
||||
step("add -1 for all protocols", false);
|
||||
add("0", 0, 0);
|
||||
step("add 0 TCP", false);
|
||||
add("70000", 0, 1);
|
||||
step("add 70000 TCP for the same network", false);
|
||||
add(" 80", 0, 0);
|
||||
step("add a port after a space", false);
|
||||
|
||||
// "Löschen" with no row chosen, then the first row.
|
||||
press("Löschen");
|
||||
step("delete with no row chosen", false);
|
||||
choose(0);
|
||||
press("Löschen");
|
||||
step("delete row 0", true);
|
||||
|
||||
// Off: the boxes cannot be changed; on again.
|
||||
click("cbEinAus");
|
||||
step("firewall off", true);
|
||||
click("cbEinAus");
|
||||
step("firewall on again", false);
|
||||
|
||||
Map<String, Object> output = new LinkedHashMap<>();
|
||||
output.put("steps", steps);
|
||||
try (FileOutputStream out = new FileOutputStream(args[1])) {
|
||||
out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
network.stop();
|
||||
System.out.println("firewall window steps: " + steps.size());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user