Public
Edited
Dec 18, 2023
Fork of DNS graph
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
`digraph "${start}" {
node [fontname="sans-serif" fontsize=10 shape=box]
edge [fontname="sans-serif" fontsize=8]
${d3
.filter(edges, (e) => e.type !== "mx" || e.data !== undefined) // missing mx is fine to hide
.map((e) => `"${e.name}" -> "${e.data}" [label=" ${rr(e.type)}"]`)
.join("\n")}
}`
Insert cell
chart = {
const links = data.links.map((d) => Object.create(d));
const nodes = data.nodes.map((d) => Object.create(d));

const simulation = d3
.forceSimulation(nodes)
.force(
"link",
d3.forceLink(links).id((d) => d.id)
)
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));

const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

const link = svg
.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", (d) => Math.sqrt(d.value));

const node = svg
.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", (d) => d.r || 5)
.attr("fill", (d) => d.color || color(d))
.call(drag(simulation))
.on("mouseenter", (evt, d) => {
link
.attr("display", "none")
.filter((l) => l.source.id === d.id || l.target.id === d.id)
.attr("display", "block");
})
.on("mouseleave", (evt) => {
link.attr("display", "block");
});

node.append("title").text((d) => d.id);

simulation.on("tick", () => {
link
.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);

node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
});

invalidation.then(() => simulation.stop());

return svg.node();
}
Insert cell
data = {
const links = [],
nodes = [];

let isRoot;

[...edges].map(({ name, type, TTL, data }) => {
isRoot = name.startsWith(start);

if (nodes.filter((d) => d.id === name).length === 0)
nodes.push({
id: name,
group: type,
r: isRoot ? 10 : 5,
color: isRoot ? "red" : undefined
});

if (nodes.filter((d) => d.id === data).length === 0)
nodes.push({
id: data,
group: type,
r: isRoot ? 10 : 5,
color: isRoot ? "gray" : undefined
});

links.push({ source: name, target: data, value: isRoot ? 1 : 0.1 });

// links.push({ source: data, target: name, value: 0.5 });
});

return { links, nodes };
}
Insert cell
edges = {
const seen = new Set();
const queue = [start];
const answers = new d3.InternSet([], ({ TTL, ...rest }) =>
JSON.stringify(rest)
); // ignore TTL differences
while (queue.length) {
const name = queue.shift();
if (seen.has(name)) continue;
seen.add(name);
for (const type of types(name)) {
const response = await query({ name, type });
for (const a of response.Answer ?? [{ name, type }]) {
answers.add(a);
switch (a.type) {
case 1: // a
case 28: { // aaaa
if (allowed.includes("ptr")) {
const data = invert(a.data);
answers.add({ name: a.data, type: "ptr?", data });
queue.push(data);
}
break;
}
case 5: // cname
case 12: // ptr
queue.push(a.data);
break;
case 15: {
// mx
const data = a.data.split(" ", 2)[1];
answers.add({ name: a.data, type: "mx?", data });
queue.push(data);
break;
}
}
}
yield answers;
}
}
}
Insert cell
types = (name) =>
d3.intersection(
allowed,
name.endsWith(".in-addr.arpa.") || name.endsWith("ip6.arpa.")
? ["ptr"]
: ["a", "aaaa", "mx"]
)
Insert cell
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
rr = (type) => ({ 1: "a", 5: "cname", 12: "ptr", 15: "mx", 28: "aaaa" }[type] ?? type)
Insert cell
query = (params) =>
fetch(`https://dns.google/resolve?${new URLSearchParams(params)}`).then(
(res) => res.ok ? res.json() : {}
)
Insert cell
invert = (ip) => {
const ipv4 = ip.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)\.?$/);
if (ipv4) {
return `${ipv4.slice(1).reverse().join(".")}.in-addr.arpa.`;
} else if (ip.match(/^[0-9a-f:]+$/)) {
return `${ip
.match(/[0-9a-f]/g)
.reverse()
.join(".")}.ip6.arpa.`;
}
}
Insert cell
d3 = require("d3@6")
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more