Files
mdg-filius/tools/java-oracle/src/SheetRun.java
T
julian.kiedaischandClaude Opus 5.5 6e97e3565d feat(editor): F4.5e a router's connections in the device sheet
SheetRun now clicks through Java's JConnectionsDialog with a router, a
computer and a switch cabled in the GUIContainer: a click marks a card,
the same click unmarks it, another card swaps the cables or moves the
one; "+" adds 192.168.0.10/24 up to eight cards, "-" removes the last
card with its cable down to none, and a router without a card keeps no
gateway.

The document adds, removes and swaps a router's cards (the same cables,
the same devices), Java's click logic is one function for the test and
the page, and the sheet names the address at a cable's far end. The
page "Verbindungen verwalten" shows Java's RJ45 buttons in Java's
colours with each card's far end below. Not adopted: the last card
stays. A card tab or a mark whose card is undone away falls back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 13:12:27 +00:00

743 lines
40 KiB
Java

// Types into FILIUS' own settings panels (JHostKonfiguration,
// JSwitchKonfiguration, JModemKonfiguration, JVermittlungsrechnerKonfiguration,
// JGatewayConfiguration) and lets them take the text as
// they do when a field loses the focus (aenderungenAnnehmen), then writes,
// for each case, whether the panel marked the text red while typing, what the
// device holds afterwards and what the field shows then (updateAttribute):
// the reference for the editor's device sheet (packages/doc/test/sheet.test.ts).
// Each case starts from a new device ("start": the field's value then). The cases file is ASCII, with
// Java-style unicode escapes (backslash, u, four hex digits) for anything
// else, one case a line:
//
// <host|switch|modem|router|homeRouter> <field> <text> the text may be empty
//
// Fields: host name, ip, mask, gateway, dns; switch name, ssid, retention;
// modem name, port; router name, ip, mask (its first interface), gateway;
// homeRouter name, gateway, wanIp, wanMask, lanIp, lanMask. A field without
// a check method of its own is marked as its key listener marks it (the
// pattern). After the cases, a few flows ("flows"): a host's name while it
// shows its address or MAC address, DHCP switched on while the device's own
// DHCP server runs; then a router's forwarding table as Java shows it
// (Weiterleitungstabelle.holeTabelle, "tables") and its rows edited in the
// panel's table (JWeiterleitungsTabelle, "edits"). The DHCP dialog
// (JDHCPKonfiguration) takes its fields with its button (speichern):
//
// <dhcpHost|dhcpHome> <lower|upper|gateway|dns> <text>
//
// a host's (gateway and DNS with its own settings on) or a home router's;
// then what the dialog shows without own settings, the static assignments
// ("dhcp") and a home router's port forwarding as its dialog keeps it
// (JPortForwardingDialog, "forwarding"). Last, a router's firewall dialog
// (JFirewallDialog, "firewall"): a new rule, a cell of a rule edited in its
// table as the table takes it (GatewayFirewallConfigTable.closeEditor), the
// rules moved and removed with the dialog's buttons. Then a router's
// connections dialog (JConnectionsDialog, "connections"): its cards and what
// their cables join, cables swapped with two clicks, cards added up to the
// last and removed down to none, and the router's panel and gateway with one
// card and with none.
//
// Usage: java -cp filius.jar:lib/*:classes SheetRun <cases txt> <output json>
import filius.gui.GUIContainer;
import filius.gui.JMainFrame;
import filius.gui.netzwerksicht.GUIDesignSidebar;
import filius.gui.netzwerksicht.GUIKabelItem;
import filius.gui.netzwerksicht.GUIKnotenItem;
import filius.gui.netzwerksicht.JConnectionsDialog;
import filius.gui.netzwerksicht.JDHCPKonfiguration;
import filius.gui.netzwerksicht.JSidebarButton;
import filius.gui.netzwerksicht.JFirewallDialog;
import filius.gui.netzwerksicht.JKonfiguration;
import filius.gui.netzwerksicht.JPortForwardingDialog;
import filius.gui.netzwerksicht.JWeiterleitungsTabelle;
import filius.hardware.Kabel;
import filius.hardware.NetzwerkInterface;
import filius.hardware.Port;
import filius.hardware.knoten.Knoten;
import filius.hardware.knoten.Gateway;
import filius.hardware.knoten.Host;
import filius.hardware.knoten.Modem;
import filius.hardware.knoten.Rechner;
import filius.hardware.knoten.Switch;
import filius.hardware.knoten.Vermittlungsrechner;
import filius.rahmenprogramm.EingabenUeberpruefung;
import filius.software.system.Betriebssystem;
import filius.software.system.GatewayFirmware;
import filius.software.system.InternetKnotenBetriebssystem;
import filius.software.system.ModemFirmware;
import filius.software.system.SwitchFirmware;
import filius.software.system.VermittlungsrechnerBetriebssystem;
import filius.software.dhcp.DHCPAddressAssignment;
import filius.software.firewall.Firewall;
import filius.software.firewall.FirewallRule;
import filius.software.dhcp.DHCPServer;
import filius.software.vermittlungsschicht.Weiterleitungstabelle;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import java.awt.Color;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import java.util.LinkedList;
import javax.swing.JTextField;
public class SheetRun {
/** A private field of a panel; "name#i" is the i-th of an array. */
static Object get(Object panel, String name) throws Exception {
String[] parts = name.split("#");
Field field = panel.getClass().getDeclaredField(parts[0]);
field.setAccessible(true);
Object value = field.get(panel);
return parts.length == 1 ? value : ((Object[]) value)[Integer.parseInt(parts[1])];
}
static void call(Object panel, String name) throws Exception {
Method method = panel.getClass().getDeclaredMethod(name);
method.setAccessible(true);
method.invoke(panel);
}
/** The panel's field, its check (the method its key listener calls) or else the pattern its key listener checks, per device and field. */
static final Map<String, String[]> FIELDS = new LinkedHashMap<>();
static {
FIELDS.put("host name", new String[] { "name", null, null });
FIELDS.put("host ip", new String[] { "ipAdresse", "checkIpAddress", null });
FIELDS.put("host mask", new String[] { "netzmaske", "checkNetmask", null });
FIELDS.put("host gateway", new String[] { "gateway", "checkGatewayAddress", null });
FIELDS.put("host dns", new String[] { "dns", "checkDnsAddress", null });
FIELDS.put("switch name", new String[] { "name", null, null });
FIELDS.put("switch ssid", new String[] { "ssid", "checkSSID", null });
FIELDS.put("switch retention", new String[] { "retentionTime", "checkRetentionTime", null });
FIELDS.put("modem name", new String[] { "name", null, null });
FIELDS.put("modem port", new String[] { "tfPort", null, null });
FIELDS.put("router name", new String[] { "name", null, null });
FIELDS.put("router ip", new String[] { "ipAdressen#0", null, "ip" });
FIELDS.put("router mask", new String[] { "netzmasken#0", null, "mask" });
FIELDS.put("router gateway", new String[] { "gateway", null, "ip" });
FIELDS.put("homeRouter name", new String[] { "name", null, null });
FIELDS.put("homeRouter gateway", new String[] { "gatewayName", null, "ip" });
FIELDS.put("homeRouter wanIp", new String[] { "ipAddressWANPort", null, "ip" });
FIELDS.put("homeRouter wanMask", new String[] { "netmaskWANPort", null, "mask" });
FIELDS.put("homeRouter lanIp", new String[] { "ipAddressLANPort", null, "ip" });
FIELDS.put("homeRouter lanMask", new String[] { "netmaskLANPort", null, "mask" });
}
static Pattern pattern(String name) {
return name.equals("ip") ? EingabenUeberpruefung.musterIpAdresse : EingabenUeberpruefung.musterSubNetz;
}
static String stored(Object device, String key) {
if (device instanceof Host) {
Host host = (Host) device;
Betriebssystem os = (Betriebssystem) host.getSystemSoftware();
switch (key) {
case "host name": return host.getName();
case "host ip": return os.primaryIPAdresse();
case "host mask": return os.primarySubnetMask();
case "host gateway": return os.getStandardGateway();
case "host dns": return os.getDNSServer();
}
} else if (device instanceof Switch) {
Switch sw = (Switch) device;
SwitchFirmware firmware = (SwitchFirmware) sw.getSystemSoftware();
switch (key) {
case "switch name": return sw.getName();
case "switch ssid": return firmware.getSSID();
case "switch retention": return Long.toString(firmware.getRetentionTime());
}
} else if (device instanceof Vermittlungsrechner) {
Vermittlungsrechner router = (Vermittlungsrechner) device;
NetzwerkInterface first = router.getNetzwerkInterfaces().get(0);
switch (key) {
case "router name": return router.getName();
case "router ip": return first.getIp();
case "router mask": return first.getSubnetzMaske();
case "router gateway": return ((InternetKnotenBetriebssystem) router.getSystemSoftware()).getStandardGateway();
}
} else if (device instanceof Gateway) {
Gateway home = (Gateway) device;
switch (key) {
case "homeRouter name": return home.getName();
case "homeRouter gateway": return ((InternetKnotenBetriebssystem) home.getSystemSoftware()).getStandardGateway();
case "homeRouter wanIp": return home.holeWANInterface().getIp();
case "homeRouter wanMask": return home.holeWANInterface().getSubnetzMaske();
case "homeRouter lanIp": return home.holeLANInterface().getIp();
case "homeRouter lanMask": return home.holeLANInterface().getSubnetzMaske();
}
} else if (device instanceof Modem) {
Modem modem = (Modem) device;
switch (key) {
case "modem name": return modem.getName();
case "modem port": return Integer.toString(((ModemFirmware) modem.getSystemSoftware()).getPort());
}
}
throw new IllegalArgumentException(key);
}
/** A new device; a host with a gateway and a DNS server, a switch with a known SSID, so that what a case leaves alone shows. */
static Object device(String kind) {
switch (kind) {
case "host": {
Rechner host = new Rechner();
Betriebssystem os = (Betriebssystem) host.getSystemSoftware();
os.setStandardGateway("192.168.0.254");
os.setDNSServer("192.168.0.253");
return host;
}
case "switch": {
Switch sw = new Switch();
((SwitchFirmware) sw.getSystemSoftware()).setSSID("Start");
return sw;
}
case "modem": return new Modem();
case "router": {
Vermittlungsrechner router = new Vermittlungsrechner();
router.setzeAnzahlAnschluesse(2);
((InternetKnotenBetriebssystem) router.getSystemSoftware()).setStandardGateway("192.168.0.254");
return router;
}
case "homeRouter": {
Gateway home = new Gateway();
((InternetKnotenBetriebssystem) home.getSystemSoftware()).setStandardGateway("192.168.0.254");
return home;
}
}
throw new IllegalArgumentException(kind);
}
/** A router's forwarding table (Weiterleitungstabelle.holeTabelle) with its manual flags. */
static List<Object> table(String[] interfaces, String gateway, String[][] manual) {
Vermittlungsrechner router = new Vermittlungsrechner();
router.setzeAnzahlAnschluesse(interfaces.length / 2);
for (int i = 0; i < interfaces.length / 2; i++) {
router.getNetzwerkInterfaces().get(i).setIp(interfaces[2 * i]);
router.getNetzwerkInterfaces().get(i).setSubnetzMaske(interfaces[2 * i + 1]);
}
VermittlungsrechnerBetriebssystem os = (VermittlungsrechnerBetriebssystem) router.getSystemSoftware();
os.setStandardGateway(gateway);
Weiterleitungstabelle table = os.getWeiterleitungstabelle();
for (String[] row : manual) table.addManuellenEintrag(row[0], row[1], row[2], row[3]);
List<Object> rows = new ArrayList<>();
List<String[]> entries = table.holeTabelle();
List<Boolean> flags = table.holeManuelleEintraegeFlags();
for (int i = 0; i < entries.size(); i++) {
Map<String, Object> row = new LinkedHashMap<>();
row.put("cells", Arrays.asList(entries.get(i)));
row.put("manual", flags.get(i));
rows.add(row);
}
return rows;
}
/**
* Steps in the panel's table of a router with one manual row, only the
* manual rows shown: {"<column>", text} sets a cell of the first row,
* {"N<column>", text} of the last row, {"new"} adds a row, {"remove"}
* removes the first; each step taken as the table takes it. The manual
* rows afterwards (and the rows shown, when asked).
*/
static Map<String, Object> edit(String[][] steps, boolean shown) throws Exception {
Vermittlungsrechner router = new Vermittlungsrechner();
router.setzeAnzahlAnschluesse(2);
VermittlungsrechnerBetriebssystem os = (VermittlungsrechnerBetriebssystem) router.getSystemSoftware();
os.getWeiterleitungstabelle().addManuellenEintrag("172.16.0.0", "255.255.0.0", "192.168.0.2", "192.168.0.10");
JKonfiguration panel = JKonfiguration.getInstance(router);
JWeiterleitungsTabelle table = (JWeiterleitungsTabelle) get(panel, "weiterleitungstabelle");
table.setzeAlleEintraegeAnzeigen(false);
table.updateAttribute();
for (String[] step : steps) {
if (step[0].equals("new")) {
table.neuerEintrag();
} else if (step[0].equals("remove")) {
table.setRowSelectionInterval(0, 0);
table.markiertenEintragLoeschen();
} else {
boolean last = step[0].startsWith("N");
int column = Integer.parseInt(last ? step[0].substring(1) : step[0]);
table.setValueAt(step[1], last ? table.getRowCount() - 1 : 0, column);
table.aenderungenAnnehmen();
}
}
Map<String, Object> result = new LinkedHashMap<>();
List<Object> rows = new ArrayList<>();
for (String[] row : os.getWeiterleitungstabelle().getManuelleTabelle()) rows.add(Arrays.asList(row));
result.put("manual", rows);
if (shown) result.put("rowsShown", table.getRowCount());
return result;
}
/** A device whose DHCP server the dialog edits: a host 192.168.0.10/24 with gateway and DNS, or a home router; the server with known settings. */
static InternetKnotenBetriebssystem dhcpDevice(String kind) {
InternetKnotenBetriebssystem os;
if (kind.equals("dhcpHost")) {
Rechner host = new Rechner();
os = (InternetKnotenBetriebssystem) host.getSystemSoftware();
os.setStandardGateway("192.168.0.254");
os.setDNSServer("192.168.0.253");
} else {
os = (InternetKnotenBetriebssystem) new Gateway().getSystemSoftware();
}
DHCPServer server = os.getDHCPServer();
server.setUntergrenze("192.168.0.100");
server.setObergrenze("192.168.0.200");
server.setGatewayip("10.0.0.1");
server.setDnsserverip("10.0.0.2");
return os;
}
static JDHCPKonfiguration dhcpDialog(InternetKnotenBetriebssystem os) {
return new JDHCPKonfiguration(JMainFrame.getJMainFrame(), "DHCP", os);
}
static String dhcpValue(DHCPServer server, String field) {
switch (field) {
case "lower": return server.getUntergrenze();
case "upper": return server.getObergrenze();
case "gateway": return server.getGatewayip();
case "dns": return server.getDnsserverip();
}
throw new IllegalArgumentException(field);
}
/** A field of the DHCP dialog typed and taken with its button. */
static Map<String, Object> dhcpCase(String kind, String field, String text) throws Exception {
InternetKnotenBetriebssystem os = dhcpDevice(kind);
DHCPServer server = os.getDHCPServer();
String start = dhcpValue(server, field);
JDHCPKonfiguration dialog = dhcpDialog(os);
if (kind.equals("dhcpHost")) ((JCheckBox) get(dialog, "cbUseInternal")).setSelected(true);
String name = field.equals("lower") ? "tfUntergrenze" : field.equals("upper") ? "tfObergrenze" : field.equals("gateway") ? "tfGateway" : "tfDNSServer";
JTextField input = (JTextField) get(dialog, name);
input.setText(text);
boolean marked = !dialog.ueberpruefen(EingabenUeberpruefung.musterIpAdresse, input);
call(dialog, "speichern");
dialog.dispose();
Map<String, Object> result = new LinkedHashMap<>();
result.put("device", kind);
result.put("field", field);
result.put("input", text);
result.put("start", start);
result.put("marked", marked);
result.put("stored", dhcpValue(server, field));
result.put("shown", dhcpValue(server, field));
return result;
}
/** What the DHCP dialog shows without own settings, and its static assignments taken with its button. */
static Map<String, Object> dhcpFlows() throws Exception {
Map<String, Object> flows = new LinkedHashMap<>();
for (String kind : new String[] { "dhcpHost", "dhcpHome" }) {
InternetKnotenBetriebssystem os = dhcpDevice(kind);
JDHCPKonfiguration dialog = dhcpDialog(os);
Map<String, Object> shown = new LinkedHashMap<>();
for (String name : new String[] { "tfUntergrenze", "tfObergrenze", "tfNetzmaske", "tfGateway", "tfDNSServer" }) {
JTextField input = (JTextField) get(dialog, name);
shown.put(name, Arrays.asList(input.getText(), input.isEditable()));
}
dialog.dispose();
flows.put(kind + "Shown", shown);
}
{
Rechner host = new Rechner();
InternetKnotenBetriebssystem os = (InternetKnotenBetriebssystem) host.getSystemSoftware();
JDHCPKonfiguration dialog = dhcpDialog(os);
flows.put("hostWithoutGatewayShown", Arrays.asList(((JTextField) get(dialog, "tfGateway")).getText(), ((JTextField) get(dialog, "tfDNSServer")).getText()));
dialog.dispose();
}
// Static assignments: rows of the dialog's table, taken with its button.
String[][] rows = { { "aa:bb:cc:dd:ee:01", "192.168.0.50" }, { "AA:BB:CC:DD:EE:01", "192.168.0.51" }, { "AA:BB:CC:DD:EE:_F", "192.168.0.52" }, { "AA:BB:CC:DD:EE:02", "192.168.0.256" },
{ "AA:BB:CC:DD:EE:03", "010.0.0.1" }, { "AABBCCDDEE04", "192.168.0.54" } };
InternetKnotenBetriebssystem os = dhcpDevice("dhcpHost");
JDHCPKonfiguration dialog = dhcpDialog(os);
DefaultTableModel model = (DefaultTableModel) ((JTable) get(dialog, "staticAddressTable")).getModel();
for (String[] row : rows) model.addRow(new Object[] { row[0], row[1] });
call(dialog, "speichern");
dialog.dispose();
List<Object> kept = new ArrayList<>();
for (DHCPAddressAssignment entry : os.getDHCPServer().holeStaticAssignedAddresses()) kept.add(Arrays.asList(entry.getMAC(), entry.getIp()));
flows.put("staticAssignments", kept);
// The add button's own check, per row: MAC and address patterns.
List<Object> addable = new ArrayList<>();
for (String[] row : rows) addable.add(EingabenUeberpruefung.isGueltig(row[0], EingabenUeberpruefung.musterMacAddress) && EingabenUeberpruefung.isGueltig(row[1], EingabenUeberpruefung.musterIpAdresse));
flows.put("addable", addable);
return flows;
}
/** A home router's port forwarding rows as its dialog keeps them, and which of them its firmware would forward (musterProtocol, musterPort, musterIpAdresse). */
static Map<String, Object> forwardingFlows() throws Exception {
Gateway home = new Gateway();
GatewayFirmware firmware = (GatewayFirmware) home.getSystemSoftware();
JPortForwardingDialog dialog = new JPortForwardingDialog(firmware.getStaticNAT(), JMainFrame.getJMainFrame());
dialog.createStaticTable();
String[][] rows = { { "TCP", "80", "192.168.0.20", "8080" }, { "UDP", "0", "192.168.0.21", "53" }, { "TCP", "abc", "192.168.0.22", "80" }, { "tcp", "443", "192.168.0.300", "443" },
{ "17", "5353", "192.168.0.23", "05353" } };
for (String[] row : rows) {
dialog.neuerEintrag();
JTable table = (JTable) get(dialog, "staticTable");
int last = table.getRowCount() - 1;
for (int column = 0; column < 4; column++) table.setValueAt(row[column], last, column);
}
dialog.aenderungenAnnehmen();
dialog.dispose();
Map<String, Object> flows = new LinkedHashMap<>();
List<Object> kept = new ArrayList<>();
List<Object> valid = new ArrayList<>();
for (String[] row : firmware.getStaticNAT()) {
kept.add(Arrays.asList(row));
valid.add(EingabenUeberpruefung.isGueltig(row[0], EingabenUeberpruefung.musterProtocol) && EingabenUeberpruefung.isGueltig(row[1], EingabenUeberpruefung.musterPort)
&& EingabenUeberpruefung.isGueltig(row[2], EingabenUeberpruefung.musterIpAdresse) && EingabenUeberpruefung.isGueltig(row[3], EingabenUeberpruefung.musterPort));
}
flows.put("rows", kept);
flows.put("valid", valid);
return flows;
}
static Map<String, Object> rule(FirewallRule rule) {
Map<String, Object> fields = new LinkedHashMap<>();
fields.put("srcIP", rule.srcIP);
fields.put("srcMask", rule.srcMask);
fields.put("destIP", rule.destIP);
fields.put("destMask", rule.destMask);
fields.put("port", rule.port);
fields.put("protocol", (int) rule.protocol);
fields.put("action", (int) rule.action);
return fields;
}
static Firewall routerFirewall() {
Vermittlungsrechner router = new Vermittlungsrechner();
router.setzeAnzahlAnschluesse(2);
return ((VermittlungsrechnerBetriebssystem) router.getSystemSoftware()).holeFirewall();
}
/** A cell of the dialog's only rule, typed into the table's editor and taken as the table takes it ("#i": the i-th choice of a list). */
static Map<String, Object> firewallCell(int column, String text) throws Exception {
Firewall firewall = routerFirewall();
firewall.addRule();
JFirewallDialog dialog = new JFirewallDialog(firewall, JMainFrame.getJMainFrame());
JTable table = (JTable) get(dialog, "ruleTable");
table.editCellAt(0, column);
java.awt.Component editor = table.getEditorComponent();
if (editor instanceof JTextField) ((JTextField) editor).setText(text);
else if (text.startsWith("#")) ((javax.swing.JComboBox<?>) editor).setSelectedIndex(Integer.parseInt(text.substring(1)));
else ((javax.swing.JComboBox<?>) editor).setSelectedItem(text);
// As Swing ends an edit: the editor stops, the table hears it (editingStopped → closeEditor).
table.getCellEditor().stopCellEditing();
Map<String, Object> result = new LinkedHashMap<>();
result.put("column", column);
result.put("input", text);
FirewallRule rule = firewall.getRuleset().get(0);
result.put("rule", rule(rule));
result.put("valid", rule.validateExistingAttributes());
List<Object> shown = new ArrayList<>();
for (int i = 1; i < 8; i++) shown.add(table.getValueAt(0, i));
result.put("shown", shown);
dialog.dispose();
return result;
}
static Map<String, Object> firewallFlows() throws Exception {
Map<String, Object> flows = new LinkedHashMap<>();
{
Firewall firewall = routerFirewall();
JFirewallDialog dialog = new JFirewallDialog(firewall, JMainFrame.getJMainFrame());
firewall.addRule();
dialog.updateRuleTable();
flows.put("newRule", rule(firewall.getRuleset().get(0)));
flows.put("newRuleShown", dialog.ruleAsVector(1, firewall.getRuleset().get(0)));
flows.put("newFirewall", Arrays.asList(firewall.isActivated(), (int) firewall.getDefaultPolicy(), firewall.getDropICMP(), firewall.getFilterSYNSegmentsOnly()));
dialog.dispose();
}
String[][] cells = { { "1", "10.0.0.0" }, { "1", "abc" }, { "1", ".0.0.1" }, { "1", "999.999.999.999" }, { "1", "010.0.0.0" }, { "1", " " }, { "2", "255.255.0.0" }, { "2", "255.0.255.0" },
{ "2", "0.0.0.0" }, { "2", "255.255.255.255" }, { "2", "0255.255.0.0" }, { "3", "192.168.0.1" }, { "3", "999.999.999.999" }, { "4", "255.255.255.0" }, { "6", "80" }, { "6", "abc" },
{ "6", "0" }, { "6", "080" }, { "6", "" }, { "6", "65536" }, { "5", "UDP" }, { "5", "*" }, { "7", "#0" } };
List<Object> edits = new ArrayList<>();
for (String[] cell : cells) edits.add(firewallCell(Integer.parseInt(cell[0]), cell[1]));
flows.put("cells", edits);
// Moving with the ids the dialog passes (1 for the first row), and removing.
Firewall firewall = routerFirewall();
for (String source : new String[] { "1.1.1.1", "2.2.2.2", "3.3.3.3" }) firewall.addRule(new FirewallRule(source, "", "", "", FirewallRule.ALL_PORTS, FirewallRule.TCP, FirewallRule.DROP));
List<Object> moves = new ArrayList<>();
Object[][] steps = { { "up", 1 }, { "up", 3 }, { "down", 3 }, { "down", 1 }, { "delete", 2 } };
for (Object[] step : steps) {
int id = (Integer) step[1];
Object answer = null;
if (step[0].equals("up")) answer = firewall.moveUp(id);
else if (step[0].equals("down")) answer = firewall.moveDown(id);
else firewall.deleteRule(id - 1);
List<Object> order = new ArrayList<>();
for (FirewallRule each : firewall.getRuleset()) order.add(each.srcIP);
moves.add(Arrays.asList(step[0], id, answer, order));
}
flows.put("moves", moves);
return flows;
}
/** A device on the design surface, as GUIContainer keeps it (NewDeviceRun.add). */
static GUIKnotenItem item(Knoten device, String icon) {
JSidebarButton label = new JSidebarButton(new ImageIcon(SheetRun.class.getResource("/" + icon + ".png")), device.holeHardwareTyp());
label.initTextAndUpdateLocation(device.holeAnzeigeName());
GUIKnotenItem item = new GUIKnotenItem();
item.setKnoten(device);
item.setImageLabel(label);
GUIContainer.getGUIContainer().getKnotenItems().add(item);
return item;
}
/** A cable as GUIEvents.createConnection plugs it, from the first device's port to the second's. */
static void cable(GUIKnotenItem one, Port first, GUIKnotenItem two, Port second) {
GUIKabelItem item = new GUIKabelItem();
item.getKabelpanel().setZiel1(one);
item.getKabelpanel().setZiel2(two);
item.setDasKabel(new Kabel(first, second));
GUIContainer.getGUIContainer().getCableItems().add(item);
}
/** What the dialog shows and what the router's cards are joined to. */
static Map<String, Object> connectionsState(JConnectionsDialog dialog, Vermittlungsrechner router) throws Exception {
Map<String, Object> state = new LinkedHashMap<>();
List<Object> local = new ArrayList<>();
List<Object> remote = new ArrayList<>();
List<Object> colours = new ArrayList<>();
for (int i = 0; i < 8; i++) {
local.add(((JLabel) get(dialog, "lblLocal#" + i)).getText());
remote.add(((JLabel) get(dialog, "lblRemote#" + i)).getText());
JButton button = (JButton) get(dialog, "btnLocal#" + i);
Color colour = button.getBackground();
colours.add(!button.isEnabled() ? "off" : colour.equals(Color.YELLOW) ? "marked" : colour.equals(Color.GREEN) ? "plugged" : colour.equals(Color.RED) ? "free" : "other");
}
state.put("local", local);
state.put("remote", remote);
state.put("buttons", colours);
state.put("add", ((JButton) get(dialog, "btnAddInterface")).isEnabled());
state.put("remove", ((JButton) get(dialog, "btnRemoveInterface")).isEnabled());
// Each card: its address, mask, and the device its cable joins ("" for none).
List<Object> cards = new ArrayList<>();
for (NetzwerkInterface nic : router.getNetzwerkInterfaces()) {
String joined = "";
if (nic.getPort().getVerbindung() != null) {
for (Port port : nic.getPort().getVerbindung().getAnschluesse()) {
if (port != nic.getPort()) {
for (GUIKnotenItem each : GUIContainer.getGUIContainer().getKnotenItems()) {
Knoten node = each.getKnoten();
boolean owns = node instanceof Switch ? ((Switch) node).getAnschluesse().contains(port)
: node instanceof filius.hardware.knoten.InternetKnoten && ((filius.hardware.knoten.InternetKnoten) node).getNetzwerkInterfaces().stream().anyMatch(other -> other.getPort() == port);
if (owns) joined = node.holeAnzeigeName();
}
}
}
}
cards.add(Arrays.asList(nic.getIp(), nic.getSubnetzMaske(), joined));
}
state.put("cards", cards);
state.put("cables", GUIContainer.getGUIContainer().getCableItems().size());
return state;
}
static Map<String, Object> connectionsFlows() throws Exception {
GUIContainer.getGUIContainer().getKnotenItems().clear();
GUIContainer.getGUIContainer().getCableItems().clear();
Vermittlungsrechner router = new Vermittlungsrechner();
router.setzeAnzahlAnschluesse(3);
router.setName("R");
String[] addresses = { "10.0.0.1", "10.1.0.1", "10.2.0.1" };
for (int i = 0; i < 3; i++) router.getNetzwerkInterfaces().get(i).setIp(addresses[i]);
Rechner host = new Rechner();
host.setName("A");
host.getNetzwerkInterfaces().get(0).setIp("10.0.0.2");
Switch sw = new Switch();
sw.setName("S");
GUIKnotenItem routerItem = item(router, GUIDesignSidebar.VERMITTLUNGSRECHNER);
GUIKnotenItem hostItem = item(host, GUIDesignSidebar.RECHNER);
GUIKnotenItem switchItem = item(sw, GUIDesignSidebar.SWITCH);
// The host plugs into the router (the host first), the router into the switch.
cable(hostItem, host.getNetzwerkInterfaces().get(0).getPort(), routerItem, router.getNetzwerkInterfaces().get(0).getPort());
cable(routerItem, router.getNetzwerkInterfaces().get(1).getPort(), switchItem, sw.holeFreienPort());
JConnectionsDialog dialog = new JConnectionsDialog(JMainFrame.getJMainFrame(), router);
List<Object> steps = new ArrayList<>();
// A click on a card's button ("c<i>"), "+" and "-".
String[] clicks = { "start", "c0", "c0", "c0", "c2", "c2", "c1", "c1", "c1", "c0", "+", "c2", "c3", "-", "-", "+", "c0", "c2", "c0", "c1", "+", "+", "+", "+", "+", "+", "-", "-", "-", "-", "-", "-", "-", "-" };
for (String click : clicks) {
if (click.startsWith("c")) ((JButton) get(dialog, "btnLocal#" + click.substring(1))).doClick();
else if (click.equals("+")) ((JButton) get(dialog, "btnAddInterface")).doClick();
else if (click.equals("-")) ((JButton) get(dialog, "btnRemoveInterface")).doClick();
Map<String, Object> step = new LinkedHashMap<>();
step.put("click", click);
step.putAll(connectionsState(dialog, router));
// The router's panel with this many cards: does it still draw?
if (router.getNetzwerkInterfaces().size() <= 1) {
String error = null;
try {
JKonfiguration panel = JKonfiguration.getInstance(router);
panel.updateAttribute();
} catch (RuntimeException thrown) {
error = thrown.getClass().getSimpleName();
}
step.put("panelError", error);
// Java keeps the gateway in the cards: does a router without one keep it?
VermittlungsrechnerBetriebssystem os = (VermittlungsrechnerBetriebssystem) router.getSystemSoftware();
os.setStandardGateway("10.0.0.254");
step.put("gateway", os.getStandardGateway());
os.setStandardGateway("");
}
steps.add(step);
}
// New cards: their MAC addresses differ from each other and from the first cards'.
List<String> macs = new ArrayList<>();
for (NetzwerkInterface nic : router.getNetzwerkInterfaces()) macs.add(nic.getMac());
Map<String, Object> flows = new LinkedHashMap<>();
flows.put("steps", steps);
flows.put("hostPortFree", host.getNetzwerkInterfaces().get(0).getPort().getVerbindung() == null);
flows.put("switchPortsFree", sw.getAnschluesse().stream().allMatch(port -> port.getVerbindung() == null));
dialog.dispose();
return flows;
}
public static void main(String[] args) throws Exception {
// FILIUS' window keeps the JVM alive: end it on any failure too.
try {
run(args);
} catch (Throwable error) {
error.printStackTrace();
System.exit(1);
}
System.exit(0);
}
static void run(String[] args) throws Exception {
GUIContainer.getGUIContainer();
List<Object> cases = new ArrayList<>();
for (String line : Files.readAllLines(Path.of(args[0]), StandardCharsets.US_ASCII)) {
if (line.isEmpty() || line.startsWith("#")) continue;
String[] words = line.split(" ", 3);
String key = words[0] + " " + words[1];
String text = words.length < 3 ? "" : IpCheckCases.unescape(words[2]);
if (words[0].startsWith("dhcp")) {
cases.add(dhcpCase(words[0], words[1], text));
continue;
}
String[] names = FIELDS.get(key);
if (names == null) throw new IllegalArgumentException(line);
Object device = device(words[0]);
String start = stored(device, key);
JKonfiguration panel = JKonfiguration.getInstance((filius.hardware.Hardware) device);
JTextField field = (JTextField) get(panel, names[0]);
field.setText(text);
boolean marked = false;
if (names[1] != null) {
call(panel, names[1]);
marked = field.getForeground().equals(EingabenUeberpruefung.farbeFalsch);
} else if (names[2] != null) {
marked = !panel.ueberpruefen(pattern(names[2]), field);
}
// A panel that throws is part of the measurement ("error").
String error = null;
try {
call(panel, "aenderungenAnnehmen");
panel.updateAttribute();
} catch (java.lang.reflect.InvocationTargetException thrown) {
error = thrown.getCause().getClass().getSimpleName();
} catch (RuntimeException thrown) {
error = thrown.getClass().getSimpleName();
}
Map<String, Object> result = new LinkedHashMap<>();
result.put("device", words[0]);
result.put("field", words[1]);
result.put("input", text);
result.put("start", start);
result.put("marked", marked);
result.put("stored", stored(device, key));
result.put("shown", field.getText());
if (error != null) result.put("error", error);
cases.add(result);
}
Map<String, Object> flows = new LinkedHashMap<>();
// The MAC address as name, then another field taken: what becomes of the name?
for (String box : new String[] { "useMacAsName", "useIpAsName" }) {
Rechner host = new Rechner();
host.setName("PC-A");
JKonfiguration panel = JKonfiguration.getInstance(host);
((JCheckBox) get(panel, box)).setSelected(true);
call(panel, "aendereAnzeigeName");
((JTextField) get(panel, "gateway")).setText("192.168.0.1");
call(panel, "aenderungenAnnehmen");
((JCheckBox) get(panel, box)).setSelected(false);
call(panel, "aendereAnzeigeName");
flows.put(box + "ThenOff", host.getName());
}
// DHCP for the configuration while the host's own DHCP server is on.
{
Rechner host = new Rechner();
Betriebssystem os = (Betriebssystem) host.getSystemSoftware();
os.getDHCPServer().setAktiv(true);
JKonfiguration panel = JKonfiguration.getInstance(host);
((JCheckBox) get(panel, "dhcp")).setSelected(true);
call(panel, "aenderungenAnnehmen");
flows.put("dhcpClientStopsServer", !os.getDHCPServer().isAktiv());
flows.put("dhcpClient", os.isDHCPKonfiguration());
}
// The same on a home router (its WAN card).
{
Gateway home = new Gateway();
GatewayFirmware firmware = (GatewayFirmware) home.getSystemSoftware();
firmware.getDHCPServer().setAktiv(true);
JKonfiguration panel = JKonfiguration.getInstance(home);
((JCheckBox) get(panel, "dhcp")).setSelected(true);
call(panel, "aenderungenAnnehmen");
flows.put("homeRouterDhcpClientStopsServer", !firmware.getDHCPServer().isAktiv());
flows.put("homeRouterDhcpClient", firmware.isDHCPKonfiguration());
}
// Forwarding tables: two interfaces, a gateway, manual rows.
Map<String, Object> tables = new LinkedHashMap<>();
String[][] manual = { { "172.16.0.0", "255.255.0.0", "192.168.1.2", "192.168.1.1" }, { "010.020.0.0", "255.255.0.0", "10.0.0.2", "10.0.0.1" } };
tables.put("gatewayInSecondNetwork", table(new String[] { "10.0.0.1", "255.0.0.0", "192.168.1.1", "255.255.255.0" }, "192.168.1.254", manual));
tables.put("gatewayInNoNetwork", table(new String[] { "10.0.0.1", "255.0.0.0", "192.168.1.1", "255.255.255.0" }, "172.30.0.1", manual));
tables.put("gatewayInBothNetworks", table(new String[] { "10.0.0.1", "255.0.0.0", "10.1.0.1", "255.255.0.0" }, "10.1.0.254", new String[0][]));
tables.put("noGateway", table(new String[] { "10.0.0.1", "255.0.0.0", "192.168.1.1", "255.255.255.0" }, "", manual));
tables.put("threeInterfaces", table(new String[] { "10.0.0.1", "255.0.0.0", "192.168.1.1", "255.255.255.0", "172.16.5.1", "255.255.0.0" }, "10.0.0.254", new String[0][]));
// The panel's table: a manual row edited, a new row, a row removed (only the manual rows shown).
Map<String, Object> edits = new LinkedHashMap<>();
edits.put("invalidCell", edit(new String[][] { { 0 + "", "abc" } }, false));
edits.put("emptyCell", edit(new String[][] { { 0 + "", "" } }, false));
edits.put("leadingZeros", edit(new String[][] { { 0 + "", "010.0.0.0" } }, false));
edits.put("dottedTwice", edit(new String[][] { { 0 + "", "10..0.0" } }, false));
edits.put("maskCell", edit(new String[][] { { 1 + "", "255.0.255.0" } }, false));
edits.put("newRowThreeCells", edit(new String[][] { { "new" }, { "N0", "10.9.0.0" }, { "N1", "255.255.0.0" }, { "N2", "10.0.0.9" } }, false));
edits.put("newRowFourCells", edit(new String[][] { { "new" }, { "N0", "10.9.0.0" }, { "N1", "255.255.0.0" }, { "N2", "10.0.0.9" }, { "N3", "10.0.0.1" } }, false));
edits.put("newRowInvalidCell", edit(new String[][] { { "new" }, { "N0", "10.9.0.0" }, { "N1", "255.255.0.0" }, { "N2", "10.0.0.9" }, { "N3", "10..0.0.1" } }, false));
edits.put("newRowTwice", edit(new String[][] { { "new" }, { "new" } }, true));
edits.put("removeRow", edit(new String[][] { { "remove" } }, false));
Map<String, Object> output = new LinkedHashMap<>();
output.put("cases", cases);
output.put("flows", flows);
output.put("tables", tables);
output.put("dhcp", dhcpFlows());
output.put("forwarding", forwardingFlows());
output.put("firewall", firewallFlows());
output.put("connections", connectionsFlows());
output.put("edits", edits);
try (FileOutputStream out = new FileOutputStream(args[1])) {
out.write((FlsOracle.Json.write(output, "") + "\n").getBytes(StandardCharsets.UTF_8));
}
System.out.println("sheet cases: " + cases.size());
}
}