From cfe4ca0d87af67986af8c503fe44545435d1ff93 Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Fri, 8 Apr 2022 13:56:20 +0200 Subject: [PATCH] add skynet-sponsor-key dnslink functionality --- docker/nginx/conf.d/server/server.dnslink | 17 ++++++++++--- packages/dnslink-api/src/index.js | 29 +++++++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/docker/nginx/conf.d/server/server.dnslink b/docker/nginx/conf.d/server/server.dnslink index cf385a1d..139196ef 100644 --- a/docker/nginx/conf.d/server/server.dnslink +++ b/docker/nginx/conf.d/server/server.dnslink @@ -5,6 +5,7 @@ location / { set $path $uri; rewrite_by_lua_block { + local cjson = require("cjson") local cache = ngx.shared.dnslink local cache_value = cache:get(ngx.var.host) @@ -28,13 +29,23 @@ location / { ngx.exit(ngx.status) end else - ngx.var.skylink = res.body + local resolved = cjson.decode(res.body) + + ngx.var.skylink = resolved.skylink + if resolved.sponsor then + ngx.req.set_header("Skynet-Api-Key", resolved.sponsor) + end local cache_ttl = 300 -- 5 minutes cache expire time - cache:set(ngx.var.host, ngx.var.skylink, cache_ttl) + cache:set(ngx.var.host, res.body, cache_ttl) end else - ngx.var.skylink = cache_value + local resolved = cjson.decode(cache_value) + + ngx.var.skylink = resolved.skylink + if resolved.sponsor then + ngx.req.set_header("Skynet-Api-Key", resolved.sponsor) + end end ngx.var.skylink = require("skynet.skylink").parse(ngx.var.skylink) diff --git a/packages/dnslink-api/src/index.js b/packages/dnslink-api/src/index.js index 300277c2..8ea0aeec 100644 --- a/packages/dnslink-api/src/index.js +++ b/packages/dnslink-api/src/index.js @@ -8,12 +8,14 @@ const port = Number(process.env.DNSLINK_API_PORT) || 3100; const server = express(); const dnslinkNamespace = "skynet-ns"; +const sponsorNamespace = "skynet-sponsor-key"; const dnslinkRegExp = new RegExp(`^dnslink=/${dnslinkNamespace}/.+$`); +const sponsorRegExp = new RegExp(`^${sponsorNamespace}=[a-zA-Z0-9]+$`); const dnslinkSkylinkRegExp = new RegExp(`^dnslink=/${dnslinkNamespace}/([a-zA-Z0-9_-]{46}|[a-z0-9]{55})`); const hint = `valid example: dnslink=/${dnslinkNamespace}/3ACpC9Umme41zlWUgMQh1fw0sNwgWwyfDDhRQ9Sppz9hjQ`; server.get("/dnslink/:name", async (req, res) => { - const success = (skylink) => res.send(skylink); + const success = (response) => res.json(response); const failure = (message) => res.status(400).send(message); if (!isValidDomain(req.params.name)) { @@ -22,7 +24,7 @@ server.get("/dnslink/:name", async (req, res) => { const lookup = `_dnslink.${req.params.name}`; - dns.resolveTxt(lookup, (error, records) => { + dns.resolveTxt(lookup, (error, addresses) => { if (error) { if (error.code === "ENOTFOUND") { return failure(`ENOTFOUND: ${lookup} TXT record doesn't exist`); @@ -35,11 +37,12 @@ server.get("/dnslink/:name", async (req, res) => { return failure(`Failed to fetch ${lookup} TXT record: ${error.message}`); } - if (records.length === 0) { + if (addresses.length === 0) { return failure(`No TXT record found for ${lookup}`); } - const dnslinks = records.flat().filter((record) => dnslinkRegExp.test(record)); + const records = addresses.flat(); + const dnslinks = records.filter((record) => dnslinkRegExp.test(record)); if (dnslinks.length === 0) { return failure(`TXT records for ${lookup} found but none of them contained valid skynet dnslink - ${hint}`); @@ -58,9 +61,25 @@ server.get("/dnslink/:name", async (req, res) => { const skylink = matchSkylink[1]; + // check if _dnslink records contain skynet-sponsor-key entries + const sponsors = records.filter((record) => sponsorRegExp.test(record)); + + if (sponsors.length > 1) { + return failure(`Multiple TXT records with valid sponsor key found for ${lookup}, only one allowed`); + } + + if (sponsors.length === 1) { + // extract just the key part from the record + const sponsor = sponsors[0].substring(sponsors[0].indexOf("=") + 1); + + console.log(`${req.params.name} => ${skylink} | sponsor: ${sponsor}`); + + return success({ skylink, sponsor }); + } + console.log(`${req.params.name} => ${skylink}`); - return success(skylink); + return success({ skylink }); }); });