Skip to content

Commit

Permalink
Add redirect support (limit: 6)
Browse files Browse the repository at this point in the history
Resolves issue #25.
  • Loading branch information
da2x authored and martinheidegger committed Mar 26, 2021
1 parent 7730845 commit b2e5cfe
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 12 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
48 changes: 38 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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', {
Expand Down Expand Up @@ -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, headers: res.headers, body })))
}).on('error', function (err) {
resolve({ statusCode: 0, err, body: '' })
})
Expand Down
36 changes: 34 additions & 2 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit b2e5cfe

Please sign in to comment.