// 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: // // 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): // // // // 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 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 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 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 rows = new ArrayList<>(); List entries = table.holeTabelle(); List flags = table.holeManuelleEintraegeFlags(); for (int i = 0; i < entries.size(); i++) { Map 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: {"", text} sets a cell of the first row, * {"N", 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 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 result = new LinkedHashMap<>(); List 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 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 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 dhcpFlows() throws Exception { Map flows = new LinkedHashMap<>(); for (String kind : new String[] { "dhcpHost", "dhcpHome" }) { InternetKnotenBetriebssystem os = dhcpDevice(kind); JDHCPKonfiguration dialog = dhcpDialog(os); Map 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 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 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 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 flows = new LinkedHashMap<>(); List kept = new ArrayList<>(); List 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 rule(FirewallRule rule) { Map 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 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 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 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 firewallFlows() throws Exception { Map 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 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 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 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 connectionsState(JConnectionsDialog dialog, Vermittlungsrechner router) throws Exception { Map state = new LinkedHashMap<>(); List local = new ArrayList<>(); List remote = new ArrayList<>(); List 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 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 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 steps = new ArrayList<>(); // A click on a card's button ("c"), "+" 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 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 macs = new ArrayList<>(); for (NetzwerkInterface nic : router.getNetzwerkInterfaces()) macs.add(nic.getMac()); Map 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 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 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 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 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 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 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()); } }