Compare commits
4 Commits
1dcd49fd0d
...
2d477bb77e
Author | SHA1 | Date | |
---|---|---|---|
2d477bb77e | |||
4b117a4743 | |||
74619915ca | |||
727f2c671e |
@ -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.
|
||||||
|
|
||||||
|
21
content/scripts/verifier.js
Normal file
21
content/scripts/verifier.js
Normal 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.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
index.html
75
index.html
@ -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>
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user