Compare commits

..

4 Commits

Author SHA1 Message Date
2d477bb77e Add content verification
Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2021-08-21 11:22:40 +02:00
4b117a4743 Add support for longer records
This makes it possible to handle more content in a single round.

Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2021-08-21 11:17:50 +02:00
74619915ca Add default title to HTML
Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2021-08-21 11:15:29 +02:00
727f2c671e Move zone to different NS
Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2021-08-21 11:14:41 +02:00
4 changed files with 77 additions and 31 deletions

View File

@ -73,7 +73,7 @@ In short: just because I could. It was one of those ideas I was wondering idly a
### Has it any practical use? ### Has it any practical use?
It is not intended to have any. Since DNS records are fairly small, serving images or something would quickly start It is not intended to have any. Since DNS records are fairly small, serving images or something would quickly start
consuming 100s of requests per second. I wouldn't want to do that to Cloudflare 😉 consuming 100s of requests per second. I wouldn't want to do that to Cloudflare.
It would be an interesting experiment to see how feasible that is. It would be an interesting experiment to see how feasible that is.

View File

@ -0,0 +1,21 @@
class Verifier {
/***
* @param content Content
*/
async verify(content) {
if (!window.isSecureContext) return;
if (typeof TextEncoder === "undefined") return;
if (typeof crypto === "undefined") return;
const encoder = new TextEncoder();
const digestBuffer = await crypto.subtle.digest(content.index.hashAlgorithm, encoder.encode(content.content));
const hashArray = Array.from(new Uint8Array(digestBuffer)); // convert buffer to byte array
const digest = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
if (content.index.hash === digest) {
document.getElementById("verification").innerHTML =
"The hash of the content is verified to correspond with the hash" +
" in the metadata. You are now reading exactly what was intended.";
}
}
}

View File

@ -1,8 +1,8 @@
<!DOCTYPE html charset="UTF-8"> <!DOCTYPE html>
<html> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Blog over DNS</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.2.0/build/styles/default.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.2.0/build/styles/default.min.css">
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.2.0/build/highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.2.0/build/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
@ -10,13 +10,20 @@
class Content { class Content {
content = ""; content = "";
type = ""; type = "";
metaData = {} metaData = {};
index;
constructor(content, type, metaData = {}) /***
* @param content string
* @param type string
* @param index Index
*/
constructor(content, type, index)
{ {
this.content = content; this.content = content;
this.type = type; this.type = type;
this.metaData = metaData; this.index = index;
this.metaData = index.metaData;
} }
} }
@ -24,13 +31,15 @@ class Index
{ {
mimeType = ""; mimeType = "";
chunks = 0; chunks = 0;
hashAlgorithm = "";
hash = ""; hash = "";
metaData = {}; metaData = {};
constructor(mimeType, chunks, hash, metaData = {}) constructor(mimeType, chunks, hash, hashAlgorithm, metaData = {})
{ {
this.mimeType = mimeType; this.mimeType = mimeType;
this.chunks = chunks; this.chunks = chunks;
this.hashAlgorithm = hashAlgorithm;
this.hash = hash; this.hash = hash;
this.metaData = metaData this.metaData = metaData
} }
@ -40,7 +49,7 @@ const dohServer = "https://cloudflare-dns.com/dns-query?ct=application/dns-json&
const baseDomain = "hod.experiments.jacobkiers.net"; const baseDomain = "hod.experiments.jacobkiers.net";
async function readUrl(domain) { async function readUrl(domain) {
var index = await fetchIndex(`${dohServer}${domain}.${baseDomain}`); var index = await fetchIndex(`${domain}.${baseDomain}`);
var chunk_promises = []; var chunk_promises = [];
for(i = 0; i < index.chunks; i++) for(i = 0; i < index.chunks; i++)
@ -48,28 +57,31 @@ async function readUrl(domain) {
chunk_promises[i] = fetchChunk(i, index.hash); chunk_promises[i] = fetchChunk(i, index.hash);
} }
var chunks = await Promise.all(chunk_promises); const chunks = await Promise.all(chunk_promises);
var content = chunks.reduce((built, current) => built += current); const base64 = chunks.reduce((built, current) => built += current);
return handleContent(new Content(atob(content), index.mimeType, index.metaData)); const content = atob(base64);
return handleContent(new Content(content, index.mimeType, index));
} }
async function fetchChunk(id, hash) async function fetchChunk(id, hash)
{ {
var domain = `${id}.${hash}.${baseDomain}`; const domain = `${id}.${hash}.${baseDomain}`;
return await fetchData(domain);
}
async function fetchData(domain)
{
const json = await fetch(`${dohServer}${domain}`) const json = await fetch(`${dohServer}${domain}`)
.then(response => response.json()); .then(response => response.json());
const raw_data = json.Answer[0].data;
const data = json.Answer[0].data.slice(1, -1); const data = raw_data.replaceAll(/[\s\"]/g, '');
return data; return data;
} }
async function fetchIndex(domain) async function fetchIndex(domain)
{ {
const response = await fetch(`${dohServer}.${domain}`); const index = await fetchData(domain);
const json = await response.json();
const index = json.Answer[0].data.slice(1, -1);
let ret = {}; let ret = {};
let items = index.split(';'); let items = index.split(';');
@ -82,7 +94,7 @@ async function fetchIndex(domain)
}); });
const metadata = JSON.parse(atob(ret["m"])); const metadata = JSON.parse(atob(ret["m"]));
return new Index(ret["t"], ret["c"], ret["h"], metadata); return new Index(ret["t"], ret["c"], ret["h"], ret["ha"], metadata);
} }
function handleContent(content) function handleContent(content)
@ -108,7 +120,12 @@ function handleContent(content)
function handleJavascript(content) function handleJavascript(content)
{ {
console.log("Got some javascript!", content.content); console.log("Got some javascript!");
const scripts = document.getElementById("scripts");
const new_script = document.createElement("script");
new_script.text = content.content;
scripts.appendChild(new_script);
} }
async function handleMarkdown(content) async function handleMarkdown(content)
@ -126,20 +143,28 @@ async function handleMarkdown(content)
let title = document.createElement("h1"); let title = document.createElement("h1");
title.innerHTML = content.metaData.title; title.innerHTML = content.metaData.title;
document.getElementById("post").prepend(title) document.getElementById("post").prepend(title)
document.getElementById("nojs").style.visibility = "hidden"; document.getElementById("nojs").remove();
if (typeof Verifier !== "undefined") {
await (new Verifier()).verify(content);
}
} }
</script> </script>
</head> </head>
<body> <body>
<script>
readUrl("posts-2021-08-17-serving-blog-content-over-dns-md");
</script>
<div id="post"></div> <div id="post"></div>
<div id="verification"></div>
<div id="nojs"> <div id="nojs">
<h1>HTML over DNS</h1> <h1>HTML over DNS</h1>
<p>The content of this page is fetched using DNS over HTTP. Since that requires Javascript, please enable that to see the content.</p> <p>The content of this page is fetched using DNS over HTTP. Since that requires Javascript, please enable that to see the content.</p>
</div> </div>
<div id="scripts">
<script>
readUrl("scripts-verifier-js").then(
() => readUrl("posts-2021-08-17-serving-blog-content-over-dns-md")
);
</script>
</div>
</body> </body>
</html> </html>

View File

@ -1,7 +1,7 @@
; ;
$TTL 5m ; Default TTL $TTL 5m ; Default TTL
@ IN SOA home.kie.rs. postmaster.kie.rs. ( @ IN SOA experiments.jacobkiers.net. postmaster.kie.rs. (
2021081611 ; serial 2021082004 ; serial
1h ; slave refresh interval 1h ; slave refresh interval
15m ; slave retry interval 15m ; slave retry interval
1w ; slave copy expire time 1w ; slave copy expire time
@ -13,7 +13,7 @@ $ORIGIN hod.experiments.jacobkiers.net.
; ;
; domain name servers ; domain name servers
; ;
@ IN NS home.kie.rs. @ IN NS experiments.jacobkiers.net.
;; START BLOG RECORDS ;; START BLOG RECORDS