From 618d530c279174e17afd761c80a46bd918ba4df1 Mon Sep 17 00:00:00 2001 From: Daniel Aleksandersen Date: Wed, 25 Mar 2020 20:02:59 +0100 Subject: [PATCH 1/4] Add redirect support (limit: 6) Resolves issue #25. --- README.md | 3 +++ index.js | 48 ++++++++++++++++++++++++++++++++++++++---------- test.js | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e8f1bc2..2f6747e 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,9 @@ datDns.resolveName('foo.com', {noDnsOverHttps: true}) // dont use .well-known/dat datDns.resolveName('foo.com', {noWellknownDat: true}) +// specify amount of redirects (default: 7) +datDns.resolveName('foo.com', { followRedirects: 2 }) + // list all entries in the cache datDns.listCache() diff --git a/index.js b/index.js index f516642..6214130 100644 --- a/index.js +++ b/index.js @@ -15,6 +15,7 @@ const VERSION_REGEX = /(\+[^\/]+)$/ const DEFAULT_DAT_DNS_TTL = 3600 // 1hr const MAX_DAT_DNS_TTL = 3600 * 24 * 7 // 1 week const DEFAULT_DNS_PROVIDERS = [['cloudflare-dns.com', 443, '/dns-query'], ['dns.google', 443, '/resolve']] +const DEFAULT_FOLLOW_REDIRECTS = 6 module.exports = createDatDNS @@ -69,6 +70,7 @@ function createDatDNS (datDnsOpts) { var ignoreCachedMiss = opts && opts.ignoreCachedMiss var noDnsOverHttps = opts && opts.noDnsOverHttps var noWellknownDat = opts && opts.noWellknownDat + var followRedirects = (opts && opts.followRedirects) || DEFAULT_FOLLOW_REDIRECTS return maybe(cb, _asyncToGenerator(function * () { // parse the name as needed var nameParsed = url.parse(name) @@ -117,7 +119,10 @@ function createDatDNS (datDnsOpts) { if (!res && !noWellknownDat) { // do a .well-known/`${recordName}` lookup - res = yield fetchWellKnownRecord(name, recordName) + const wellknownPath = !recordName.startsWith('/') + ? '/.well-known/' + recordName + : recordName + res = yield fetchWellKnownRecordWithRedirects(recordName, name, wellknownPath, followRedirects) if (res.statusCode === 0 || res.statusCode === 404) { debug('.well-known/' + recordName + ' lookup failed for name:', name, res.statusCode, res.err) datDns.emit('failed', { @@ -280,16 +285,39 @@ function parseDnsOverHttpsRecord (datDns, name, body, dnsTxtRegex) { return res } -function fetchWellKnownRecord (name, recordName) { - return new Promise((resolve, reject) => { - debug('.well-known/dat lookup for name:', name) - https.get({ - host: name, - path: '/.well-known/' + recordName, - timeout: 2000 - }, function (res) { +function fetchWellKnownRecordWithRedirects (recordName, host, path, followRedirects) { + return _asyncToGenerator(function * () { + let redirectCount = 0 + while (redirectCount < followRedirects) { + const res = yield fetchWellKnownRecord(recordName, host, path) + if ([301, 302, 307, 308].includes(res.statusCode)) { + if (!'location' in res.headers) { + debug('.well-known/' + recordName + ' lookup redirect did not contain destination Location header.') + throw new Error('Well record redirected to nowhere') + } + // resolve relative paths with original URL as base URL + const uri = new URL(res.headers['location'], 'https://' + host) + if (uri.protocol !== 'https:') { + throw new Error('DNS record redirected to non https: protocol: ' + uri.href) + } + host = uri.host + path = uri.pathname + uri.search + redirectCount++ + debug('.well-known/' + recordName + ' lookup redirected to https://' + host + path, '(' + res.statusCode + ') [' + redirectCount + '/' + followRedirects + ']') + } else { + return res + } + } + throw new Error('Well known record lookup exceeded redirection limit: ' + followRedirects) + })() +} + +function fetchWellKnownRecord (recordName, host, path) { + return new Promise(resolve => { + debug('.well-known/' + recordName + ' lookup at https://' + host + path) + https.get({ host, path, timeout: 2000 }, function (res) { res.setEncoding('utf-8') - res.pipe(concat(body => resolve({ statusCode: res.statusCode, body }))) + res.pipe(concat(body => resolve({ statusCode: res.statusCode, body, headers: res.headers }))) }).on('error', function (err) { resolve({ statusCode: 0, err, body: '' }) }) diff --git a/test.js b/test.js index 6701a10..9620009 100644 --- a/test.js +++ b/test.js @@ -8,8 +8,6 @@ var cabalDns = createDatDNS({ txtRegex: /^"?cabalkey=([0-9a-f]{64})"?$/i }) -var FAKE_DAT = 'f'.repeat(64) - tape('Successful test against cblgh.org', function (t) { cabalDns.resolveName('cblgh.org', function (err, name) { t.error(err) @@ -197,6 +195,40 @@ tape('Successful test against dns-test-setup.dat-ecosystem.org (no well-known/da }) }) +tape('Successful test against dns-test-setup-2.dat-ecosystem.org (well-known, multiple, , redirects)', function (t) { + const datDnsHop3 = require('./index')({ recordName: 'dat-hop-3' }) + datDnsHop3.resolveName('dns-test-setup-2.dat-ecosystem.org', {noDnsOverHttps: true, ignoreCache: true }) + .then(name => { + t.equals(name, '222231b5589a5099aa3610a8ee550dcd454c3e33f4cac93b7d41b6b850cde222') + return datDnsHop3.resolveName('dns-test-setup-2.dat-ecosystem.org') + .then(name2 => { + t.equal(name, name2) + t.end() + }) + }) + .catch(err => { + t.error(err) + t.end() + }) +}) + +tape('Fail test against dns-test-setup-2.dat-ecosystem.org (well-known, multiple, exceeding redirects)', function (t) { + const datDnsHop3 = require('./index')({ recordName: 'dat-hop-3' }) + datDnsHop3.resolveName('dns-test-setup-2.dat-ecosystem.org', {noDnsOverHttps: true, ignoreCache: true, followRedirects: 2}) + .then(name => { + t.equals(name, '222231b5589a5099aa3610a8ee550dcd454c3e33f4cac93b7d41b6b850cde222') + return datDnsHop3.resolveName('dns-test-setup-2.dat-ecosystem.org') + .then(() => { + t.fail('Dont expect to succeed') + t.end() + }) + }) + .catch(err => { + t.equals(err.message, 'Well known record lookup exceeded redirection limit: 2') + t.end() + }) +}) + tape('List cache', function (t) { t.is(Object.keys(datDns.listCache()).length, 6) t.end() From 7a488de777e1b67cc4d8a17814010e6559911b63 Mon Sep 17 00:00:00 2001 From: Martin Heidegger Date: Wed, 31 Mar 2021 14:56:53 +0900 Subject: [PATCH 2/4] option for 0 redirects --- index.js | 2 +- test.js | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 6214130..f6a58fd 100644 --- a/index.js +++ b/index.js @@ -70,7 +70,7 @@ function createDatDNS (datDnsOpts) { var ignoreCachedMiss = opts && opts.ignoreCachedMiss var noDnsOverHttps = opts && opts.noDnsOverHttps var noWellknownDat = opts && opts.noWellknownDat - var followRedirects = (opts && opts.followRedirects) || DEFAULT_FOLLOW_REDIRECTS + var followRedirects = (opts && opts.followRedirects >= 0) ? opts.followRedirects : DEFAULT_FOLLOW_REDIRECTS return maybe(cb, _asyncToGenerator(function * () { // parse the name as needed var nameParsed = url.parse(name) diff --git a/test.js b/test.js index 9620009..c700b5a 100644 --- a/test.js +++ b/test.js @@ -229,6 +229,23 @@ tape('Fail test against dns-test-setup-2.dat-ecosystem.org (well-known, multiple }) }) +tape('Fail with zero redirects dns-test-setup-2.dat-ecosystem.org (well-known, multiple, exceeding redirects)', function (t) { + const datDnsHop3 = require('./index')({ recordName: 'dat-hop-3' }) + datDnsHop3.resolveName('dns-test-setup-2.dat-ecosystem.org', {noDnsOverHttps: true, ignoreCache: true, followRedirects: 0}) + .then(name => { + t.equals(name, '222231b5589a5099aa3610a8ee550dcd454c3e33f4cac93b7d41b6b850cde222') + return datDnsHop3.resolveName('dns-test-setup-2.dat-ecosystem.org') + .then(() => { + t.fail('Dont expect to succeed') + t.end() + }) + }) + .catch(err => { + t.equals(err.message, 'Well known record lookup exceeded redirection limit: 0') + t.end() + }) +}) + tape('List cache', function (t) { t.is(Object.keys(datDns.listCache()).length, 6) t.end() From 7edfc1b9a1b0a00e865fc2e618b49ae1bc1d128c Mon Sep 17 00:00:00 2001 From: Martin Heidegger Date: Wed, 31 Mar 2021 14:57:27 +0900 Subject: [PATCH 3/4] more expressive/clearer error messages for redirects --- index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index f6a58fd..3efe4b5 100644 --- a/index.js +++ b/index.js @@ -292,13 +292,12 @@ function fetchWellKnownRecordWithRedirects (recordName, host, path, followRedire const res = yield fetchWellKnownRecord(recordName, host, path) if ([301, 302, 307, 308].includes(res.statusCode)) { if (!'location' in res.headers) { - debug('.well-known/' + recordName + ' lookup redirect did not contain destination Location header.') - throw new Error('Well record redirected to nowhere') + throw new Error('well-known lookup at', host + path , 'redirected (' + res.statusCode + ') to nowhere') } // resolve relative paths with original URL as base URL const uri = new URL(res.headers['location'], 'https://' + host) if (uri.protocol !== 'https:') { - throw new Error('DNS record redirected to non https: protocol: ' + uri.href) + throw new Error('well-known lookup at', host + path , 'redirected (' + res.statusCode + ') to non-https location') } host = uri.host path = uri.pathname + uri.search From ed3331638fd4dfd32aa7c42482bc0896518fdb01 Mon Sep 17 00:00:00 2001 From: Martin Heidegger Date: Wed, 31 Mar 2021 14:57:39 +0900 Subject: [PATCH 4/4] fixing typo in docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f6747e..0f6b986 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ datDns.resolveName('foo.com', {noDnsOverHttps: true}) // dont use .well-known/dat datDns.resolveName('foo.com', {noWellknownDat: true}) -// specify amount of redirects (default: 7) +// specify amount of redirects (default: 6) datDns.resolveName('foo.com', { followRedirects: 2 }) // list all entries in the cache