A web page by its DNS name (internetwork_komplett), an e-mail between two networks through NAT and MX relay (Internet_Komplett_..._DE), a file found and fetched with Gnutella across a router (zwei_rechnernetze_komplett): ExampleRun records frames, tables, program lines and end states; the core plays the same calls and matches card by card. Equal RIP ties take Java's choice, RIP beacons are left out, the search's two connections compared flow by flow. Also: a core test that an equal RIP route from another router neither replaces nor refreshes the route, and nine stale type errors in core tests fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
364 lines
18 KiB
Java
364 lines
18 KiB
Java
// Runs one of FILIUS' own example files (corpus/java-examples) in Java FILIUS,
|
|
// loaded and started as FILIUS opens and starts it (ScenarioNetwork.fromFile),
|
|
// plays a story on it and records every frame each card saw, the capture's
|
|
// tables, the story's calls and each host's addresses at the end.
|
|
//
|
|
// ExampleRun <file.fls> <story> <milliseconds> <out.json>
|
|
//
|
|
// The stories:
|
|
// routing internetwork_komplett: Notebook 1.10 pings Webserver 5.10 across
|
|
// three routers; then Notebook 1.11, which has no gateway
|
|
// dhcp einfaches_rechnernetz_komplett: four notebooks get addresses
|
|
// from the server
|
|
// dns dns_server: the notebook asks its DNS server (recursive) for
|
|
// www.filius.de, then for the mail server of filius.de
|
|
// nat nat_with_mapped_port: Notebook 1.10 (behind Heimrouter Paul)
|
|
// pings Webserver www.jupiter.de, then Heimrouter Vincent's WAN
|
|
// address
|
|
// firewall Internet_Komplett_…_DE: when the DHCP clients have their
|
|
// addresses (8 s), iPad 1 (its network dropped by the firewall,
|
|
// which leaves ICMP to its own switch) pings mail.gmx.de, then
|
|
// the first Notebook (accepted), then mail.gmx.de pings the
|
|
// firewall's WAN address. Targets with one shortest RIP path
|
|
// each: where two are equal, Java takes whichever its threads
|
|
// learn first
|
|
//
|
|
// The classics of the classroom (the acceptance of F3), with what each
|
|
// program told its window ("programs": time, program, text) and where each
|
|
// ended ("end"):
|
|
// web internetwork_komplett: Notebook 1.10 types www.filius.de into
|
|
// its browser; the name is asked of DNS-Server 2.10 in another
|
|
// network, the page comes from Webserver 5.10, two routers away
|
|
// email Internet_Komplett_…_DE: when the DHCP clients have their
|
|
// addresses (8 s), herbert@gmx.de (the first Notebook, in the
|
|
// school's network behind the firewall) writes to
|
|
// frimp@webmail.de; mail.gmx.de hands it on to mail.webmail.de
|
|
// in another network; at 22 s frimp (Notebook#2) fetches. The
|
|
// file keeps the programs' accounts only in the older kontoListe,
|
|
// which FILIUS 2.14 no longer reads (the reader takes it,
|
|
// importEmail.ts): they are set as one types them into the
|
|
// program's account dialog, with the values the list holds
|
|
// gnutella zwei_rechnernetze_komplett with FILIUS' Gnutella program on
|
|
// Notebook 1.10, Notebook 1.11 and DNS-Server 2.10 (in the other
|
|
// network; it shares "Lied der Woche.txt"), each keeping three
|
|
// peers: 1.10 joins 2.10; 1.11 joins 1.10 (its ping goes on to
|
|
// 2.10, the pong comes back through 1.10); 1.10 searches "woche"
|
|
// (1.11 passes the query on) and downloads the hit through the
|
|
// router (1.11 has no gateway: it could not fetch from the other
|
|
// network)
|
|
|
|
import filius.hardware.knoten.Knoten;
|
|
import filius.software.dateiaustausch.PeerToPeerAnwendung;
|
|
import filius.software.dns.Resolver;
|
|
import filius.software.email.Email;
|
|
import filius.software.email.EmailAnwendung;
|
|
import filius.software.email.EmailKonto;
|
|
import filius.software.email.EmailServer;
|
|
import filius.software.email.EmailUtils;
|
|
import filius.software.system.Datei;
|
|
import filius.software.www.HTTPNachricht;
|
|
import filius.software.www.WebBrowser;
|
|
import filius.software.www.WebServer;
|
|
import filius.software.rip.RIPRoute;
|
|
import filius.software.rip.RIPTable;
|
|
import filius.software.system.VermittlungsrechnerBetriebssystem;
|
|
import filius.software.system.InternetKnotenBetriebssystem;
|
|
import filius.software.vermittlungsschicht.IcmpPaket;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
public class ExampleRun {
|
|
static ScenarioNetwork network;
|
|
static final List<Object[]> events = new ArrayList<>();
|
|
/** Every change of a RIP router's table: time, router, table. */
|
|
static final List<Object[]> ripChanges = new ArrayList<>();
|
|
static final Map<String, String> lastTables = new java.util.HashMap<>();
|
|
|
|
/** A RIP router's table as one line: `network/mask gateway interface hops; …`, in its order. */
|
|
static String tableText(RIPTable table) {
|
|
StringBuilder text = new StringBuilder();
|
|
synchronized (table) {
|
|
for (RIPRoute route : table.routes) {
|
|
if (text.length() > 0) text.append("; ");
|
|
text.append(route.getNetAddress()).append('/').append(route.getNetMask()).append(' ').append(route.getGateway())
|
|
.append(' ').append(route.getInterfaceIpAddress()).append(' ').append(route.hops);
|
|
}
|
|
}
|
|
return text.toString();
|
|
}
|
|
|
|
static synchronized void pollTables() {
|
|
for (Map.Entry<String, Knoten> entry : network.nodes.entrySet()) {
|
|
if (!(entry.getValue().getSystemSoftware() instanceof VermittlungsrechnerBetriebssystem)) continue;
|
|
RIPTable table = ((VermittlungsrechnerBetriebssystem) entry.getValue().getSystemSoftware()).getRIPTable();
|
|
if (table == null) continue;
|
|
String text = tableText(table);
|
|
if (!text.equals(lastTables.get(entry.getKey()))) {
|
|
lastTables.put(entry.getKey(), text);
|
|
ripChanges.add(new Object[] { System.currentTimeMillis() - network.start, entry.getKey(), text });
|
|
}
|
|
}
|
|
}
|
|
|
|
static synchronized void event(String what) {
|
|
events.add(new Object[] { System.currentTimeMillis() - network.start, what });
|
|
}
|
|
|
|
/** What a program told its window: time, program, text. */
|
|
static final List<Object[]> told = new ArrayList<>();
|
|
|
|
static synchronized void tell(String program, String text) {
|
|
told.add(new Object[] { System.currentTimeMillis() - network.start, program, text });
|
|
}
|
|
|
|
static <T> T program(String node, String name) {
|
|
@SuppressWarnings("unchecked")
|
|
T program = (T) network.system(node).holeSoftware(name);
|
|
return program;
|
|
}
|
|
|
|
static void watchServer(String node, String program) {
|
|
filius.software.Anwendung server = program(node, program.equals("webServer") ? "filius.software.www.WebServer" : "filius.software.email.EmailServer");
|
|
server.hinzuBeobachter((observable, data) -> tell(node, String.valueOf(data)));
|
|
}
|
|
|
|
/** GUIApplicationEmailAnwendungWindow's send button, as EmailClientRun makes it. */
|
|
static void send(EmailAnwendung program, EmailKonto account, String to, String subject, String text) {
|
|
Email mail = new Email();
|
|
mail.setAbsender(account.getVorname() + (!account.getNachname().isEmpty() ? (" " + account.getNachname()) : "") + " <"
|
|
+ account.getEmailAdresse() + ">");
|
|
mail.setEmpfaenger(EmailUtils.stringToAddressEntryList(to));
|
|
mail.setCc(EmailUtils.stringToAddressEntryList(""));
|
|
mail.setBcc(EmailUtils.stringToAddressEntryList(""));
|
|
mail.setBetreff(subject);
|
|
mail.setText(text);
|
|
program.versendeEmail(account.getSmtpserver(), mail, account.getEmailAdresse());
|
|
}
|
|
|
|
static EmailKonto account(EmailAnwendung program) {
|
|
return program.holeKontoListe().values().iterator().next();
|
|
}
|
|
|
|
/** The account dialog's fields: user, password, first name, last name, address, and the server for both POP3 and SMTP. */
|
|
static void setAccount(EmailAnwendung program, String user, String password, String first, String last, String address, String server) {
|
|
EmailKonto account = new EmailKonto();
|
|
account.setBenutzername(user);
|
|
account.setPasswort(password);
|
|
account.setVorname(first);
|
|
account.setNachname(last);
|
|
account.setEmailAdresse(address);
|
|
account.setPop3server(server);
|
|
account.setPop3port("110");
|
|
account.setSmtpserver(server);
|
|
account.setSmtpport("25");
|
|
program.setzeKonto(account);
|
|
}
|
|
|
|
static final String[] GNUTELLA_PEERS = { "Notebook 1.10", "Notebook 1.11", "DNS-Server 2.10" };
|
|
|
|
interface Call {
|
|
String run() throws Exception;
|
|
}
|
|
|
|
static void call(String label, Call call) {
|
|
event(label + " called");
|
|
try {
|
|
event(label + " returned " + call.run());
|
|
} catch (Exception e) {
|
|
event(label + " failed " + e.getClass().getSimpleName());
|
|
}
|
|
}
|
|
|
|
static String icmp(IcmpPaket packet) {
|
|
return packet == null ? "null" : "ICMP " + packet.getIcmpType() + "/" + packet.getIcmpCode() + " from " + packet.getSender();
|
|
}
|
|
|
|
static void ping(String node, String target, int sequence) {
|
|
call("sendProbe " + target, () -> icmp(network.system(node).holeICMP().sendProbe(target, 64, 1, sequence)));
|
|
}
|
|
|
|
static Resolver resolver(String node) {
|
|
return network.system(node).holeDNSClient();
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
network = ScenarioNetwork.fromFile(args[0]);
|
|
String story = args[1];
|
|
long end = Long.parseLong(args[2]);
|
|
if (story.equals("web")) {
|
|
watchServer("Webserver 5.10", "webServer");
|
|
WebBrowser browser = program("Notebook 1.10", "filius.software.www.WebBrowser");
|
|
browser.hinzuBeobachter((observable, data) -> {
|
|
if (data instanceof HTTPNachricht) {
|
|
HTTPNachricht page = (HTTPNachricht) data;
|
|
tell("browser", "page " + page.getStatusCode() + " " + page.getContentType() + "\n" + page.getDaten());
|
|
} else {
|
|
tell("browser", data == null ? "update" : String.valueOf(data));
|
|
}
|
|
});
|
|
}
|
|
if (story.equals("email")) {
|
|
watchServer("mail.gmx.de (130.100.200.25)", "emailServer");
|
|
watchServer("mail.webmail.de (140.100.200.25)", "emailServer");
|
|
setAccount(program("Notebook", "filius.software.email.EmailAnwendung"), "herbert", "herbert1234", "Herbert", "Schmidt", "herbert@gmx.de", "mail.gmx.de");
|
|
setAccount(program("Notebook#2", "filius.software.email.EmailAnwendung"), "frimp", "frimp1234", "Frimp", "Mueller", "frimp@webmail.de", "mail.webmail.de");
|
|
for (String node : new String[] { "Notebook", "Notebook#2" }) {
|
|
EmailAnwendung client = program(node, "filius.software.email.EmailAnwendung");
|
|
client.hinzuBeobachter((observable, data) -> tell(node, String.valueOf(data)));
|
|
}
|
|
}
|
|
if (story.equals("gnutella")) {
|
|
for (String node : GNUTELLA_PEERS) {
|
|
network.system(node).installApp("filius.software.dateiaustausch.PeerToPeerAnwendung", false);
|
|
PeerToPeerAnwendung peer = program(node, "filius.software.dateiaustausch.PeerToPeerAnwendung");
|
|
peer.setMaxTeilnehmerZahl(3);
|
|
peer.hinzuBeobachter((observable, data) -> {
|
|
tell(node, "peers " + String.join(",", peer.holeBekanntePeerToPeerTeilnehmer()));
|
|
tell(node, "results " + String.join("|", peer.holeErgebnisse()));
|
|
});
|
|
}
|
|
network.system("DNS-Server 2.10").getDateisystem().speicherDatei("/peer2peer", new Datei("Lied der Woche.txt", "txt", "La la la"));
|
|
}
|
|
network.start();
|
|
Thread poller = new Thread(() -> {
|
|
while (true) {
|
|
pollTables();
|
|
try {
|
|
Thread.sleep(2);
|
|
} catch (InterruptedException e) {
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
poller.setDaemon(true);
|
|
poller.start();
|
|
Thread.sleep(500);
|
|
switch (story) {
|
|
case "routing":
|
|
ping("Notebook 1.10", "141.99.5.10", 1);
|
|
ping("Notebook 1.11", "141.99.5.10", 2);
|
|
break;
|
|
case "dhcp":
|
|
break;
|
|
case "dns":
|
|
call("holeIPAdresse www.filius.de", () -> resolver("Notebook").holeIPAdresse("www.filius.de"));
|
|
call("holeIPAdresseMailServer filius.de", () -> resolver("Notebook").holeIPAdresseMailServer("filius.de"));
|
|
break;
|
|
case "nat":
|
|
ping("Notebook 1.10", "141.99.4.10", 1);
|
|
ping("Notebook 1.10", "141.99.5.10", 2);
|
|
break;
|
|
case "firewall":
|
|
Thread.sleep(7500);
|
|
ping("iPad 1", "130.100.200.25", 1);
|
|
ping("Notebook", "130.100.200.25", 2);
|
|
ping("mail.gmx.de (130.100.200.25)", "42.0.0.10", 3);
|
|
break;
|
|
case "web": {
|
|
tell("browser", "call holeWebseite www.filius.de");
|
|
WebBrowser browser = program("Notebook 1.10", "filius.software.www.WebBrowser");
|
|
browser.holeWebseite(WebRun.url("www.filius.de"));
|
|
break;
|
|
}
|
|
case "email": {
|
|
Thread.sleep(7500);
|
|
EmailAnwendung herbert = program("Notebook", "filius.software.email.EmailAnwendung");
|
|
tell("Notebook", "call versendeEmail frimp@webmail.de");
|
|
send(herbert, account(herbert), "frimp@webmail.de", "Treffen", "Hallo Frimp,\nbis morgen!\nHerbert");
|
|
long until = network.start + 22000 - System.currentTimeMillis();
|
|
if (until > 0) Thread.sleep(until);
|
|
EmailAnwendung frimp = program("Notebook#2", "filius.software.email.EmailAnwendung");
|
|
EmailKonto account = account(frimp);
|
|
tell("Notebook#2", "call emailsAbholenEmails");
|
|
frimp.emailsAbholenEmails(account.getBenutzername(), account.getPasswort(), account.getPop3port(), account.getPop3server());
|
|
break;
|
|
}
|
|
case "gnutella": {
|
|
PeerToPeerAnwendung searching = program("Notebook 1.10", "filius.software.dateiaustausch.PeerToPeerAnwendung");
|
|
PeerToPeerAnwendung near = program("Notebook 1.11", "filius.software.dateiaustausch.PeerToPeerAnwendung");
|
|
tell("Notebook 1.10", "call join 141.99.2.10");
|
|
searching.beitretenNetzwerk("141.99.2.10");
|
|
Thread.sleep(network.start + 3000 - System.currentTimeMillis());
|
|
tell("Notebook 1.11", "call join 141.99.1.10");
|
|
near.beitretenNetzwerk("141.99.1.10");
|
|
Thread.sleep(network.start + 5500 - System.currentTimeMillis());
|
|
tell("Notebook 1.10", "call search woche");
|
|
searching.sucheDatei("woche");
|
|
Thread.sleep(network.start + 8500 - System.currentTimeMillis());
|
|
tell("Notebook 1.10", "call download 0");
|
|
if (searching.holeErgebnisse().isEmpty()) tell("Notebook 1.10", "skipped download 0");
|
|
else searching.herunterladenDatei(0);
|
|
break;
|
|
}
|
|
default:
|
|
throw new IllegalArgumentException("unknown story " + story);
|
|
}
|
|
long rest = network.start + end - System.currentTimeMillis();
|
|
if (rest > 0) Thread.sleep(rest);
|
|
|
|
ScenarioNetwork.Json out = new ScenarioNetwork.Json();
|
|
out.open('{');
|
|
network.writeFrames(out);
|
|
network.writeDevices(out);
|
|
// Every change of each RIP router's table (polled every 2 ms), as RipRun writes them.
|
|
synchronized (ExampleRun.class) {
|
|
out.key("ripTables").open('[');
|
|
for (Object[] each : ripChanges) out.open('[').number((Long) each[0]).string((String) each[1]).string((String) each[2]).close(']');
|
|
out.close(']');
|
|
}
|
|
synchronized (ExampleRun.class) {
|
|
out.key("events").open('[');
|
|
for (Object[] each : events) out.open('[').number((Long) each[0]).string((String) each[1]).close(']');
|
|
out.close(']');
|
|
}
|
|
synchronized (ExampleRun.class) {
|
|
out.key("programs").open('[');
|
|
for (Object[] each : told) out.open('[').number((Long) each[0]).string((String) each[1]).string((String) each[2]).close(']');
|
|
out.close(']');
|
|
}
|
|
// Where the programs ended: each e-mail server's accounts with their mails, each e-mail program's
|
|
// inbox and sent mails (as EmailClientRun writes them); each Gnutella program's peers, results and files.
|
|
out.key("end").open('{');
|
|
if (story.equals("email")) {
|
|
for (String node : new String[] { "mail.gmx.de (130.100.200.25)", "mail.webmail.de (140.100.200.25)" }) {
|
|
out.key(node);
|
|
EmailClientRun.accounts(out, program(node, "filius.software.email.EmailServer"));
|
|
}
|
|
for (String node : new String[] { "Notebook", "Notebook#2" }) {
|
|
EmailAnwendung client = program(node, "filius.software.email.EmailAnwendung");
|
|
out.key(node).open('{');
|
|
out.key("inbox");
|
|
EmailClientRun.mails(out, client.getEmpfangeneNachrichten());
|
|
out.key("sent");
|
|
EmailClientRun.mails(out, client.getGesendeteNachrichten());
|
|
out.close('}');
|
|
}
|
|
}
|
|
if (story.equals("gnutella")) {
|
|
for (String node : GNUTELLA_PEERS) {
|
|
PeerToPeerAnwendung peer = program(node, "filius.software.dateiaustausch.PeerToPeerAnwendung");
|
|
out.key(node).open('{');
|
|
out.key("peers").open('[');
|
|
for (String each : peer.holeBekanntePeerToPeerTeilnehmer()) out.string(each);
|
|
out.close(']');
|
|
out.key("results").open('[');
|
|
for (String each : peer.holeErgebnisse()) out.string(each);
|
|
out.close(']');
|
|
out.key("files").open('[');
|
|
for (Datei file : network.system(node).getDateisystem().holeDateien(peer.holeVerzeichnis())) {
|
|
out.open('[').string(file.getName()).string(file.getDateiTyp()).string(file.getDateiInhalt()).close(']');
|
|
}
|
|
out.close(']');
|
|
out.close('}');
|
|
}
|
|
}
|
|
out.close('}');
|
|
out.close('}');
|
|
network.stop();
|
|
ScenarioNetwork.write(args[3], out);
|
|
System.exit(0);
|
|
}
|
|
}
|