Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redirect support (limit: 6) #29

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: 6)
datDns.resolveName('foo.com', { followRedirects: 2 })

// list all entries in the cache
datDns.listCache()

Expand Down
47 changes: 37 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 >= 0) ? 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,38 @@ 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) {
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('well-known lookup at', host + path , 'redirected (' + res.statusCode + ') to non-https location')
}
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: '' })
})
Expand Down
53 changes: 51 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,57 @@ 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('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()
Expand Down