feat(core): the e-mail server after Java, measured against Java
SMTP on port 25 stores mails for the domain's accounts as Java reads them anew and tells a local sender of unknown recipients; POP3 on port 110 hands them out. The mailboxes are in the snapshot and go back into the project. EmailRun plays four stories over the network; MailCases feeds Java's workers without one (26 cases, de and en). 18 cases name Java bugs that the core does not take. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,7 @@ import { forwardsIPv4ByDefault, isPcClassEndpoint, stableGeneratedMacAddress } f
|
||||
import type {
|
||||
DeviceConfiguration,
|
||||
DhcpServerConfiguration,
|
||||
EmailServerAccount,
|
||||
FirewallConfiguration,
|
||||
GatewayPortForwardingRow,
|
||||
FirewallRule,
|
||||
@@ -115,6 +116,7 @@ import { RuntimeSpeed } from './speed.js';
|
||||
import { ApplicationThreads, waitUntil, type Steps, type Wait } from './threads.js';
|
||||
import { EchoServer, SimpleClient } from './apps/echo.js';
|
||||
import { WebBrowser, WebServer, type WebServerPage } from './apps/web.js';
|
||||
import { EmailServer } from './apps/emailServer.js';
|
||||
import { firewallLogPage, forwardingTablePage, ripTablePage, routerIndexPage } from './apps/routerPages.js';
|
||||
import { applyFirewallSettings, firewallSettingsPage } from './apps/firewallSettings.js';
|
||||
import { VirtualFileSystem } from '../model/virtualFileSystem.js';
|
||||
@@ -910,6 +912,9 @@ export class NetworkRuntimeEngine {
|
||||
private readonly simpleClientsByNodeId = new Map<Id, SimpleClient>();
|
||||
private readonly webServersByNodeId = new Map<Id, WebServer>();
|
||||
private readonly webBrowsersByNodeId = new Map<Id, WebBrowser>();
|
||||
private readonly emailServersByNodeId = new Map<Id, EmailServer>();
|
||||
/** The time of day a program writes into a mail (Java's new Date()). */
|
||||
private readonly wallClock: () => number;
|
||||
/** Each node's firewall log; it outlives a stop and a new start, as Java's log page does. */
|
||||
private readonly firewallLogsByNodeId = new Map<Id, FirewallLogEntry[]>();
|
||||
/** Accepted connections a program has taken (acceptTcpSteps). */
|
||||
@@ -929,6 +934,9 @@ export class NetworkRuntimeEngine {
|
||||
* web administration answers without a socket), likewise.
|
||||
*
|
||||
* `captureRetention`: how many frames each card's packet capture keeps.
|
||||
*
|
||||
* `wallClock`: the time of day in milliseconds, which a program writes
|
||||
* into a mail (the e-mail server's notice); the host's clock by default.
|
||||
*/
|
||||
constructor(
|
||||
seed: bigint | number = DEFAULT_SEED,
|
||||
@@ -939,8 +947,10 @@ export class NetworkRuntimeEngine {
|
||||
readonly swiftSends?: boolean;
|
||||
readonly swiftRouters?: boolean;
|
||||
readonly captureRetention?: CaptureRetentionPolicy;
|
||||
readonly wallClock?: () => number;
|
||||
} = {},
|
||||
) {
|
||||
this.wallClock = options.wallClock ?? Date.now;
|
||||
this.packetCapture = new PacketCapture(options.captureRetention ?? PRODUCTION_CAPTURE_RETENTION);
|
||||
this.cablesCarryOneFrameAtATime = options.swiftCables !== true;
|
||||
this.swiftArp = options.swiftArp === true;
|
||||
@@ -1032,6 +1042,7 @@ export class NetworkRuntimeEngine {
|
||||
this.simpleClientsByNodeId.clear();
|
||||
this.webServersByNodeId.clear();
|
||||
this.webBrowsersByNodeId.clear();
|
||||
this.emailServersByNodeId.clear();
|
||||
this.arpLookups = [];
|
||||
this.protocolThreads.clear();
|
||||
this.dnsQueriesBySocketId.clear();
|
||||
@@ -1117,6 +1128,20 @@ export class NetworkRuntimeEngine {
|
||||
return this.webBrowsersByNodeId.get(nodeId);
|
||||
}
|
||||
|
||||
/** The node's e-mail server while the simulation runs, if it has one installed. */
|
||||
emailServer(nodeId: Id): EmailServer | undefined {
|
||||
return this.emailServersByNodeId.get(nodeId);
|
||||
}
|
||||
|
||||
/** An e-mail server's accounts as a run changed them, into the snapshot (and so into the project saved from it). */
|
||||
private setEmailServerAccounts(nodeId: Id, accounts: readonly EmailServerAccount[]): void {
|
||||
const servers = new Map(this.stateValue.topologySnapshot.emailServersByNodeId);
|
||||
const server = servers.get(nodeId);
|
||||
if (server === undefined) return;
|
||||
servers.set(nodeId, { ...server, accounts });
|
||||
this.stateValue.topologySnapshot = withSnapshot(this.stateValue.topologySnapshot, { emailServersByNodeId: servers });
|
||||
}
|
||||
|
||||
/**
|
||||
* Each node's programs start with the simulation (Betriebssystem.starten),
|
||||
* from the snapshot; what they show starts empty.
|
||||
@@ -1127,6 +1152,7 @@ export class NetworkRuntimeEngine {
|
||||
this.simpleClientsByNodeId.clear();
|
||||
this.webServersByNodeId.clear();
|
||||
this.webBrowsersByNodeId.clear();
|
||||
this.emailServersByNodeId.clear();
|
||||
for (const node of snapshot.nodes) {
|
||||
const echoServer = snapshot.echoServersByNodeId.get(node.id);
|
||||
if (echoServer !== undefined) this.echoServersByNodeId.set(node.id, new EchoServer(this, node.id, echoServer));
|
||||
@@ -1137,6 +1163,21 @@ export class NetworkRuntimeEngine {
|
||||
this.webServersByNodeId.set(node.id, new WebServer(this, node.id, webServer, snapshot.fileSystemsByNodeId.get(node.id)));
|
||||
}
|
||||
if (snapshot.webBrowserNodeIds.has(node.id)) this.webBrowsersByNodeId.set(node.id, new WebBrowser(this, node.id));
|
||||
const emailServer = snapshot.emailServersByNodeId.get(node.id);
|
||||
if (emailServer !== undefined) {
|
||||
const nodeId = node.id;
|
||||
this.emailServersByNodeId.set(
|
||||
nodeId,
|
||||
new EmailServer(
|
||||
this,
|
||||
nodeId,
|
||||
emailServer,
|
||||
(accounts) => this.setEmailServerAccounts(nodeId, accounts),
|
||||
() => this.networkInterfaces(nodeId)[0]?.ipAddress ?? 'null',
|
||||
this.wallClock,
|
||||
),
|
||||
);
|
||||
}
|
||||
if ((node.kind === 'router' || node.kind === 'gateway') && !this.swiftRouters) this.startRouterWebServer(node.id, node.kind);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user