feat(core): recursive DNS server after Java, measured with Java FILIUS
The resolver runs on the events now (a query, its dnsQueryTimeout, its answer at the socket); the blocking resolveDns waits for it, and a recursive server uses it: its own records, else the zone's name server from its records, else its own DNS server, else a referral (DNSServer.answerWithRemoteData). DnsRun plays five stories on the new dns-recursive network. Measured and not taken: a server that is its zone's name server asks itself, which asks itself again, without end (the client times out, then every level floods its own DNS server); Java guards only the step through its own DNS server. The core asks itself in neither step. That story is compared up to the client's query (JAVA_UNTIL). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+2
-2
File diff suppressed because one or more lines are too long
@@ -29,7 +29,8 @@
|
||||
// or forwards ICMP); each takes one unit after the other and holds the rest
|
||||
// while it waits for ARP. A socket's send blocks behind its thread.
|
||||
// DNS after Java (filius.software.dns: DNSServer, DNSServerMitarbeiter,
|
||||
// Resolver, DNSQueryAgent), not Swift's typed format; recursion not yet.
|
||||
// Resolver, DNSQueryAgent), not Swift's typed format; a recursive server
|
||||
// asks on without asking itself (Java does, without end).
|
||||
// Java counterparts: filius.hardware.Verbindung, filius.software.netzzugangsschicht,
|
||||
// filius.software.vermittlungsschicht (ARP, IP, ICMP), system.SwitchFirmware,
|
||||
// filius.software.transportschicht (UDP, TCP), filius.software.dhcp.
|
||||
@@ -41,7 +42,7 @@
|
||||
// advance the virtual clock inside the call, exactly as the Swift port does.
|
||||
//
|
||||
// Not ported yet — each place throws NotPortedError instead of guessing:
|
||||
// gateway NAT, RIP routing, remote links, recursive DNS resolution.
|
||||
// gateway NAT, RIP routing, remote links.
|
||||
|
||||
import { compareIds, type Id } from '../ids.js';
|
||||
import { isPcClassEndpoint, stableGeneratedMacAddress } from '../model/node.js';
|
||||
@@ -135,6 +136,8 @@ export type ScheduledEventKind =
|
||||
/** The lookups waiting for an address wake up, after what else happens at that time. */
|
||||
| { readonly kind: 'arpAnswered'; readonly nodeId: Id; readonly ipAddress: string; readonly macAddress: string }
|
||||
| { readonly kind: 'tcpTimeout'; readonly sessionId: Id }
|
||||
/** A DNS query waited five round trips for its answer (DNSQueryAgent). */
|
||||
| { readonly kind: 'dnsQueryTimeout'; readonly socketId: Id }
|
||||
| {
|
||||
/** A frame that waited in the port's output buffer leaves: the cable was busy. */
|
||||
readonly kind: 'ethernetLinkDeparture';
|
||||
@@ -322,6 +325,10 @@ export type TcpConnectionResult = 'connected' | 'invalidSocket' | 'unreachable'
|
||||
/** Java's DNSQueryAgent waits five round trips for an answer. */
|
||||
export const DNS_QUERY_TIMEOUT_ROUND_TRIPS = 5;
|
||||
export type DnsResolution = { readonly type: 'answered'; readonly records: readonly DnsMessageRecord[] } | { readonly type: 'timedOut' };
|
||||
/** A resolution as it goes on the events: the answer and authority records apart, as Java keeps them. */
|
||||
type DnsLookup =
|
||||
| { readonly type: 'answered'; readonly answers: readonly DnsMessageRecord[]; readonly authority: readonly DnsMessageRecord[] }
|
||||
| { readonly type: 'timedOut' };
|
||||
export type DnsAddressResolution = { readonly type: 'resolved'; readonly address: string | undefined } | { readonly type: 'timedOut' };
|
||||
|
||||
export const DHCP_SERVER_PORT = 67;
|
||||
@@ -622,6 +629,8 @@ export class NetworkRuntimeEngine {
|
||||
/** ARP lookups waiting for their answer (Java: a thread in holeARPTabellenEintrag). */
|
||||
private arpLookups: ArpLookup[] = [];
|
||||
private nextArpLookupId = 1;
|
||||
/** DNS queries waiting for their answer, by the socket they were sent from. */
|
||||
private dnsQueriesBySocketId = new Map<Id, (response: DnsMessage | undefined) => void>();
|
||||
/** Per node and kind (`nodeId/udp`, `nodeId/icmp`, …). */
|
||||
private protocolThreads = new Map<string, ProtocolThread>();
|
||||
/** Per cable end (node and port): when the last frame sent there arrives. */
|
||||
@@ -710,6 +719,7 @@ export class NetworkRuntimeEngine {
|
||||
this.isProcessingScheduledEvents = false;
|
||||
this.arpLookups = [];
|
||||
this.protocolThreads.clear();
|
||||
this.dnsQueriesBySocketId.clear();
|
||||
state.pendingEvents = [];
|
||||
state.arpCachesByNodeId.clear();
|
||||
state.switchForwardingTablesByNodeId.clear();
|
||||
@@ -908,6 +918,9 @@ export class NetworkRuntimeEngine {
|
||||
case 'arpAnswered':
|
||||
this.completeArpLookups(kind.nodeId, kind.ipAddress, kind.macAddress);
|
||||
return;
|
||||
case 'dnsQueryTimeout':
|
||||
this.finishDnsQuery(kind.socketId, undefined);
|
||||
return;
|
||||
case 'tcpTimeout':
|
||||
this.handleTcpTimeout(kind.sessionId);
|
||||
return;
|
||||
@@ -1233,6 +1246,11 @@ export class NetworkRuntimeEngine {
|
||||
});
|
||||
if (datagram.destinationPort === DHCP_CLIENT_PORT && this.readDhcpResponse(nodeId)) this.advanceDhcpClient(nodeId);
|
||||
if (datagram.destinationPort === DNS_PORT) this.serveDnsQueries(nodeId);
|
||||
for (const socket of matching) {
|
||||
if (!this.dnsQueriesBySocketId.has(socket.id)) continue;
|
||||
const received = this.receiveUdp(socket.id);
|
||||
this.finishDnsQuery(socket.id, received === undefined ? undefined : decodeDnsMessage(new TextDecoder().decode(received.datagram.payload)));
|
||||
}
|
||||
}
|
||||
|
||||
// ── DNS (after Java's DNSServer and Resolver) ────────────────────────────
|
||||
@@ -1242,39 +1260,103 @@ export class NetworkRuntimeEngine {
|
||||
const state = this.stateValue;
|
||||
state.dnsServerSocketIdsByNodeId.clear();
|
||||
for (const [nodeId, configuration] of state.topologySnapshot.dnsServerConfigurationsByNodeId) {
|
||||
if (configuration.recursiveResolutionEnabled) throw new NotPortedError('recursive DNS resolution');
|
||||
const socketId = this.bindUdpSocket(nodeId, { localPort: DNS_PORT });
|
||||
if (socketId !== undefined) state.dnsServerSocketIdsByNodeId.set(nodeId, socketId);
|
||||
}
|
||||
}
|
||||
|
||||
/** DNSServerMitarbeiter: each query answered to where it came from, with the query's ID. */
|
||||
/**
|
||||
* DNSServerMitarbeiter: each query answered to where it came from, with the
|
||||
* query's ID, once every question has its records (a recursive server may
|
||||
* have to ask on).
|
||||
*/
|
||||
private serveDnsQueries(nodeId: Id): void {
|
||||
const state = this.stateValue;
|
||||
const socketId = state.dnsServerSocketIdsByNodeId.get(nodeId);
|
||||
const configuration = state.topologySnapshot.dnsServerConfigurationsByNodeId.get(nodeId);
|
||||
if (socketId === undefined || configuration === undefined) return;
|
||||
if (socketId === undefined || !state.topologySnapshot.dnsServerConfigurationsByNodeId.has(nodeId)) return;
|
||||
for (let received = this.receiveUdp(socketId); received !== undefined; received = this.receiveUdp(socketId)) {
|
||||
const client = { ipAddress: received.senderIPAddress, port: received.datagram.sourcePort };
|
||||
const query = decodeDnsMessage(new TextDecoder().decode(received.datagram.payload));
|
||||
const records = configuration.records.map(messageRecord);
|
||||
const answers = query.questions.flatMap((question) => answerDnsQuestion(question, records));
|
||||
const response: DnsMessage = {
|
||||
id: query.id,
|
||||
queryResponse: DNS_RESPONSE,
|
||||
responseCode: 0,
|
||||
questions: [],
|
||||
answers,
|
||||
authority: [],
|
||||
additional: [],
|
||||
};
|
||||
this.recordTrace({ nodeId, direction: 'outbound', layer: 'application', operation: 'sent', detail: 'DNS response' });
|
||||
this.sendUdp(socketId, new TextEncoder().encode(encodeDnsMessage(response)), {
|
||||
ipAddress: received.senderIPAddress,
|
||||
port: received.datagram.sourcePort,
|
||||
this.answerDnsQuestions(nodeId, query.questions, [], (answers) => {
|
||||
const response: DnsMessage = {
|
||||
id: query.id,
|
||||
queryResponse: DNS_RESPONSE,
|
||||
responseCode: 0,
|
||||
questions: [],
|
||||
answers,
|
||||
authority: [],
|
||||
additional: [],
|
||||
};
|
||||
this.recordTrace({ nodeId, direction: 'outbound', layer: 'application', operation: 'sent', detail: 'DNS response' });
|
||||
this.sendUdp(socketId, new TextEncoder().encode(encodeDnsMessage(response)), client);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** The questions one after the other, as the worker loops over them. */
|
||||
private answerDnsQuestions(
|
||||
nodeId: Id,
|
||||
questions: readonly DnsQuestion[],
|
||||
answered: readonly DnsMessageRecord[],
|
||||
done: (answers: DnsMessageRecord[]) => void,
|
||||
): void {
|
||||
const [question, ...rest] = questions;
|
||||
if (question === undefined) {
|
||||
done([...answered]);
|
||||
return;
|
||||
}
|
||||
this.answerDnsQuestion(nodeId, question, (answers) => this.answerDnsQuestions(nodeId, rest, [...answered, ...answers], done));
|
||||
}
|
||||
|
||||
/**
|
||||
* DNSServer.answer: the server's own records for the question; else, when
|
||||
* it resolves recursively, what it learns from others; else a referral
|
||||
* (the NS record of the question's zone and its address).
|
||||
*/
|
||||
private answerDnsQuestion(nodeId: Id, question: DnsQuestion, done: (answers: DnsMessageRecord[]) => void): void {
|
||||
const configuration = this.stateValue.topologySnapshot.dnsServerConfigurationsByNodeId.get(nodeId);
|
||||
const records = (configuration?.records ?? []).map(messageRecord);
|
||||
const local = localDnsAnswers(question, records);
|
||||
if (local.length > 0 || configuration?.recursiveResolutionEnabled !== true) {
|
||||
done(local.length > 0 ? local : dnsReferral(question, records));
|
||||
return;
|
||||
}
|
||||
this.resolveDnsRemotely(nodeId, question, records, (remote) => done(remote.length > 0 ? remote : dnsReferral(question, records)));
|
||||
}
|
||||
|
||||
/**
|
||||
* DNSServer.answerWithRemoteData: resolves with the name server its records
|
||||
* name for the question's zone, then, if that brings nothing, with its own
|
||||
* DNS server. The records of the answer and authority sections; nothing
|
||||
* when a server does not answer.
|
||||
*
|
||||
* Java asks its own DNS server only when that is not itself, but asks the
|
||||
* zone's name server even then: a query to itself, which asks itself again,
|
||||
* without end (measured: the client times out, then every level falls back
|
||||
* to its own DNS server). The core asks neither when it is itself.
|
||||
*/
|
||||
private resolveDnsRemotely(nodeId: Id, question: DnsQuestion, records: readonly DnsMessageRecord[], done: (answers: DnsMessageRecord[]) => void): void {
|
||||
const ownAddresses = new Set(this.networkInterfaces(nodeId).map((each) => each.ipAddress));
|
||||
const resolveWith = (server: string | undefined, next: (answers: DnsMessageRecord[]) => void) => {
|
||||
if (server === undefined || server.trim() === '' || ownAddresses.has(server)) {
|
||||
next([]);
|
||||
return;
|
||||
}
|
||||
this.beginDnsResolution(nodeId, question.name, question.type, server, (resolution) =>
|
||||
next(resolution.type === 'answered' ? [...resolution.answers, ...resolution.authority] : []),
|
||||
);
|
||||
};
|
||||
const nameServer = findApplicableNameServerRecord(question.name, records);
|
||||
const nameServerAddress = nameServer === undefined ? undefined : findDnsRecords(nameServer.data, 'A', records)[0]?.data;
|
||||
resolveWith(nameServerAddress, (answers) => {
|
||||
if (answers.length > 0) {
|
||||
done(answers);
|
||||
return;
|
||||
}
|
||||
resolveWith(this.stateValue.topologySnapshot.deviceConfigurations.get(nodeId)?.dnsServer, done);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Java's Resolver.resolve, blocking on the virtual clock: asks the node's
|
||||
* DNS server (or `serverIPAddress`), and follows the name servers it is
|
||||
@@ -1283,35 +1365,69 @@ export class NetworkRuntimeEngine {
|
||||
* does not answer within five round trips (Java: TimeOutException).
|
||||
*/
|
||||
resolveDns(nodeId: Id, name: string, type: string, serverIPAddress?: string): DnsResolution {
|
||||
let resolution: DnsLookup | undefined;
|
||||
const server = serverIPAddress ?? this.stateValue.topologySnapshot.deviceConfigurations.get(nodeId)?.dnsServer;
|
||||
this.beginDnsResolution(nodeId, name, type, server, (result) => {
|
||||
resolution = result;
|
||||
});
|
||||
// Each query ends within five round trips, by its answer or its timeout.
|
||||
const window = (DNS_QUERY_TIMEOUT_ROUND_TRIPS + 1) * this.runtimeSpeed.roundTripTimeMilliseconds;
|
||||
while (resolution === undefined && this.stateValue.phase === 'running' && this.stateValue.pendingEvents.length > 0) {
|
||||
this.advanceUntil(() => resolution !== undefined, this.stateValue.currentTimeMilliseconds + window);
|
||||
}
|
||||
if (resolution === undefined || resolution.type === 'timedOut') return { type: 'timedOut' };
|
||||
return { type: 'answered', records: [...resolution.answers, ...resolution.authority] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolver.resolve as a chain of queries on the events: `done` with the
|
||||
* records for the name and type from the answer and authority sections of
|
||||
* the responses (and, for MX and NS, the address of the first), or
|
||||
* timedOut when a server did not answer.
|
||||
*/
|
||||
private beginDnsResolution(nodeId: Id, name: string, type: string, server: string | undefined, done: (resolution: DnsLookup) => void): void {
|
||||
const domainName = name.endsWith('.') ? name : `${name}.`;
|
||||
const collected: DnsMessageRecord[] = [];
|
||||
if (/^localhost\.?$/i.test(domainName)) {
|
||||
return { type: 'answered', records: [{ name: 'localhost.', type: 'A', ttl: 3600, data: LOCALHOST_IP_ADDRESS }] };
|
||||
done({ type: 'answered', answers: [{ name: 'localhost.', type: 'A', ttl: 3600, data: LOCALHOST_IP_ADDRESS }], authority: [] });
|
||||
return;
|
||||
}
|
||||
const answers: DnsMessageRecord[] = [];
|
||||
const authority: DnsMessageRecord[] = [];
|
||||
const queried = new Set<string>();
|
||||
let server: string | undefined = serverIPAddress ?? this.stateValue.topologySnapshot.deviceConfigurations.get(nodeId)?.dnsServer;
|
||||
while (server !== undefined && server !== '') {
|
||||
const response = this.queryDnsServer(nodeId, domainName, type, server);
|
||||
if (response === undefined) return { type: 'timedOut' };
|
||||
const relevant = (records: readonly DnsMessageRecord[], wanted: string, wantedType: string) => findDnsRecords(wanted, wantedType, records);
|
||||
collected.push(...relevant(response.answers, domainName, type), ...relevant(response.authority, domainName, type));
|
||||
const data = [...response.answers, ...response.authority, ...response.additional].filter(
|
||||
(record) => record.name.toLowerCase() === domainName.toLowerCase() && record.type === type,
|
||||
);
|
||||
if (data.length > 0) {
|
||||
if (type === 'MX' || type === 'NS') {
|
||||
collected.push(...relevant(response.answers, data[0]!.data, 'A'), ...relevant(response.authority, data[0]!.data, 'A'));
|
||||
}
|
||||
break;
|
||||
const ask = (serverToQuery: string | undefined): void => {
|
||||
if (serverToQuery === undefined || serverToQuery === '') {
|
||||
done({ type: 'answered', answers, authority });
|
||||
return;
|
||||
}
|
||||
const all = [...response.answers, ...response.authority, ...response.additional];
|
||||
const nameServer = findApplicableNameServerRecord(domainName, all);
|
||||
const next = nameServer === undefined ? undefined : findDnsRecords(nameServer.data, 'A', all)[0]?.data;
|
||||
// Java remembers only the servers it was referred to, not the first one.
|
||||
server = next !== undefined && !queried.has(next) ? next : undefined;
|
||||
if (next !== undefined) queried.add(next);
|
||||
}
|
||||
return { type: 'answered', records: collected };
|
||||
this.queryDnsServer(nodeId, domainName, type, serverToQuery, (response) => {
|
||||
if (response === undefined) {
|
||||
done({ type: 'timedOut' });
|
||||
return;
|
||||
}
|
||||
answers.push(...findDnsRecords(domainName, type, response.answers));
|
||||
authority.push(...findDnsRecords(domainName, type, response.authority));
|
||||
const data = findDnsRecords(domainName, type, [...response.answers, ...response.authority, ...response.additional]);
|
||||
if (data.length > 0) {
|
||||
if (type === 'MX' || type === 'NS') {
|
||||
answers.push(...findDnsRecords(data[0]!.data, 'A', response.answers));
|
||||
authority.push(...findDnsRecords(data[0]!.data, 'A', response.authority));
|
||||
}
|
||||
done({ type: 'answered', answers, authority });
|
||||
return;
|
||||
}
|
||||
const all = [...response.answers, ...response.authority, ...response.additional];
|
||||
const nameServer = findApplicableNameServerRecord(domainName, all);
|
||||
const next = nameServer === undefined ? undefined : findDnsRecords(nameServer.data, 'A', all)[0]?.data;
|
||||
// Java remembers only the servers it was referred to, not the first one.
|
||||
if (next === undefined || queried.has(next)) {
|
||||
done({ type: 'answered', answers, authority });
|
||||
return;
|
||||
}
|
||||
queried.add(next);
|
||||
ask(next);
|
||||
});
|
||||
};
|
||||
ask(server);
|
||||
}
|
||||
|
||||
/** Resolver.holeIPAdresse: the A record's address, undefined when there is none. */
|
||||
@@ -1331,10 +1447,16 @@ export class NetworkRuntimeEngine {
|
||||
return { type: 'resolved', address: exchange === undefined ? undefined : findDnsRecords(exchange.data, 'A', resolution.records)[0]?.data };
|
||||
}
|
||||
|
||||
/** DNSQueryAgent.query: one question from a fresh socket, the first datagram back within five round trips. */
|
||||
private queryDnsServer(nodeId: Id, domainName: string, type: string, serverIPAddress: string): DnsMessage | undefined {
|
||||
/**
|
||||
* DNSQueryAgent.query: one question from a fresh socket; `done` with the
|
||||
* first datagram back, or undefined after five round trips.
|
||||
*/
|
||||
private queryDnsServer(nodeId: Id, domainName: string, type: string, serverIPAddress: string, done: (response: DnsMessage | undefined) => void): void {
|
||||
const socketId = this.bindUdpSocket(nodeId, { remoteIPAddress: serverIPAddress, remotePort: DNS_PORT });
|
||||
if (socketId === undefined) return undefined;
|
||||
if (socketId === undefined) {
|
||||
done(undefined);
|
||||
return;
|
||||
}
|
||||
const query: DnsMessage = {
|
||||
// Java: (int) (Math.random() * 65536).
|
||||
id: Math.floor(this.dnsIdRandom.nextDouble() * 65_536),
|
||||
@@ -1347,9 +1469,28 @@ export class NetworkRuntimeEngine {
|
||||
};
|
||||
this.recordTrace({ nodeId, direction: 'outbound', layer: 'application', operation: 'sent', detail: `DNS query ${domainName} ${type}` });
|
||||
this.sendUdp(socketId, new TextEncoder().encode(encodeDnsMessage(query)));
|
||||
const received = this.receiveUdp(socketId, DNS_QUERY_TIMEOUT_ROUND_TRIPS * this.runtimeSpeed.roundTripTimeMilliseconds);
|
||||
if (!this.stateValue.socketsById.has(socketId)) {
|
||||
done(undefined);
|
||||
return;
|
||||
}
|
||||
// The wait begins when the send returns (empfangen after senden).
|
||||
this.dnsQueriesBySocketId.set(socketId, done);
|
||||
this.schedule(this.stateValue.currentTimeMilliseconds + DNS_QUERY_TIMEOUT_ROUND_TRIPS * this.runtimeSpeed.roundTripTimeMilliseconds, {
|
||||
kind: 'dnsQueryTimeout',
|
||||
socketId,
|
||||
});
|
||||
}
|
||||
|
||||
/** The query's answer came (or its time ran out): its socket closes, the resolution goes on. */
|
||||
private finishDnsQuery(socketId: Id, response: DnsMessage | undefined): void {
|
||||
const done = this.dnsQueriesBySocketId.get(socketId);
|
||||
if (done === undefined) return;
|
||||
this.dnsQueriesBySocketId.delete(socketId);
|
||||
this.stateValue.pendingEvents = this.stateValue.pendingEvents.filter(
|
||||
(event) => !(event.kind.kind === 'dnsQueryTimeout' && event.kind.socketId === socketId),
|
||||
);
|
||||
this.closeSocket(socketId);
|
||||
return received === undefined ? undefined : decodeDnsMessage(new TextDecoder().decode(received.datagram.payload));
|
||||
done(response);
|
||||
}
|
||||
|
||||
// ── TCP sockets ──────────────────────────────────────────────────────────
|
||||
@@ -4178,7 +4319,8 @@ function firewallAddressMatches(packetIPAddress: string, configuredIPAddress: st
|
||||
* (with the A record of an MX or NS target), else the name server of the
|
||||
* zone the name lies in, with its A record — in the answer section, as Java has it.
|
||||
*/
|
||||
function answerDnsQuestion(question: DnsQuestion, records: readonly DnsMessageRecord[]): DnsMessageRecord[] {
|
||||
/** DNSServer.answerWithLocalData: the records for the question, an MX's or NS's with its address. */
|
||||
function localDnsAnswers(question: DnsQuestion, records: readonly DnsMessageRecord[]): DnsMessageRecord[] {
|
||||
const answers: DnsMessageRecord[] = [];
|
||||
for (const record of findDnsRecords(question.name, question.type, records)) {
|
||||
answers.push(record);
|
||||
@@ -4187,7 +4329,11 @@ function answerDnsQuestion(question: DnsQuestion, records: readonly DnsMessageRe
|
||||
if (address !== undefined) answers.push(address);
|
||||
}
|
||||
}
|
||||
if (answers.length > 0) return answers;
|
||||
return answers;
|
||||
}
|
||||
|
||||
/** DNSServer.defineNameserverRecords: the NS record of the question's zone (or the root's) and its address. */
|
||||
function dnsReferral(question: DnsQuestion, records: readonly DnsMessageRecord[]): DnsMessageRecord[] {
|
||||
const nameServer = findApplicableNameServerRecord(question.name, records);
|
||||
if (nameServer === undefined) return [];
|
||||
const address = findDnsRecords(nameServer.data, 'A', records)[0];
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// DNS as Java FILIUS runs it. tools/java-oracle's DnsRun called a client's
|
||||
// resolver on dns-lan's network in Java FILIUS and wrote every frame each
|
||||
// card saw and what the call returned (dns-java-<story>.java.json). The
|
||||
// card saw and what the call returned (dns-java-<story>.java.json); on
|
||||
// dns-recursive's, where the server resolves recursively, likewise
|
||||
// (dns-recursive-java-<story>.java.json). The
|
||||
// engine makes the same call: the frames must come in the same order, with
|
||||
// the same content, at about the same distance from each other (javaRun.ts),
|
||||
// the call must return the same, after about as long.
|
||||
@@ -26,12 +28,43 @@ interface JavaRun {
|
||||
events: [number, string][];
|
||||
}
|
||||
|
||||
const STORIES: Record<string, (engine: NetworkRuntimeEngine) => DnsAddressResolution> = {
|
||||
address: (engine) => engine.resolveDnsAddress(CLIENT, 'web.filius.de'),
|
||||
mail: (engine) => engine.resolveDnsMailServer(CLIENT, 'filius.de'),
|
||||
referral: (engine) => engine.resolveDnsAddress(CLIENT, 'www.schule.de'),
|
||||
unknown: (engine) => engine.resolveDnsAddress(CLIENT, 'x.unbekannt.org'),
|
||||
noServer: (engine) => engine.resolveDnsAddress(LOST_CLIENT, 'web.filius.de'),
|
||||
type Story = (engine: NetworkRuntimeEngine) => DnsAddressResolution;
|
||||
|
||||
/** Per scenario the stories DnsRun played on it. */
|
||||
const RUNS: Record<string, Record<string, Story>> = {
|
||||
'dns-lan': {
|
||||
address: (engine) => engine.resolveDnsAddress(CLIENT, 'web.filius.de'),
|
||||
mail: (engine) => engine.resolveDnsMailServer(CLIENT, 'filius.de'),
|
||||
referral: (engine) => engine.resolveDnsAddress(CLIENT, 'www.schule.de'),
|
||||
unknown: (engine) => engine.resolveDnsAddress(CLIENT, 'x.unbekannt.org'),
|
||||
noServer: (engine) => engine.resolveDnsAddress(LOST_CLIENT, 'web.filius.de'),
|
||||
},
|
||||
'dns-recursive': {
|
||||
address: (engine) => engine.resolveDnsAddress(CLIENT, 'web.filius.de'),
|
||||
referral: (engine) => engine.resolveDnsAddress(CLIENT, 'www.schule.de'),
|
||||
unknown: (engine) => engine.resolveDnsAddress(CLIENT, 'x.unbekannt.org'),
|
||||
unknownInZone: (engine) => engine.resolveDnsAddress(CLIENT, 'nix.schule.de'),
|
||||
unknownInOwnZone: (engine) => engine.resolveDnsAddress(CLIENT, 'nix.filius.de'),
|
||||
},
|
||||
};
|
||||
|
||||
/** The recording of a story: dns-java-<story> for dns-lan, <scenario>-java-<story> else. */
|
||||
function recording(scenarioName: string, story: string): string {
|
||||
return scenarioName === 'dns-lan' ? `dns-java-${story}.java.json` : `${scenarioName}-java-${story}.java.json`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where Java errs, per scenario and story: compared up to the frames Java
|
||||
* sends before its error shows, not the result (engine.test.ts has the rest).
|
||||
*
|
||||
* dns-recursive/unknownInOwnZone: the server's own zone names itself as its
|
||||
* name server, and Java's server asks it — itself, which asks itself again,
|
||||
* without end; the client times out, then every level falls back to .11. The
|
||||
* engine asks neither itself nor, here, anyone but .11. Compared up to the
|
||||
* client's query.
|
||||
*/
|
||||
const JAVA_UNTIL: Record<string, (java: JavaRun) => number> = {
|
||||
'dns-recursive/unknownInOwnZone': (java) => Math.min(...Object.values(java.frames).flat().filter((frame) => frame.destinationPort === 53).map((frame) => frame.time)) + 1,
|
||||
};
|
||||
|
||||
/** Java's words for the result: the address, null, or the exception. */
|
||||
@@ -53,35 +86,43 @@ function normalized(frames: readonly SeenFrame[]): SeenFrame[] {
|
||||
}));
|
||||
}
|
||||
|
||||
/** Stories the engine does not play like Java yet, with the reason; each must still differ from Java. */
|
||||
const NOT_YET_LIKE_JAVA: Record<string, string> = {};
|
||||
|
||||
describe('DNS as Java FILIUS runs it', () => {
|
||||
const scenario = JSON.parse(readFileSync(join(scenarioDirectory, 'dns-lan.scenario.json'), 'utf8')) as Scenario;
|
||||
it('cuts only where Java goes on with its error', () => {
|
||||
for (const [key, until] of Object.entries(JAVA_UNTIL)) {
|
||||
const [scenarioName, story] = key.split('/') as [string, string];
|
||||
const java = JSON.parse(readFileSync(join(scenarioDirectory, recording(scenarioName, story)), 'utf8')) as JavaRun;
|
||||
expect(Object.values(java.frames).flat().some((frame) => frame.time >= until(java)), key).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
for (const [name, story] of Object.entries(STORIES)) {
|
||||
it(name, () => {
|
||||
const java = JSON.parse(readFileSync(join(scenarioDirectory, `dns-java-${name}.java.json`), 'utf8')) as JavaRun;
|
||||
const { engine, frames } = watchedEngine(scenario);
|
||||
engine.handle({ type: 'advance', toMilliseconds: 500 });
|
||||
const called = engine.state.currentTimeMilliseconds;
|
||||
const result = story(engine);
|
||||
const returned = engine.state.currentTimeMilliseconds;
|
||||
for (const [scenarioName, stories] of Object.entries(RUNS)) {
|
||||
const scenario = JSON.parse(readFileSync(join(scenarioDirectory, `${scenarioName}.scenario.json`), 'utf8')) as Scenario;
|
||||
for (const [name, story] of Object.entries(stories)) {
|
||||
it(`${scenarioName}: ${name}`, () => {
|
||||
const java = JSON.parse(readFileSync(join(scenarioDirectory, recording(scenarioName, name)), 'utf8')) as JavaRun;
|
||||
const { engine, frames } = watchedEngine(scenario);
|
||||
engine.handle({ type: 'advance', toMilliseconds: 500 });
|
||||
const called = engine.state.currentTimeMilliseconds;
|
||||
const result = story(engine);
|
||||
const returned = engine.state.currentTimeMilliseconds;
|
||||
|
||||
const [[javaCalled], [javaReturned, javaOutcome]] = java.events as [[number, string], [number, string]];
|
||||
expect(`${javaOutcome.split(' ').slice(-2).join(' ')}`).toBe(javaResult(result).split(' ').slice(-2).join(' '));
|
||||
|
||||
const expected = gaps(normalized(javaFrames(java.frames)));
|
||||
const actual = gaps(normalized(frames()));
|
||||
if (name in NOT_YET_LIKE_JAVA) {
|
||||
expect(JSON.stringify(actual), NOT_YET_LIKE_JAVA[name]).not.toBe(JSON.stringify(expected));
|
||||
return;
|
||||
}
|
||||
expect(Math.abs(returned - called - (javaReturned - javaCalled)), 'duration of the call').toBeLessThanOrEqual(GAP_TOLERANCE_MILLISECONDS);
|
||||
for (let index = 0; index < Math.max(expected.length, actual.length); index += 1) {
|
||||
expect(actual[index]?.text, `frame #${index + 1}`).toBe(expected[index]?.text);
|
||||
expect(Math.abs(actual[index]!.gap - expected[index]!.gap), `gap before frame #${index + 1}`).toBeLessThanOrEqual(GAP_TOLERANCE_MILLISECONDS);
|
||||
}
|
||||
});
|
||||
let expected = gaps(normalized(javaFrames(java.frames)));
|
||||
let actual = gaps(normalized(frames()));
|
||||
const until = JAVA_UNTIL[`${scenarioName}/${name}`]?.(java);
|
||||
if (until !== undefined) {
|
||||
const count = javaFrames(java.frames).filter((frame) => frame.time < until).length;
|
||||
expected = expected.slice(0, count);
|
||||
actual = actual.slice(0, count);
|
||||
} else {
|
||||
const [[javaCalled], [javaReturned, javaOutcome]] = java.events as [[number, string], [number, string]];
|
||||
expect(`${javaOutcome.split(' ').slice(-2).join(' ')}`).toBe(javaResult(result).split(' ').slice(-2).join(' '));
|
||||
expect(Math.abs(returned - called - (javaReturned - javaCalled)), 'duration of the call').toBeLessThanOrEqual(GAP_TOLERANCE_MILLISECONDS);
|
||||
}
|
||||
for (let index = 0; index < Math.max(expected.length, actual.length); index += 1) {
|
||||
expect(actual[index]?.text, `frame #${index + 1}`).toBe(expected[index]?.text);
|
||||
expect(Math.abs(actual[index]!.gap - expected[index]!.gap), `gap before frame #${index + 1}`).toBeLessThanOrEqual(GAP_TOLERANCE_MILLISECONDS);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
decodeDnsMessage,
|
||||
encodeDnsMessage,
|
||||
findApplicableNameServerRecord,
|
||||
NotPortedError,
|
||||
DHCP_DYNAMIC_LEASE_LIFETIME_MILLISECONDS,
|
||||
deviceConfiguration,
|
||||
dhcpServerConfiguration,
|
||||
@@ -1668,6 +1667,7 @@ describe('DNS', () => {
|
||||
expect(engine.resolveDnsAddress(client, 'web.filius.de')).toEqual({ type: 'resolved', address: '10.30.0.9' });
|
||||
// ARP there and back, then the query and its answer: four cables each way through the switch.
|
||||
expect(engine.state.currentTimeMilliseconds - called).toBe(4 * 110);
|
||||
expect(engine.state.pendingEvents.some((event) => event.kind.kind === 'dnsQueryTimeout')).toBe(false);
|
||||
});
|
||||
|
||||
it("answers behind a datagram of its own whose ARP fails: Java's UDP thread sends one after the other", () => {
|
||||
@@ -1722,8 +1722,76 @@ describe('DNS', () => {
|
||||
expect([...engine.state.socketsById.values()].filter((socket) => socket.nodeId === client)).toEqual([]);
|
||||
});
|
||||
|
||||
it('does not answer recursively yet', () => {
|
||||
expect(() => lan({ '10.30.0.2': [] }, true)).toThrow(NotPortedError);
|
||||
it('answers every question of a query, one after the other', () => {
|
||||
const { engine, client } = lan({ '10.30.0.2': ['web.filius.de A 10.30.0.9', 'mail.filius.de A 10.30.0.10'] });
|
||||
const socket = engine.bindUdpSocket(client, { remoteIPAddress: '10.30.0.2', remotePort: 53 })!;
|
||||
const query = encodeDnsMessage({
|
||||
id: 9,
|
||||
queryResponse: 0,
|
||||
responseCode: 0,
|
||||
questions: [
|
||||
{ name: 'web.filius.de.', type: 'A', questionClass: 'IN' },
|
||||
{ name: 'mail.filius.de.', type: 'A', questionClass: 'IN' },
|
||||
],
|
||||
answers: [],
|
||||
authority: [],
|
||||
additional: [],
|
||||
});
|
||||
engine.sendUdp(socket, new TextEncoder().encode(query));
|
||||
const response = decodeDnsMessage(new TextDecoder().decode(engine.receiveUdp(socket, 10_000)!.datagram.payload));
|
||||
|
||||
expect(response.answers.map((record) => record.data)).toEqual(['10.30.0.9', '10.30.0.10']);
|
||||
});
|
||||
|
||||
it('forgets the DNS queries that wait on stop', () => {
|
||||
const records = { '10.30.0.2': ['schule.de NS ns.schule.de.', 'ns.schule.de A 10.30.0.3'] };
|
||||
const { engine, client } = lan(records, true);
|
||||
// The client gives up; the server still waits for .3, which runs no DNS server, when the simulation stops.
|
||||
expect(engine.resolveDnsAddress(client, 'www.schule.de')).toEqual({ type: 'timedOut' });
|
||||
const snapshot = engine.state.topologySnapshot;
|
||||
engine.handle({ type: 'stop' });
|
||||
engine.handle({ type: 'start', snapshot, seed: 14, initialTimeMilliseconds: 0 });
|
||||
// The sockets are numbered anew: the second takes the number of the server's query socket before.
|
||||
engine.bindUdpSocket(client, { localPort: 7000 });
|
||||
const receiver = engine.bindUdpSocket(client, { localPort: 7001 })!;
|
||||
const sender = engine.bindUdpSocket(uuid(0x1906), { remoteIPAddress: '10.30.0.1', remotePort: 7001 })!;
|
||||
engine.sendUdp(sender, new TextEncoder().encode('still mine'));
|
||||
|
||||
expect(new TextDecoder().decode(engine.receiveUdp(receiver, 10_000)?.datagram.payload)).toBe('still mine');
|
||||
});
|
||||
|
||||
it('asks on for a name it does not know, and answers with what it learns', () => {
|
||||
const { engine, client } = lan(
|
||||
{ '10.30.0.2': ['schule.de NS ns.schule.de.', 'ns.schule.de A 10.30.0.3'], '10.30.0.3': ['www.schule.de A 10.30.0.40'] },
|
||||
true,
|
||||
);
|
||||
const called = engine.state.currentTimeMilliseconds;
|
||||
expect(engine.resolveDnsAddress(client, 'www.schule.de')).toEqual({ type: 'resolved', address: '10.30.0.40' });
|
||||
// ARP, query and answer between client and server, ARP, query and answer between server and .3,
|
||||
// and the answer to the client: eight crossings of the switch, 110 ms each.
|
||||
expect(queries(engine)).toEqual(['DNS query www.schule.de. A', 'DNS query www.schule.de. A']);
|
||||
expect(engine.state.currentTimeMilliseconds - called).toBe(8 * 110);
|
||||
});
|
||||
|
||||
it('asks neither itself as the name server of its own zone nor as its own DNS server: it refers (Java asks itself without end)', () => {
|
||||
const { engine, client } = lan({ '10.30.0.2': ['filius.de NS ns.filius.de.', 'ns.filius.de A 10.30.0.2'] }, true);
|
||||
const called = engine.state.currentTimeMilliseconds;
|
||||
expect(engine.resolveDnsAddress(client, 'nix.filius.de')).toEqual({ type: 'resolved', address: undefined });
|
||||
// The client follows the referral to the server once more: two exchanges, the first with ARP.
|
||||
expect(queries(engine)).toEqual(['DNS query nix.filius.de. A', 'DNS query nix.filius.de. A']);
|
||||
expect(engine.state.currentTimeMilliseconds - called).toBe(4 * 110 + 2 * 110);
|
||||
});
|
||||
|
||||
it('gives up on a name server that does not answer after five round trips, when the client has given up too', () => {
|
||||
const { engine, client } = lan({ '10.30.0.2': ['schule.de NS ns.schule.de.', 'ns.schule.de A 10.30.0.3'] }, true);
|
||||
const called = engine.state.currentTimeMilliseconds;
|
||||
// .3 runs no DNS server.
|
||||
expect(engine.resolveDnsAddress(client, 'www.schule.de')).toEqual({ type: 'timedOut' });
|
||||
expect(engine.state.currentTimeMilliseconds - called).toBe(5 * 2_750);
|
||||
// The server's own wait ends later, then it refers the client, which no longer listens.
|
||||
engine.handle({ type: 'advance', toMilliseconds: engine.state.currentTimeMilliseconds + 5 * 2_750 });
|
||||
expect(engine.state.packetTraces.filter((trace) => trace.detail === 'DNS response')).toHaveLength(1);
|
||||
expect([...engine.state.socketsById.values()].filter((socket) => socket.nodeId === client)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"frames": {
|
||||
"00000000-0000-0000-0000-000000000101": [
|
||||
{
|
||||
"time": 727,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 963,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 965,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 60119,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=56151 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nweb.filius.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1196,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 60119,
|
||||
"payload": "ID=56151 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=1 NSCOUNT=0 ARCOUNT=0 \nweb.filius.de. A 3600 192.168.0.30\n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000102": [
|
||||
{
|
||||
"time": 850,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 851,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 1079,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 60119,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=56151 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nweb.filius.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1084,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 60119,
|
||||
"payload": "ID=56151 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=1 NSCOUNT=0 ARCOUNT=0 \nweb.filius.de. A 3600 192.168.0.30\n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000103": [
|
||||
{
|
||||
"time": 850,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000104": [
|
||||
{
|
||||
"time": 850,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
}
|
||||
]
|
||||
},
|
||||
"events": [
|
||||
[
|
||||
717,
|
||||
"holeIPAdresse web.filius.de called"
|
||||
],
|
||||
[
|
||||
1197,
|
||||
"holeIPAdresse web.filius.de returned 192.168.0.30"
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
{
|
||||
"frames": {
|
||||
"00000000-0000-0000-0000-000000000101": [
|
||||
{
|
||||
"time": 727,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 961,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 963,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 51710,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=39889 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nwww.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1196,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1645,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 51710,
|
||||
"payload": "ID=39889 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=1 NSCOUNT=0 ARCOUNT=0 \nwww.schule.de. A 3600 192.168.0.40\n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000102": [
|
||||
{
|
||||
"time": 848,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 849,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 1076,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 51710,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=39889 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nwww.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1085,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1308,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1308,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 52403,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=12187 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nwww.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1533,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 52403,
|
||||
"payload": "ID=12187 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=1 NSCOUNT=0 ARCOUNT=0 \nwww.schule.de. A 3600 192.168.0.40\n"
|
||||
},
|
||||
{
|
||||
"time": 1534,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 51710,
|
||||
"payload": "ID=39889 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=1 NSCOUNT=0 ARCOUNT=0 \nwww.schule.de. A 3600 192.168.0.40\n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000103": [
|
||||
{
|
||||
"time": 848,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1196,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1196,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1420,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 52403,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=12187 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nwww.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1421,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 52403,
|
||||
"payload": "ID=12187 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=1 NSCOUNT=0 ARCOUNT=0 \nwww.schule.de. A 3600 192.168.0.40\n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000104": [
|
||||
{
|
||||
"time": 848,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1196,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
}
|
||||
]
|
||||
},
|
||||
"events": [
|
||||
[
|
||||
716,
|
||||
"holeIPAdresse www.schule.de called"
|
||||
],
|
||||
[
|
||||
1645,
|
||||
"holeIPAdresse www.schule.de returned 192.168.0.40"
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
{
|
||||
"frames": {
|
||||
"00000000-0000-0000-0000-000000000101": [
|
||||
{
|
||||
"time": 729,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 963,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 965,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 59169,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=28614 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nx.unbekannt.org. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1198,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1645,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 59169,
|
||||
"payload": "ID=28614 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000102": [
|
||||
{
|
||||
"time": 850,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 851,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 1078,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 59169,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=28614 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nx.unbekannt.org. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1087,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1309,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1309,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 61822,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=44286 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nx.unbekannt.org. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1533,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 61822,
|
||||
"payload": "ID=44286 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
},
|
||||
{
|
||||
"time": 1534,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 59169,
|
||||
"payload": "ID=28614 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000103": [
|
||||
{
|
||||
"time": 850,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1198,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1198,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1421,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 61822,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=44286 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nx.unbekannt.org. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1422,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 61822,
|
||||
"payload": "ID=44286 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000104": [
|
||||
{
|
||||
"time": 850,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1198,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
}
|
||||
]
|
||||
},
|
||||
"events": [
|
||||
[
|
||||
719,
|
||||
"holeIPAdresse x.unbekannt.org called"
|
||||
],
|
||||
[
|
||||
1646,
|
||||
"holeIPAdresse x.unbekannt.org returned null"
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
{
|
||||
"frames": {
|
||||
"00000000-0000-0000-0000-000000000101": [
|
||||
{
|
||||
"time": 735,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 969,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 972,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 51431,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=22768 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.filius.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 15066,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000102": [
|
||||
{
|
||||
"time": 857,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 858,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 1085,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 51431,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=22768 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.filius.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 14910,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 15276,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 15278,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 52690,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=54566 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.filius.de. A IN\n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000103": [
|
||||
{
|
||||
"time": 856,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 15066,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 15073,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 15423,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 52690,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=54566 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.filius.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 15434,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 52690,
|
||||
"payload": "ID=54566 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000104": [
|
||||
{
|
||||
"time": 856,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 15059,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
}
|
||||
]
|
||||
},
|
||||
"events": [
|
||||
[
|
||||
724,
|
||||
"holeIPAdresse nix.filius.de called"
|
||||
],
|
||||
[
|
||||
14485,
|
||||
"holeIPAdresse nix.filius.de failed TimeOutException"
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
{
|
||||
"frames": {
|
||||
"00000000-0000-0000-0000-000000000101": [
|
||||
{
|
||||
"time": 750,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 984,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 987,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 64317,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=59872 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1221,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1897,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 64317,
|
||||
"payload": "ID=59872 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=2 NSCOUNT=0 ARCOUNT=0 \nschule.de. NS 3600 ns.schule.de.\nns.schule.de. A 3600 192.168.0.11\n"
|
||||
},
|
||||
{
|
||||
"time": 1899,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 2121,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 2121,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53912,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=65453 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 2345,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 53912,
|
||||
"payload": "ID=65453 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000102": [
|
||||
{
|
||||
"time": 871,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 873,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 1100,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 64317,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=59872 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1109,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1333,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1333,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 50451,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=16993 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1558,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 50451,
|
||||
"payload": "ID=16993 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
},
|
||||
{
|
||||
"time": 1558,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 60605,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=33013 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1783,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 60605,
|
||||
"payload": "ID=33013 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
},
|
||||
{
|
||||
"time": 1786,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 64317,
|
||||
"payload": "ID=59872 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=2 NSCOUNT=0 ARCOUNT=0 \nschule.de. NS 3600 ns.schule.de.\nns.schule.de. A 3600 192.168.0.11\n"
|
||||
},
|
||||
{
|
||||
"time": 2010,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.11"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000103": [
|
||||
{
|
||||
"time": 871,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1221,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 1221,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1445,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 50451,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=16993 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1446,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 50451,
|
||||
"payload": "ID=16993 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
},
|
||||
{
|
||||
"time": 1670,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.10",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 60605,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=33013 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 1671,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:12",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.10",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 60605,
|
||||
"payload": "ID=33013 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
},
|
||||
{
|
||||
"time": 2010,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 2010,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"arp": 2,
|
||||
"senderIp": "192.168.0.11",
|
||||
"targetIp": "192.168.0.20"
|
||||
},
|
||||
{
|
||||
"time": 2233,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "02:00:00:00:01:13",
|
||||
"sourceIp": "192.168.0.20",
|
||||
"destinationIp": "192.168.0.11",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53912,
|
||||
"destinationPort": 53,
|
||||
"payload": "ID=65453 QR=0 RCODE=0 QDCOUNT=1 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \nnix.schule.de. A IN\n"
|
||||
},
|
||||
{
|
||||
"time": 2234,
|
||||
"source": "02:00:00:00:01:13",
|
||||
"destination": "02:00:00:00:01:11",
|
||||
"sourceIp": "192.168.0.11",
|
||||
"destinationIp": "192.168.0.20",
|
||||
"protocol": 17,
|
||||
"sourcePort": 53,
|
||||
"destinationPort": 53912,
|
||||
"payload": "ID=65453 QR=1 RCODE=0 QDCOUNT=0 ANCOUNT=0 NSCOUNT=0 ARCOUNT=0 \n"
|
||||
}
|
||||
],
|
||||
"00000000-0000-0000-0000-000000000104": [
|
||||
{
|
||||
"time": 871,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.10"
|
||||
},
|
||||
{
|
||||
"time": 1221,
|
||||
"source": "02:00:00:00:01:12",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.10",
|
||||
"targetIp": "192.168.0.11"
|
||||
},
|
||||
{
|
||||
"time": 2010,
|
||||
"source": "02:00:00:00:01:11",
|
||||
"destination": "FF:FF:FF:FF:FF:FF",
|
||||
"arp": 1,
|
||||
"senderIp": "192.168.0.20",
|
||||
"targetIp": "192.168.0.11"
|
||||
}
|
||||
]
|
||||
},
|
||||
"events": [
|
||||
[
|
||||
738,
|
||||
"holeIPAdresse nix.schule.de called"
|
||||
],
|
||||
[
|
||||
2345,
|
||||
"holeIPAdresse nix.schule.de returned null"
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
{
|
||||
"seed": 51,
|
||||
"nodes": [
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000101",
|
||||
"kind": "pc",
|
||||
"ports": [
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000111",
|
||||
"label": "eth0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000102",
|
||||
"kind": "pc",
|
||||
"ports": [
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000112",
|
||||
"label": "eth0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000103",
|
||||
"kind": "pc",
|
||||
"ports": [
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000113",
|
||||
"label": "eth0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000104",
|
||||
"kind": "pc",
|
||||
"ports": [
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000114",
|
||||
"label": "eth0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000105",
|
||||
"kind": "networkSwitch",
|
||||
"ports": [
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000121",
|
||||
"label": "sw1"
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000122",
|
||||
"label": "sw2"
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000123",
|
||||
"label": "sw3"
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000124",
|
||||
"label": "sw4"
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-000000000125",
|
||||
"label": "sw5"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-00000000F001",
|
||||
"sourceNode": "00000000-0000-0000-0000-000000000101",
|
||||
"sourcePort": "00000000-0000-0000-0000-000000000111",
|
||||
"targetNode": "00000000-0000-0000-0000-000000000105",
|
||||
"targetPort": "00000000-0000-0000-0000-000000000121"
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-00000000F002",
|
||||
"sourceNode": "00000000-0000-0000-0000-000000000102",
|
||||
"sourcePort": "00000000-0000-0000-0000-000000000112",
|
||||
"targetNode": "00000000-0000-0000-0000-000000000105",
|
||||
"targetPort": "00000000-0000-0000-0000-000000000122"
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-00000000F003",
|
||||
"sourceNode": "00000000-0000-0000-0000-000000000103",
|
||||
"sourcePort": "00000000-0000-0000-0000-000000000113",
|
||||
"targetNode": "00000000-0000-0000-0000-000000000105",
|
||||
"targetPort": "00000000-0000-0000-0000-000000000123"
|
||||
},
|
||||
{
|
||||
"id": "00000000-0000-0000-0000-00000000F004",
|
||||
"sourceNode": "00000000-0000-0000-0000-000000000104",
|
||||
"sourcePort": "00000000-0000-0000-0000-000000000114",
|
||||
"targetNode": "00000000-0000-0000-0000-000000000105",
|
||||
"targetPort": "00000000-0000-0000-0000-000000000124"
|
||||
}
|
||||
],
|
||||
"devices": {
|
||||
"00000000-0000-0000-0000-000000000101": {
|
||||
"ip": "192.168.0.20",
|
||||
"mask": "255.255.255.0",
|
||||
"dns": "192.168.0.10"
|
||||
},
|
||||
"00000000-0000-0000-0000-000000000102": {
|
||||
"ip": "192.168.0.10",
|
||||
"mask": "255.255.255.0",
|
||||
"dns": "192.168.0.11"
|
||||
},
|
||||
"00000000-0000-0000-0000-000000000103": {
|
||||
"ip": "192.168.0.11",
|
||||
"mask": "255.255.255.0"
|
||||
},
|
||||
"00000000-0000-0000-0000-000000000104": {
|
||||
"ip": "192.168.0.21",
|
||||
"mask": "255.255.255.0",
|
||||
"dns": "192.168.0.99"
|
||||
}
|
||||
},
|
||||
"dnsServers": {
|
||||
"00000000-0000-0000-0000-000000000102": {
|
||||
"records": [
|
||||
"web.filius.de A 192.168.0.30",
|
||||
"filius.de MX mail.filius.de",
|
||||
"mail.filius.de A 192.168.0.31",
|
||||
"schule.de NS ns.schule.de",
|
||||
"ns.schule.de A 192.168.0.11",
|
||||
"filius.de NS ns.filius.de",
|
||||
"ns.filius.de A 192.168.0.10"
|
||||
],
|
||||
"recursive": true
|
||||
},
|
||||
"00000000-0000-0000-0000-000000000103": {
|
||||
"records": [
|
||||
"www.schule.de A 192.168.0.40"
|
||||
]
|
||||
}
|
||||
},
|
||||
"steps": []
|
||||
}
|
||||
@@ -267,6 +267,26 @@ Nameservers im Antwortteil. Der Resolver folgt dem Verweis selbst
|
||||
(iterativ) und wartet je Anfrage 5 RTT (13750 ms bei 90 %); ohne Server
|
||||
fragt ARP zweimal im Abstand einer RTT, dann scheitert der Aufruf.
|
||||
|
||||
Auf dem Netz von `dns-recursive` (sonst wie `dns-lan`) löst 192.168.0.10
|
||||
rekursiv auf, sein eigener DNS-Server ist .11, und er führt die Zone
|
||||
`filius.de` mit sich selbst als Nameserver. Abläufe
|
||||
(`dns-recursive-java-<ablauf>.java.json`): `address` (eigener Eintrag),
|
||||
`referral` (`www.schule.de` über den NS von `schule.de`), `unknown`
|
||||
(`x.unbekannt.org`), `unknownInZone` (`nix.schule.de`, das .11 nicht kennt),
|
||||
`unknownInOwnZone` (`nix.filius.de`).
|
||||
|
||||
Gemessen (30.09.2026): Findet der Server nichts in seinen Einträgen, fragt
|
||||
er selbst, mit seinem eigenen Resolver (iterativ, 5 RTT je Anfrage): zuerst
|
||||
den Nameserver der längsten passenden Zone aus seinen Einträgen, bringt das
|
||||
nichts, seinen eigenen DNS-Server (nur wenn das nicht er selbst ist). Die
|
||||
Antwort an den Client enthält, was er gelernt hat, sonst den Verweis (NS und
|
||||
A). Bei `unknownInZone` fragt er .11 also zweimal. Bei `unknownInOwnZone`
|
||||
fragt er als Nameserver der Zone sich selbst, ohne Ende: Jede Ebene erzeugt
|
||||
die nächste (ohne Frames, die Anfragen bleiben im Rechner), der Client gibt
|
||||
nach 5 RTT auf, dann laufen die inneren Ebenen nacheinander ab und fragen
|
||||
ersatzweise .11, eine Anfrage alle 55 ms. Ein Java-Fehler; der Kern fragt
|
||||
sich weder als Nameserver noch als DNS-Server selbst.
|
||||
|
||||
## ARP zur Laufzeit
|
||||
|
||||
`ArpRun` sendet auf dem Netz von `arp-routed` (10.0.0.2 über einen Switch am
|
||||
|
||||
@@ -141,6 +141,16 @@ for run in address:5000 mail:5000 referral:5000 unknown:5000 noServer:22000; do
|
||||
"$root/packages/core/test/scenarios/dns-lan.scenario.json" "$story" "${run##*:}" "$output" 2>&1 | grep -v 'logback\|^$' || true
|
||||
test -s "$output"
|
||||
done
|
||||
# The same with a recursive server (dns-recursive). unknownInOwnZone asks
|
||||
# itself without end: a short run, and a bounded heap.
|
||||
for run in address:5000 referral:5000 unknown:5000 unknownInZone:5000 unknownInOwnZone:15500; do
|
||||
story="${run%%:*}"
|
||||
output="$root/packages/core/test/scenarios/dns-recursive-java-$story.java.json"
|
||||
rm -f "$output"
|
||||
HOME="$here/.build/home" xvfb-run -a java -Xmx1g -cp "$classpath:$here/.build" DnsRun \
|
||||
"$root/packages/core/test/scenarios/dns-recursive.scenario.json" "$story" "${run##*:}" "$output" 2>&1 | grep -v 'logback\|^$' || true
|
||||
test -s "$output"
|
||||
done
|
||||
|
||||
# ARP as Java FILIUS runs it: sends to addresses no card has on arp-routed's
|
||||
# network (ArpRun); frames and the calls, for the core's ARP comparison.
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
// referral 192.168.0.20 asks for www.schule.de (A), referred to .11
|
||||
// unknown 192.168.0.20 asks for x.unbekannt.org, which no one knows
|
||||
// noServer 192.168.0.21 asks its DNS server 192.168.0.99, which is not there
|
||||
//
|
||||
// On dns-recursive's network .10 resolves recursively, its own DNS server is
|
||||
// .11, and it holds filius.de's NS record, which names itself. There also:
|
||||
// unknownInZone 192.168.0.20 asks for nix.schule.de, which .11 does not know
|
||||
// unknownInOwnZone 192.168.0.20 asks for nix.filius.de, in .10's own zone
|
||||
|
||||
import filius.software.dns.Resolver;
|
||||
|
||||
@@ -62,6 +67,12 @@ public class DnsRun {
|
||||
case "unknown":
|
||||
call("holeIPAdresse x.unbekannt.org", () -> client.holeIPAdresse("x.unbekannt.org"));
|
||||
break;
|
||||
case "unknownInZone":
|
||||
call("holeIPAdresse nix.schule.de", () -> client.holeIPAdresse("nix.schule.de"));
|
||||
break;
|
||||
case "unknownInOwnZone":
|
||||
call("holeIPAdresse nix.filius.de", () -> client.holeIPAdresse("nix.filius.de"));
|
||||
break;
|
||||
case "noServer":
|
||||
Resolver lost = network.system(LOST_CLIENT).holeDNSClient();
|
||||
call("holeIPAdresse web.filius.de", () -> lost.holeIPAdresse("web.filius.de"));
|
||||
|
||||
Reference in New Issue
Block a user