<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Blog over DNS</title> <link rel="stylesheet" href="equilibrium-light.min.css"> <script src="highlight.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script> class Content { content = ""; type = ""; metaData = {}; index; /*** * @param content string * @param type string * @param index Index */ constructor(content, type, index) { this.content = content; this.type = type; this.index = index; this.metaData = index.metaData; } } class Index { mimeType = ""; chunks = 0; hashAlgorithm = ""; hash = ""; metaData = {}; constructor(mimeType, chunks, hash, hashAlgorithm, metaData = {}) { this.mimeType = mimeType; this.chunks = chunks; this.hashAlgorithm = hashAlgorithm; this.hash = hash; this.metaData = metaData } } const dohServer = "https://cloudflare-dns.com/dns-query?ct=application/dns-json&type=TXT&name="; const baseDomain = "hod.experiments.jacobkiers.net"; async function readUrl(domain) { var index = await fetchIndex(`${domain}.${baseDomain}`); var chunk_promises = []; for(i = 0; i < index.chunks; i++) { chunk_promises[i] = fetchChunk(i, index.hash); } const chunks = await Promise.all(chunk_promises); const base64 = chunks.reduce((built, current) => built += current); const content = atob(base64); return handleContent(new Content(content, index.mimeType, index)); } async function fetchChunk(id, hash) { const domain = `${id}.${hash}.${baseDomain}`; return await fetchData(domain); } async function fetchData(domain) { const json = await fetch(`${dohServer}${domain}`, { method: "GET", headers: { "Accept": "application/dns-json" } }) .then(response => response.json()); const raw_data = json.Answer[0].data; const data = raw_data.replaceAll(/[\s\"]/g, ''); return data; } async function fetchIndex(domain) { const index = await fetchData(domain); let ret = {}; let items = index.split(';'); items.forEach(item => { let md = item.split('='); let key = md[0]; let value = md[1]; ret[key] = value; }); const metadata = JSON.parse(atob(ret["m"])); return new Index(ret["t"], ret["c"], ret["h"], ret["ha"], metadata); } function handleContent(content) { if (!content instanceof Content) { console.log("Not valid content in handleContent.") return; } switch(content.type) { case "text/javascript": return handleJavascript(content); case "text/markdown": return handleMarkdown(content); default: console.log(`handleContent() does not know how to parse ${content.type}`); break; } } function handleJavascript(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) { console.log("Got me some markdown!"); marked.setOptions({ highlight: function(code, lang) { const avialable_languages = hljs.listLanguages(); const has_language = hljs.getLanguage(lang); if (typeof has_language === "undefined") return code; const result = hljs.highlight(code, { language: lang, ignoreIllegals: true}); return result.value; }, }); if (typeof content.metaData.title !== "undefined") document.title = content.metaData.title; document.getElementById("post").innerHTML = marked.parse(content.content); let title = document.createElement("h1"); title.innerHTML = content.metaData.title; document.getElementById("post").prepend(title) document.getElementById("nojs").remove(); if (typeof Verifier !== "undefined") { await (new Verifier()).verify(content); } } </script> </head> <body> <div id="post"></div> <div id="verification"></div> <div id="nojs"> <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> </div> <div id="scripts"> <script> readUrl("scripts-verifier-js").then( () => readUrl("posts-2021-08-17-serving-blog-content-over-dns-md") ); </script> </div> </body> </html>