Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
feat: enable recursive lookups on ipfs.dns
Browse files Browse the repository at this point in the history
  • Loading branch information
niinpatel committed Mar 16, 2019
1 parent 835459a commit e81b89f
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 8 deletions.
10 changes: 8 additions & 2 deletions src/cli/commands/dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ module.exports = {
describe: 'Resolve DNS links',

builder: {
recursive: {
type: 'boolean',
default: false,
alias: 'r',
desc: 'Resolve until the result is not a DNS link'
},
format: {
type: 'string'
}
},

handler ({ getIpfs, domain, resolve }) {
handler ({ getIpfs, domain, resolve, ...opts }) {
resolve((async () => {
const ipfs = await getIpfs()
const path = await ipfs.dns(domain)
const path = await ipfs.dns(domain, opts)
print(path)
})())
}
Expand Down
32 changes: 28 additions & 4 deletions src/core/runtime/dns-nodejs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,30 @@

const dns = require('dns')
const _ = require('lodash')
const isIPFS = require('is-ipfs')
const errcode = require('err-code')

const maxRecursiveDepth = 32

module.exports = (domain, opts, callback) => {
resolveDnslink(domain)
const recursive = opts.recursive && opts.recursive.toString() === 'true'
let depth
if (recursive) {
depth = maxRecursiveDepth
}

return recursiveResolveDnslink(domain, depth, callback)
}

function recursiveResolveDnslink (domain, depth, callback) {
if (depth === 0) {
return callback(errcode(`recursion limit exceeded`, 'ERR_DNSLINK_RECURSION_LIMIT'))
}

return resolveDnslink(domain)
.catch(err => {
// If the code is not ENOTFOUND or ERR_DNSLINK_NOT_FOUND then throw the error
if (err.code !== 'ENOTFOUND' && err.code !== 'ERR_DNSLINK_NOT_FOUND') throw err
// If the code is not ENOTFOUND or ERR_DNSLINK_NOT_FOUND or ENODATA then throw the error
if (err.code !== 'ENOTFOUND' && err.code !== 'ERR_DNSLINK_NOT_FOUND' && err.code !== 'ENODATA') throw err

if (domain.startsWith('_dnslink.')) {
// The supplied domain contains a _dnslink component
Expand All @@ -22,7 +39,14 @@ module.exports = (domain, opts, callback) => {
return resolveDnslink(_dnslinkDomain)
})
.then(dnslinkRecord => {
callback(null, dnslinkRecord.replace('dnslink=', ''))
const result = dnslinkRecord.replace('dnslink=', '')
const domainOrCID = result.split('/')[2]
const isIPFSCID = isIPFS.cid(domainOrCID)

if (isIPFSCID || !depth) {
return callback(null, result)
}
return recursiveResolveDnslink(domainOrCID, depth - 1, callback)
})
.catch(callback)
}
Expand Down
6 changes: 4 additions & 2 deletions src/http/api/resources/dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
const Boom = require('boom')

module.exports = async (request, h) => {
if (!request.query.arg) {
const { arg: domain, ...opts } = request.query

if (!domain) {
throw Boom.badRequest("Argument 'domain' is required")
}

const path = await request.server.app.ipfs.dns(request.query.arg)
const path = await request.server.app.ipfs.dns(domain, opts)
return h.response({
Path: path
})
Expand Down
26 changes: 26 additions & 0 deletions test/cli/dns.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

const expect = require('chai').expect
const runOnAndOff = require('../utils/on-and-off')
const isIPFS = require('is-ipfs')

describe('dns', () => runOnAndOff((thing) => {
let ipfs
Expand All @@ -27,4 +28,29 @@ describe('dns', () => runOnAndOff((thing) => {
expect(res.substr(0, 6)).to.eql('/ipns/')
})
})

it('recursively resolve ipfs.io', function () {
this.timeout(60 * 1000)

return ipfs('dns --recursive ipfs.io').then((res) => {
const resultingDomainOrCid = res.split('/')[2].trim()
expect(isIPFS.cid(resultingDomainOrCid)).to.eql(true)
})
})

it('resolve subdomain docs.ipfs.io dns', function () {
this.timeout(60 * 1000)

return ipfs('dns docs.ipfs.io').then(res => {
expect(res.substr(0, 6)).to.eql('/ipfs/')
})
})

it('resolve subdomain _dnslink.docs.ipfs.io dns', function () {
this.timeout(60 * 1000)

return ipfs('dns _dnslink.docs.ipfs.io').then(res => {
expect(res.substr(0, 6)).to.eql('/ipfs/')
})
})
}))
62 changes: 62 additions & 0 deletions test/core/dns.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* eslint-env mocha */
'use strict'

const chai = require('chai')
const dirtyChai = require('dirty-chai')
const expect = chai.expect
chai.use(dirtyChai)

const IPFSFactory = require('ipfsd-ctl')
const IPFS = require('../../src/core')

describe('.dns', () => {
let ipfsd, ipfs

before(function (done) {
this.timeout(20 * 1000)

const factory = IPFSFactory.create({ type: 'proc' })

factory.spawn({
exec: IPFS,
initOptions: { bits: 512 },
config: { Bootstrap: [] }
}, (err, _ipfsd) => {
expect(err).to.not.exist()
ipfsd = _ipfsd
ipfs = _ipfsd.api
done()
})
})

after((done) => {
if (ipfsd) {
ipfsd.stop(done)
} else {
done()
}
})

// skipping because there is an error in https://ipfs.io/api/v0/dns?arg=ipfs.io
// unskip once this is resolved: https://github.com/ipfs/go-ipfs/issues/6086
it.skip('should resolve ipfs.io', () => {
return ipfs.dns('ipfs.io').then(res => {
// matches pattern /ipns/<ipnsaddress>
expect(res).to.match(/\/ipns\/.+$/)
})
})

it('should recursively resolve ipfs.io', () => {
return ipfs.dns('ipfs.io', { recursive: true }).then(res => {
// matches pattern /ipfs/<hash>
expect(res).to.match(/\/ipfs\/.+$/)
})
})

it('should resolve subdomain docs.ipfs.io', () => {
return ipfs.dns('docs.ipfs.io').then(res => {
// matches pattern /ipfs/<hash>
expect(res).to.match(/\/ipfs\/.+$/)
})
})
})

0 comments on commit e81b89f

Please sign in to comment.