diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index 967bd51..a534b84 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - node-version: [ 14.x, 16.x ] + node-version: [ 14, 16 ] fail-fast: false steps: @@ -24,12 +24,11 @@ jobs: with: fetch-depth: 1 - - uses: actions/setup-node@v1 - name: Use Node.js ${{ matrix.node-version }} + - uses: actions/setup-node@v3 + name: Use Node.js ${{ matrix.node-version }} on ${{ matrix.os }} with: node-version: ${{ matrix.node-version }} - run: npm install - - name: Run test suite - run: npm run test + - run: npm run test diff --git a/.github/workflows/coveralls.yml b/.github/workflows/coveralls.yml index 220ca43..4c775ad 100644 --- a/.github/workflows/coveralls.yml +++ b/.github/workflows/coveralls.yml @@ -5,6 +5,7 @@ name: Coverage env: CI: true + node_version: 14 jobs: @@ -19,18 +20,18 @@ jobs: with: fetch-depth: 1 - - name: Use Node.js 14 + - name: Use Node.js ${{ env.node_version }} uses: actions/setup-node@master with: - node-version: 14 + node-version: ${{ env.node_version }} - name: npm install run: | npm install - npm install --no-save nyc codecov + npm install --no-save c8 codecov - name: run coverage - run: npx nyc --reporter=lcovonly npm run test + run: npx c8 --reporter=lcovonly npm run test env: NODE_ENV: cov diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index b3d5a50..91b349f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,6 +5,7 @@ on: [ push ] env: CI: true + node_version: 14 jobs: @@ -22,12 +23,11 @@ jobs: with: fetch-depth: 1 - - uses: actions/setup-node@v2 - name: Use Node.js ${{ matrix.node-version }} + - uses: actions/setup-node@v3 + name: Use Node.js ${{ env.node_version }} with: - node-version: ${{ matrix.node-version }} + node-version: ${{ env.node_version }} - run: npm install - - name: Lint - run: npm run lint \ No newline at end of file + - run: npm run lint \ No newline at end of file diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 72cce11..2fa8fd9 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -7,8 +7,8 @@ on: - master env: - node_version: 14 CI: true + node_version: 14 jobs: @@ -22,18 +22,16 @@ jobs: fetch-depth: 0 - name: Setup node.js ${{ env.node_version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ env.node_version }} registry-url: https://registry.npmjs.org/ - run: npm install - - name: Run tests - run: npm run test + - run: npm run test - - name: publish to NPM - run: npm publish --access public + - run: npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} @@ -43,8 +41,8 @@ jobs: - name: run coverage run: | - npm install --no-save nyc codecov - npx nyc --reporter=lcovonly npm run test + npm install --no-save c8 codecov + npx c8 --reporter=lcovonly npm run test env: NODE_ENV: cov diff --git a/CHANGELOG.md b/CHANGELOG.md index fa1f5ef..560d7f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ #### 1.N.N - YYYY-MM-DD +#### 0.9.9 - 2022-04-14 + +- feat: parser improvements (DNSKEY, HINFO, NAPTR, SOA, TLSA, TXT) +- CAA: more robust fromBind parser +- SOA: leave $TTL and $ORIGIN to dns-zone +- test/base: improve invalid tests, check error message against expected +- test/rr: update tests with expected error messages +- README: move some content to web links + + #### 0.9.8 - 2022-04-07 - url updates diff --git a/README.md b/README.md index f30d3ed..0820dfd 100644 --- a/README.md +++ b/README.md @@ -235,35 +235,13 @@ PRs are welcome, especially PRs with tests. - this library enforces duplicate suppression - DNSSEC canonicalization (see RFC 4034) - wire format for most RRs require it - - Master Zone File expansions exist at another level -- fromBIND is regex based and is naive. [dns-zone](https://github.com/NicTool/dns-zone) has a much more robust parser. + - Master Zone File expansions exist in [dns-zone](https://github.com/NicTool/dns-zone) - to{Bind|MaraDNS} output can be influenced (suppress TTL, class, relative domain names) with an options object. See it in `bin/dns-zone` in the [dns-zone](https://github.com/NicTool/dns-zone) package. -## DEFINITIONS - -- Resource Record: structured data associated with names / nodes - - format: owner ttl class type rdata - - example: www.example.com 3600 IN A 192.0.2.127 -- owner [name]: - - a node in the domain name tree - - consists of a sequence of labels - - format: node.zone.tld. - - the right most label is null, aka the root - - the 2nd from right is the top level domain - - the 3rd from right is the [organizational] domain name -- label: character strings between the dots in a domain name -- ttl: time to live - - how long to cache this DNS resource record -- class: IN, internet. -- type: the type of rdata the follows - - examples: A, MX, SOA, PTR -- rdata: resource data. The contents vary widely by type - - examples: A records have an address, CNAME records bear a cname target, NS records point to nameservers (nsdname). - - ## SEE ALSO +- [Dictionary of DNS terms](https://nictool.github.io/web/Dictionary) - [Wikipedia, List of DNS Record Types](https://en.wikipedia.org/wiki/List_of_DNS_record_types) @@ -275,20 +253,17 @@ PRs are welcome, especially PRs with tests. - [x] add defaults for empty values like TTL - [x] DNSSEC RRs: DS, NSEC, NSEC3, NSEC3PARAM, RRSIG - [x] CERT RRs: CERT, KEY, SIG, OPENPGPKEY -- [ ] APL, KX, DHCID, HIP, RP, SVCB/HTTPS -- [ ] add toWire, exports in wire/network format - [x] RFC 4034: if the type of RR is NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR, HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX, SRV, DNAME, A6, RRSIG, or NSEC, all uppercase letters in the DNS names contained within the RDATA are replaced by the lowercase letters; -- [ ] LOC record ingest/out isn't consistent with API -- [ ] handling unknown RR types: RFC 3597 +- [x] LOC record ingest/out isn't consistent with API - [ ] export a web page for each RR type ## DEVELOP - this package has no dependencies. That's no accident. -- eventually this will be used a node.js app & a browser based app - - so, ESM, eventually -- CI tests are on linux, windows, and macos +- this will be used by a node.js app & a browser based app + - [x] ES6 modules, eventually +- [x] CI tests are on linux, windows, and macos diff --git a/package.json b/package.json index 54f1608..de11787 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dns-resource-record", - "version": "0.9.8", + "version": "0.9.9", "description": "DNS Resource Records", "main": "rr/index.js", "scripts": { diff --git a/rr/caa.js b/rr/caa.js index 75aa5e0..d31ad52 100644 --- a/rr/caa.js +++ b/rr/caa.js @@ -41,9 +41,11 @@ class CAA extends RR { } // check if val starts with one of iodefSchemes - const iodefSchemes = [ 'mailto:', 'http:', 'https:' ] - if (!iodefSchemes.filter(s => val.startsWith(s)).length) { - throw new Error(`CAA value must have valid iodefScheme prefix, ${this.citeRFC()}`) + if (this.get('tag') === 'iodef') { + const iodefSchemes = [ 'mailto:', 'http:', 'https:' ] + if (!iodefSchemes.filter(s => val.startsWith(s)).length) { + throw new Error(`CAA value must have valid iodefScheme prefix, ${this.citeRFC()}`) + } } this.set('value', val) @@ -96,7 +98,10 @@ class CAA extends RR { fromBind (str) { // test.example.com 3600 IN CAA flags, tags, value - const [ owner, ttl, c, type, flags, tag ] = str.split(/\s+/) + const fields = str.match(/^([^\s]+)\s+([0-9]+)\s+(\w+)\s+(\w+)\s+([0-9]+)\s+(\w+)\s+("[^"]+"|[^\s]+?)\s*$/i) + if (!fields) throw new Error(`unable to parse: ${str}`) + + const [ owner, ttl, c, type, flags, tag, value ] = fields.slice(1) return new this.constructor({ owner, ttl : parseInt(ttl, 10), @@ -104,7 +109,7 @@ class CAA extends RR { type, flags: parseInt(flags, 10), tag, - value: str.split(/\s+/).slice(6).join(' ').trim(), + value, }) } diff --git a/rr/dnskey.js b/rr/dnskey.js index c4e1cb1..73d5df3 100644 --- a/rr/dnskey.js +++ b/rr/dnskey.js @@ -60,7 +60,10 @@ class DNSKEY extends RR { fromBind (str) { // test.example.com 3600 IN DNSKEY Flags Protocol Algorithm PublicKey - const [ owner, ttl, c, type, flags, protocol, algorithm ] = str.split(/\s+/) + const match = str.match(/^([^\s]+)\s+([0-9]+)\s+(\w+)\s+(\w+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+\s*(.*?)\s*$/) + if (!match) throw new Error(`unable to parse DNSKEY: ${str}`) + const [ owner, ttl, c, type, flags, protocol, algorithm, publickey ] = match.slice(1) + return new this.constructor({ owner, ttl : parseInt(ttl, 10), @@ -69,7 +72,7 @@ class DNSKEY extends RR { flags : parseInt(flags, 10), protocol : parseInt(protocol, 10), algorithm: parseInt(algorithm, 10), - publickey: str.split(/\s+/).slice(7).join(' ').trim(), + publickey: publickey, }) } diff --git a/rr/hinfo.js b/rr/hinfo.js index 53d7d46..74b915d 100644 --- a/rr/hinfo.js +++ b/rr/hinfo.js @@ -9,12 +9,12 @@ class HINFO extends RR { /****** Resource record specific setters *******/ setCpu (val) { if (val.length > 255) throw new Error('HINFO cpu cannot exceed 255 chars') - this.set('cpu', val) + this.set('cpu', val.replace(/^["']|["']$/g, '')) } setOs (val) { if (val.length > 255) throw new Error('HINFO os cannot exceed 255 chars') - this.set('os', val) + this.set('os', val.replace(/^["']|["']$/g, '')) } getDescription () { @@ -38,13 +38,11 @@ class HINFO extends RR { } /****** IMPORTERS *******/ - // fromTinydns (str) { - // // HINFO via generic, :fqdn:n:rdata:ttl:timestamp:lo - // } - fromBind (str) { // test.example.com 3600 IN HINFO DEC-2060 TOPS20 - const [ owner, ttl, c, type, cpu, os ] = str.split(/\s+/) + const match = str.match(/([^\s]+)\s+([0-9]+)\s+(IN)\s+(HINFO)\s+("[^"]+"|[^\s]+)\s+("[^"]+"|[^\s]+)/i) + if (!match) throw new Error(`unable to parse HINFO: ${str}`) + const [ owner, ttl, c, type, cpu, os ] = match.slice(1) const bits = { owner, @@ -57,6 +55,10 @@ class HINFO extends RR { return new this.constructor(bits) } + // fromTinydns (str) { + // // HINFO via generic, :fqdn:n:rdata:ttl:timestamp:lo + // } + /****** EXPORTERS *******/ // toTinydns () { // const rdata = '' // TODO diff --git a/rr/index.js b/rr/index.js index 8f98707..1fa4640 100644 --- a/rr/index.js +++ b/rr/index.js @@ -9,17 +9,17 @@ class RR extends Map { if (opts.default) this.default = opts.default - if (opts.tinyline) return this.fromTinydns(opts.tinyline) if (opts.bindline) return this.fromBind(opts.bindline) + if (opts.tinyline) return this.fromTinydns(opts.tinyline) // tinydns specific this.setLocation(opts?.location) this.setTimestamp(opts?.timestamp) this.setOwner(opts?.owner) + this.setType (opts?.type) this.setTtl (opts?.ttl) this.setClass(opts?.class) - this.setType (opts?.type) for (const f of this.getFields('rdata')) { const fnName = `set${this.ucfirst(f)}` @@ -283,7 +283,6 @@ class RR extends Map { const type = this.get('type') const supportedTypes = 'A PTR MX AAAA SRV NAPTR NS SOA TXT SPF RAW FQDN4 FQDN6 CNAME HINFO WKS LOC'.split(/\s+/g) if (!supportedTypes.includes(type)) return this.toMaraGeneric() - // console.log(supportedTypes) return `${this.get('owner')}\t+${this.get('ttl')}\t${type}\t${this.getRdataFields().map(f => this.getQuoted(f)).join('\t')} ~\n` } diff --git a/rr/naptr.js b/rr/naptr.js index 7a2ba47..9cbb4f0 100644 --- a/rr/naptr.js +++ b/rr/naptr.js @@ -41,10 +41,10 @@ class NAPTR extends RR { } setFlags (val) { - if (![ '', 'S', 'A', 'U', 'P' ].includes(val)) + if (![ '', 'S', 'A', 'U', 'P' ].includes(val.toUpperCase())) throw new Error (`NAPTR flags are invalid, ${this.citeRFC()}`) - this.set('flags', val) + this.set('flags', val.toUpperCase()) } setService (val) { @@ -113,9 +113,9 @@ class NAPTR extends RR { type : type, order : parseInt(order, 10), preference : parseInt(preference, 10), - flags : flags.trim().replace(/^"|"$/g, ''), - service : service.trim().replace(/^"|"$/g, ''), - regexp : regexp.trim().replace(/^"|"$/g, ''), + flags : flags.trim().replace(/^['"]|['"]$/g, ''), + service : service.trim().replace(/^['"]|['"]$/g, ''), + regexp : regexp.trim().replace(/^['"]|['"]/g, ''), replacement: replacement, } return new this.constructor(bits) diff --git a/rr/soa.js b/rr/soa.js index 6e4f960..c711033 100644 --- a/rr/soa.js +++ b/rr/soa.js @@ -85,6 +85,27 @@ class SOA extends RR { } /****** IMPORTERS *******/ + fromBind (str) { + // example.com TTL IN SOA mname rname serial refresh retry expire minimum + const [ owner, ttl, c, type, mname, rname, serial, refresh, retry, expire, minimum ] = str.split(/[\s+]/) + + const bits = { + owner, + ttl : parseInt(ttl) || parseInt(minimum), + class : c, + type, + mname, + rname, + serial : parseInt(serial , 10), + refresh: parseInt(refresh, 10), + retry : parseInt(retry , 10), + expire : parseInt(expire , 10), + minimum: parseInt(minimum, 10), + } + // console.log(bits) + return new this.constructor(bits) + } + fromTinydns (str) { // Zfqdn:mname:rname:ser:ref:ret:exp:min:ttl:time:lo const [ fqdn, mname, rname, ser, ref, ret, exp, min, ttl, ts, loc ] = str.substring(1).split(':') @@ -105,39 +126,10 @@ class SOA extends RR { }) } - fromBind (str) { - /* - $TTL 3600 - $ORIGIN example.com - example.com IN SOA mname rname ( serial refresh retry expire minimum ) - */ - const [ , ttl, , owner, , c, type, mname, rname, , serial, refresh, retry, expire, minimum ] = str.split(/\s+/) - - const bits = { - owner, - ttl : parseInt(ttl , 10), - class : c, - type, - mname, - rname, - serial : parseInt(serial , 10), - refresh: parseInt(refresh, 10), - retry : parseInt(retry , 10), - expire : parseInt(expire , 10), - minimum: parseInt(minimum, 10), - } - // console.log(bits) - return new this.constructor(bits) - } - /****** EXPORTERS *******/ toBind (zone_opts) { const numFields = [ 'serial', 'refresh', 'retry', 'expire', 'minimum' ] - return `$TTL\t${this.get('ttl')}${this.getComment('ttl')} -$ORIGIN\t${this.getFQDN('owner')}${this.getComment('origin')} -${this.getFQDN('owner', zone_opts)}\t${this.get('class')}\tSOA\t${this.getFQDN('mname', zone_opts)}\t${this.getFQDN('rname', zone_opts)} ( -${numFields.map(f => '\t\t' + this.get(f) + this.getComment(f) + '\n').join('')}\t\t) -\n` + return `${this.getFQDN('owner', zone_opts)}\t${this.get('ttl')}\t${this.get('class')}\tSOA\t${this.getFQDN('mname', zone_opts)}\t${this.getFQDN('rname', zone_opts)}${numFields.map(f => '\t' + this.get(f) ).join('')}\n` } toMaraDNS () { diff --git a/rr/tlsa.js b/rr/tlsa.js index 52f171b..c942969 100644 --- a/rr/tlsa.js +++ b/rr/tlsa.js @@ -56,16 +56,18 @@ class TLSA extends RR { fromBind (str) { // test.example.com 3600 IN TLSA, usage, selector, match, data - const [ owner, ttl, c, type, usage, selector, match ] = str.split(/\s+/) + const match = str.split(/^([^\s]+)\s+([0-9]+)\s+(\w+)\s+(\w+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+(.*?)\s*$/) + if (!match) throw new Error(`unable to parse TLSA: ${str}`) + const [ owner, ttl, c, type, usage, selector, matchtype, cad ] = match.slice(1) return new this.constructor({ owner : this.fullyQualify(owner), ttl : parseInt(ttl, 10), class : c, - type : type, - 'certificate usage' : parseInt(usage, 10), - selector : parseInt(selector, 10), - 'matching type' : parseInt(match , 10), - 'certificate association data': str.split(/\s+/).slice(7).join(' ').trim(), + type, + 'certificate usage' : parseInt(usage, 10), + selector : parseInt(selector, 10), + 'matching type' : parseInt(matchtype, 10), + 'certificate association data': cad, }) } } diff --git a/rr/txt.js b/rr/txt.js index 2e3cb84..844ed8d 100644 --- a/rr/txt.js +++ b/rr/txt.js @@ -71,13 +71,16 @@ class TXT extends RR { fromBind (str) { // test.example.com 3600 IN TXT "..." - const [ owner, ttl, c, type ] = str.split(/\s+/) + const match = str.split(/^([^\s]+)\s+([0-9]+)\s+(\w+)\s+(\w+)\s+?\s*(.*?)\s*$/) + if (!match) throw new Error(`unable to parse TXT: ${str}`) + const [ owner, ttl, c, type, rdata ] = match.slice(1) + return new this.constructor({ owner, ttl : parseInt(ttl, 10), class: c, type, - data : str.match(/"([^"]+?)"/g).map(s => s.replace(/^"|"$/g, '')).join(''), + data : rdata.match(/"([^"]+?)"/g).map(s => s.replace(/^"|"$/g, '')).join(''), }) } diff --git a/test/a.js b/test/a.js index ff9c3c1..4fa9c11 100644 --- a/test/a.js +++ b/test/a.js @@ -4,30 +4,28 @@ const A = require('../rr/a.js') const base = require('./base') +const defaults = { class: 'IN', ttl: 3600, type: 'A' } + const validRecords = [ { + ...defaults, owner : 'test.example.com.', - ttl : 3600, - class : 'IN', - type : 'A', address: '192.0.2.127', testB : 'test.example.com.\t3600\tIN\tA\t192.0.2.127\n', testT : '+test.example.com:192.0.2.127:3600::\n', }, { + ...defaults, owner : 'test.example.com.', ttl : 2147483647, - class : 'IN', - type : 'A', address: '192.0.2.127', testB : 'test.example.com.\t2147483647\tIN\tA\t192.0.2.127\n', testT : '+test.example.com:192.0.2.127:2147483647::\n', }, { + ...defaults, owner : 'a.', ttl : 86400, - class : 'IN', - type : 'A', address: '192.0.2.127', testB : 'a.\t86400\tIN\tA\t192.0.2.127\n', testT : '+a:192.0.2.127:86400::\n', @@ -36,6 +34,7 @@ const validRecords = [ const moreValid = [ { + ...defaults, owner: '*.example.com.', testB: '*.example.com.\t3600\tIN\tA\t192.0.2.127\n', testT: '+*.example.com:192.0.2.127:3600::\n', @@ -49,24 +48,21 @@ for (let i = 0; i < moreValid.length; i++) { } const invalidRecords = [ - { owner: '' }, - { owner: 'something*' }, - { owner: 'some*thing' }, - { owner: '*something' }, - { owner: 'something.*' }, - { owner: 'a.m.' }, - { owner: 'something.test.' }, - { address: 'hosts.not.valid.here' }, - { address: '' }, - { address: undefined }, - { address: '1.x.2.3' }, - { address: '.1.2.3' }, - { address: '0.0.0.0' }, - { type: '' }, - { type: undefined }, - { ttl: '' }, - { ttl: -299 }, - { ttl: 2147483648 }, + { ...defaults, owner: '', msg: /RFC/ }, + { ...defaults, owner: 'something*', msg: /fully/ }, + { ...defaults, owner: 'some*thing', msg: /fully/ }, + { ...defaults, owner: '*something', msg: /fully/ }, + { ...defaults, owner: 'something.*', msg: /fully/ }, + { ...defaults, address: 'hosts.not.valid.here', msg: /address must be IPv4/ }, + { ...defaults, address: '', msg: /address is required/ }, + { ...defaults, address: undefined, msg: /address is required/ }, + { ...defaults, address: '1.x.2.3', msg: /address must be IPv4/ }, + { ...defaults, address: '.1.2.3', msg: /address must be IPv4/ }, + { ...defaults, type: '', msg: /not supported/ }, + { ...defaults, type: undefined, msg: /type undefined not supported/ }, + { ...defaults, ttl: '', msg: /TTL must be numeric/ }, + { ...defaults, ttl: -299, msg: /TTL must be a 32-bit integer/ }, + { ...defaults, ttl: 2147483648, msg: /TTL must be a 32-bit integer/ }, ] // copy invalid properties to a valid object diff --git a/test/aaaa.js b/test/aaaa.js index 6fa18be..ddf478b 100644 --- a/test/aaaa.js +++ b/test/aaaa.js @@ -4,13 +4,13 @@ const assert = require('assert') const base = require('./base') const AAAA = require('../rr/aaaa.js') +const defaults = { class: 'IN', ttl: 3600, type: 'AAAA' } + const validRecords = [ { - class : 'IN', + ...defaults, owner : 'test.example.com.', - type : 'AAAA', address: '2001:0db8:0020:000a:0000:0000:0000:0004', - ttl : 3600, testB : 'test.example.com.\t3600\tIN\tAAAA\t2001:db8:20:a::4\n', testT : ':test.example.com:28:\\040\\001\\015\\270\\000\\040\\000\\012\\000\\000\\000\\000\\000\\000\\000\\004:3600::\n', }, @@ -18,11 +18,10 @@ const validRecords = [ const invalidRecords = [ { - class : 'IN', - owner : 'test.example.com', - type : 'AAAA', + ...defaults, + owner : 'test.example.com.', address: '192.0.2.204', - ttl : 3600, + msg : /address must be IPv6/, }, ] diff --git a/test/base.js b/test/base.js index 0b1505c..c04745b 100644 --- a/test/base.js +++ b/test/base.js @@ -25,15 +25,12 @@ exports.invalid = (type, invalidRecords, defaults) => { for (const inv of invalidRecords) { if (defaults) inv.default = defaults it(`throws on record (${inv.owner})`, async function () { - try { - assert.deepEqual(new type(inv), null) - } - catch (e) { - if (process.env.DEBUG) console.error(e.message) - assert.ok(e) - return - } - throw new Error(`failed to throw`) + assert.throws(() => { + new type(inv) + }, + { + message: inv.msg, + }) }) } }) diff --git a/test/caa.js b/test/caa.js index 6429bdc..423adfe 100644 --- a/test/caa.js +++ b/test/caa.js @@ -38,26 +38,42 @@ const validRecords = [ testB: 'example.net.\t86400\tIN\tCAA\t0\tissuewild\t"https://letsencrypt.org"\n', testT: ':example.net:257:\\000\\011issuewild"https\\072\\057\\057letsencrypt.org":86400::\n', }, + { + owner: 'certs.example.com.', + ttl : 86400, + type : 'CAA', + flags: 0, + tag : 'issue', + value: 'ca1.example.net', + testB: 'certs.example.com.\t86400\tIN\tCAA\t0\tissue\t"ca1.example.net"\n', + testT: ':certs.example.com:257:\\000\\005issue"ca1.example.net":86400::\n', + }, ] const invalidRecords = [ { - owner: 'example.com', + owner: 'example.com.', + type : 'CAA', flags: 128, - tag : 'issue', + tag : 'iodef', value: 'letsencrypt.org', // missing iodef prefix + msg : /RFC/, }, { - owner: 'example.com', + owner: 'example.com.', + type : 'CAA', flags: 128, tag : 'invalid', // invalid value: 'http://letsencrypt.org', + msg : /RFC/, }, { - owner: 'example.com', + owner: 'example.com.', + type : 'CAA', flags: 15, // invalid tag : 'issue', value: 'http://letsencrypt.org', + msg : /RFC/, }, ] diff --git a/test/cname.js b/test/cname.js index aa8d3b2..f2fabe2 100644 --- a/test/cname.js +++ b/test/cname.js @@ -5,13 +5,13 @@ const base = require('./base') const CNAME = require('../rr/cname') +const defaults = { class: 'IN', ttl: 3600, type: 'CNAME' } + const validRecords = [ { - class: 'IN', + ...defaults, owner: 'ns1.example.com.', - type : 'CNAME', cname: 'ns2.example.com.', - ttl : 3600, testB: 'ns1.example.com.\t3600\tIN\tCNAME\tns2.example.com.\n', testT: 'Cns1.example.com:ns2.example.com.:3600::\n', }, @@ -19,11 +19,10 @@ const validRecords = [ const invalidRecords = [ { - class: 'IN', - owner: 'example.com', - type : 'CNAME', + ...defaults, + owner: 'example.com.', cname: '192.0.2.4', // FQDN required - ttl : 3600, + msg : /cname must be a FQDN/, }, ] diff --git a/test/dname.js b/test/dname.js index e87a846..189692d 100644 --- a/test/dname.js +++ b/test/dname.js @@ -5,12 +5,12 @@ const base = require('./base') const DNAME = require('../rr/dname') +const defaults = { class: 'IN', ttl: 86400, type: 'DNAME' } + const validRecords = [ { + ...defaults, owner : '_tcp.example.com.', - ttl : 86400, - class : 'IN', - type : 'DNAME', target: '_tcp.example.net.', testB : '_tcp.example.com.\t86400\tIN\tDNAME\t_tcp.example.net.\n', testT : ':_tcp.example.com:39:\\004\\137tcp\\007example\\003net\\000:86400::\n', @@ -19,10 +19,10 @@ const validRecords = [ const invalidRecords = [ { - owner : 'spf.example.com', - ttl : 3600, - type : 'DNAME', + ...defaults, + owner : 'spf.example.com.', target: '1.2.3.4', // FQDN required + msg : /target must be a domain name/, }, ] diff --git a/test/dnskey.js b/test/dnskey.js index 57ed426..ae3cf5f 100644 --- a/test/dnskey.js +++ b/test/dnskey.js @@ -5,12 +5,12 @@ const base = require('./base') const DNSKEY = require('../rr/dnskey') +const defaults = { class: 'IN', ttl: 3600, type: 'DNSKEY' } + const validRecords = [ { + ...defaults, owner : 'example.com.', - class : 'IN', - ttl : 3600, - type : 'DNSKEY', flags : 256, protocol : 3, algorithm: 5, @@ -22,14 +22,16 @@ const validRecords = [ const invalidRecords = [ { - owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', algorithm: 6, // invalid + msg : /flags invalid/, }, ] describe('DNSKEY record', function () { base.valid(DNSKEY, validRecords) - base.invalid(DNSKEY, invalidRecords, { ttl: 3600 }) + base.invalid(DNSKEY, invalidRecords) base.getDescription(DNSKEY) base.getRFCs(DNSKEY, validRecords[0]) diff --git a/test/ds.js b/test/ds.js index b58ddbf..334dcc1 100644 --- a/test/ds.js +++ b/test/ds.js @@ -5,12 +5,12 @@ const base = require('./base') const DS = require('../rr/ds') +const defaults = { class: 'IN', ttl: 3600, type: 'DS' } + const validRecords = [ { + ...defaults, owner : 'dskey.example.com.', - class : 'IN', - type : 'DS', - ttl : 3600, 'key tag' : 60485, algorithm : 5, 'digest type': 1, @@ -22,14 +22,16 @@ const validRecords = [ const invalidRecords = [ { - owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', algorithm: 6, // invalid + msg : /key tag is required/, }, ] describe('DS record', function () { base.valid(DS, validRecords) - base.invalid(DS, invalidRecords, { ttl: 3600 }) + base.invalid(DS, invalidRecords) base.getDescription(DS) base.getRFCs(DS, validRecords[0]) diff --git a/test/hinfo.js b/test/hinfo.js index 6fe05d7..96d18bc 100644 --- a/test/hinfo.js +++ b/test/hinfo.js @@ -5,24 +5,22 @@ const base = require('./base') const HINFO = require('../rr/hinfo') +const defaults = { class: 'IN', ttl: 86400, type: 'HINFO' } + const validRecords = [ { - class: 'IN', - type : 'HINFO', + ...defaults, owner: 'server-under-my-desk.example.com.', cpu : 'PDP-11/73', os : 'UNIX', - ttl : 86400, testB: 'server-under-my-desk.example.com.\t86400\tIN\tHINFO\t"PDP-11/73"\t"UNIX"\n', // testT : ':server-under-my-desk:13: :86400::\n', }, { - class: 'IN', - type : 'HINFO', + ...defaults, owner: 'sri-nic.arpa.', cpu : 'DEC-2060', os : 'TOPS20', - ttl : 86400, testB: 'sri-nic.arpa.\t86400\tIN\tHINFO\t"DEC-2060"\t"TOPS20"\n', // testT : ':server-under-my-desk:13: :86400::\n', }, @@ -30,10 +28,10 @@ const validRecords = [ const invalidRecords = [ { - owner : 'www.example.com', - type : 'HINFO', + ...defaults, + owner : 'www.example.com.', address: '', - // ttl : 3600, + msg : /Cannot read proper/, }, ] diff --git a/test/loc.js b/test/loc.js index 0a9841e..96aed4d 100644 --- a/test/loc.js +++ b/test/loc.js @@ -6,28 +6,26 @@ const base = require('./base') const LOC = require('../rr/loc') // const TINYDNS = require('../lib/tinydns') +const defaults = { class: 'IN', ttl: 3600, type: 'LOC' } + const validRecords = [ { + ...defaults, owner : 'loc.home.example.com.', - ttl : 3600, - class : 'IN', - type : 'LOC', address: '47 43 47 N 122 21 35 W 132m 100m 100m 2m', testB : `loc.home.example.com.\t3600\tIN\tLOC\t47 43 47 N 122 21 35 W 132m 100m 100m 2m\n`, testT : ':loc.home.example.com:29:\\000\\024\\024\\042\\212\\075\\337\\070\\145\\276\\224\\150\\000\\230\\312\\020:3600::\n', }, { + ...defaults, owner : 'cambridge-net.kei.com.', - ttl : 3600, - type : 'LOC', address: '42 21 54 N 71 6 18 W -24m 30m', testB : 'cambridge-net.kei.com.\t3600\tIN\tLOC\t42 21 54 N 71 6 18 W -24m 30m\n', testT : ':cambridge-net.kei.com:29:\\000\\063\\000\\000\\211\\027\\055\\320\\160\\276\\025\\360\\000\\230\\215\\040:3600::\n', }, { + ...defaults, owner : 'rwy04l.logan-airport.boston.', - ttl : 3600, - type : 'LOC', address: '42 21 28.764 N 71 0 51.617 W -44m 2000m', testB : 'rwy04l.logan-airport.boston.\t3600\tIN\tLOC\t42 21 28.764 N 71 0 51.617 W -44m 2000m\n', testT : ':rwy04l.logan-airport.boston:29:\\000\\045\\000\\000\\211\\026\\313\\074\\160\\303\\020\\337\\000\\230\\205\\120:3600::\n', @@ -36,11 +34,10 @@ const validRecords = [ const invalidRecords = [ { - owner : 'server.example.com', - ttl : 3600, - class : 'IN', - type : 'LOC', + ...defaults, + owner : 'server.example.com.', address: '', // empty + msg : /address is required/, }, ] diff --git a/test/mx.js b/test/mx.js index dddb1d9..f52f0d4 100644 --- a/test/mx.js +++ b/test/mx.js @@ -5,21 +5,20 @@ const base = require('./base') const MX = require('../rr/mx') +const defaults = { class: 'IN', ttl: 3600, type: 'MX', preference: 0 } + const validRecords = [ { + ...defaults, owner : 'test.example.com.', - class : 'IN', - type : 'MX', - ttl : 3600, preference: 0, exchange : 'mail.example.com.', testB : 'test.example.com.\t3600\tIN\tMX\t0\tmail.example.com.\n', testT : '@test.example.com::mail.example.com:0:3600::\n', }, { + ...defaults, owner : 'www.example.com.', - class : 'IN', - type : 'MX', ttl : 86400, preference: 0, exchange : '.', // null MX, RFC 7505 @@ -30,24 +29,28 @@ const validRecords = [ const invalidRecords = [ { - owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', exchange: 'not-full-qualified.example.com', + msg : /exchange must be fully qualified/, }, { - owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', exchange: '192.0.2.1', + msg : /exchange must be a FQDN/, }, { - owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', exchange: '-blah', + msg : /exchange must be fully qualified/, }, ] -const defaults = { ttl: 3600, preference: 0 } - describe('MX record', function () { base.valid(MX, validRecords) - base.invalid(MX, invalidRecords, defaults) + base.invalid(MX, invalidRecords) base.getDescription(MX) base.getRFCs(MX, validRecords[0]) diff --git a/test/ns.js b/test/ns.js index 17f09fb..7875336 100644 --- a/test/ns.js +++ b/test/ns.js @@ -5,12 +5,12 @@ const base = require('./base') const NS = require('../rr/ns') +const defaults = { class: 'IN', ttl: 3600, type: 'NS' } + const validRecords = [ { + ...defaults, owner: 'example.com.', - ttl : 3600, - class: 'IN', - type : 'NS', dname: 'ns1.example.com.', testB: 'example.com.\t3600\tIN\tNS\tns1.example.com.\n', testT: '&example.com::ns1.example.com:3600::\n', @@ -19,14 +19,16 @@ const validRecords = [ const invalidRecords = [ { - owner: 'example.com', + ...defaults, + owner: 'example.com.', dname: '1.2.3.4', // FQDN required + msg : /dname must be fully qualified/, }, ] describe('NS record', function () { base.valid(NS, validRecords) - base.invalid(NS, invalidRecords, { ttl: 3600 }) + base.invalid(NS, invalidRecords) base.getDescription(NS) base.getRFCs(NS, validRecords[0]) diff --git a/test/openpgpkey.js b/test/openpgpkey.js new file mode 100644 index 0000000..a588196 --- /dev/null +++ b/test/openpgpkey.js @@ -0,0 +1,35 @@ + +const base = require('./base') + +const OPENPGPKEY = require('../rr/openpgpkey.js') + +const validRecords = [ + // { + // owner : 'example.com.', + // ttl : 3600, + // class : 'IN', + // type : 'OPENPGPKEY', + // 'public key': ``, + // testB : '', + // testT : '', + // }, +] + +const invalidRecords = [ +] + +describe('OPENPGPKEY record', function () { + base.valid(OPENPGPKEY, validRecords) + base.invalid(OPENPGPKEY, invalidRecords, { ttl: 3600 }) + + base.getDescription(OPENPGPKEY) + base.getRFCs(OPENPGPKEY) + base.getFields(OPENPGPKEY, [ 'public key' ]) + base.getTypeId(OPENPGPKEY, 61) + + base.toBind(OPENPGPKEY, validRecords) + // base.toTinydns(OPENPGPKEY, validRecords) + + base.fromBind(OPENPGPKEY, validRecords) + // base.fromTinydns(OPENPGPKEY, validRecords) +}) diff --git a/test/ptr.js b/test/ptr.js index 4c7e2be..ef02b17 100644 --- a/test/ptr.js +++ b/test/ptr.js @@ -5,13 +5,13 @@ const base = require('./base') const PTR = require('../rr/ptr') +const defaults = { class: 'IN', ttl: 86400, type: 'PTR' } + const validRecords = [ { - class: 'IN', + ...defaults, owner: '2.2.0.192.in-addr.arpa.', - type : 'PTR', dname: 'dhcp.example.com.', - ttl : 86400, testB: '2.2.0.192.in-addr.arpa.\t86400\tIN\tPTR\tdhcp.example.com.\n', testT: '^2.2.0.192.in-addr.arpa:dhcp.example.com:86400::\n', }, @@ -19,11 +19,10 @@ const validRecords = [ const invalidRecords = [ { - class: 'IN', - owner: 'example.com', - type : 'PTR', + ...defaults, + owner: 'example.com.', dname: '192.0.2.4', // FQDN required - ttl : 3600, + msg : /dname must be fully qualified/, }, ] diff --git a/test/smimea.js b/test/smimea.js index 07ea668..1562147 100644 --- a/test/smimea.js +++ b/test/smimea.js @@ -5,11 +5,12 @@ const base = require('./base') const SMIMEA = require('../rr/tlsa') +const defaults = { class: 'IN', ttl: 3600, type: 'SMIMEA' } + const validRecords = [ { - class : 'IN', + ...defaults, owner : '_443._tcp.www.example.com.', - type : 'SMIMEA', ttl : 3600, 'certificate usage' : 0, selector : 0, @@ -22,14 +23,16 @@ const validRecords = [ const invalidRecords = [ { - // owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', selector: 6, // invalid + msg : /certificate usage invalid/, }, ] describe('SMIMEA record', function () { - base.valid(SMIMEA, validRecords, { ttl: 3600 }) - base.invalid(SMIMEA, invalidRecords, { ttl: 3600 }) + base.valid(SMIMEA, validRecords) + base.invalid(SMIMEA, invalidRecords) base.getDescription(SMIMEA) base.getRFCs(SMIMEA, validRecords[0]) diff --git a/test/soa.js b/test/soa.js index a6fa043..a2c90f4 100644 --- a/test/soa.js +++ b/test/soa.js @@ -5,11 +5,12 @@ const base = require('./base') const SOA = require('../rr/soa') +const defaults = { class: 'IN', ttl: 3600, type: 'SOA' } + const validRecords = [ { - class : 'IN', + ...defaults, owner : 'example.com.', - type : 'SOA', mname : 'ns1.example.com.', rname : 'matt.example.com.', serial : 1, @@ -17,32 +18,35 @@ const validRecords = [ retry : 3600, expire : 1209600, minimum: 3600, - ttl : 3600, - testB : `$TTL\t3600 -$ORIGIN\texample.com. -example.com.\tIN\tSOA\tns1.example.com.\tmatt.example.com. ( -\t\t1 -\t\t7200 -\t\t3600 -\t\t1209600 -\t\t3600 -\t\t)\n\n`, - testT: 'Zexample.com:ns1.example.com:matt.example.com:1:7200:3600:1209600:3600:3600::\n', + testB : `example.com.\t3600\tIN\tSOA\tns1.example.com.\tmatt.example.com.\t1\t7200\t3600\t1209600\t3600\n`, + testT : 'Zexample.com:ns1.example.com:matt.example.com:1:7200:3600:1209600:3600:3600::\n', + }, + { + ...defaults, + owner : '2.example.com.', + mname : 'ns2.example.com.', + rname : 'matt.example.com.', + serial : 1, + refresh: 7200, + retry : 3600, + expire : 1209600, + minimum: 3600, + testB : `2.example.com.\t3600\tIN\tSOA\tns2.example.com.\tmatt.example.com.\t1\t7200\t3600\t1209600\t3600\n`, + testT : 'Z2.example.com:ns2.example.com:matt.example.com:1:7200:3600:1209600:3600:3600::\n', }, ] const invalidRecords = [ { - owner : 'example.com', - ttl : 3600, - class : 'IN', - type : 'SOA', + ...defaults, + owner : 'example.com.', mname : 'ns1.example.com.', rname : 'matt.example.com.', serial : 4294967296, refresh: 7200, retry : 3600, expire : 1209600, + msg : /serial must be a 32-bit integer/, }, ] diff --git a/test/spf.js b/test/spf.js index 7612638..ffa2616 100644 --- a/test/spf.js +++ b/test/spf.js @@ -5,11 +5,12 @@ const base = require('./base') const SPF = require('../rr/spf') +const defaults = { class: 'IN', ttl: 86400, type: 'SPF' } + const validRecords = [ { + ...defaults, owner: 'example.com.', - ttl : 86400, - type : 'SPF', data : 'v=spf1 mx a include:mx.example.com -all', testB: 'example.com.\t86400\tIN\tSPF\t"v=spf1 mx a include:mx.example.com -all"\n', testT: ':example.com:99:v=spf1 mx a include\\072mx.example.com -all:86400::\n', diff --git a/test/srv.js b/test/srv.js index 8765f3c..cb8e1bb 100644 --- a/test/srv.js +++ b/test/srv.js @@ -5,12 +5,12 @@ const base = require('./base') const SRV = require('../rr/srv') +const defaults = { class: 'IN', ttl: 3600, type: 'SRV' } + const validRecords = [ { owner : '_imaps._tcp.example.com.', - ttl : 3600, - class : 'IN', - type : 'SRV', + ...defaults, priority: 1, weight : 0, port : 993, @@ -22,18 +22,22 @@ const validRecords = [ const invalidRecords = [ { - owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', target: 'not-full-qualified.example.com', + msg : /must be a 16-bit integer/, }, { - owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', target: '192.168.0.1', + msg : /must be a 16-bit integer/, }, ] describe('SRV record', function () { base.valid(SRV, validRecords) - base.invalid(SRV, invalidRecords, { ttl: 3600 }) + base.invalid(SRV, invalidRecords) base.getDescription(SRV) base.getRFCs(SRV, validRecords[0]) diff --git a/test/tlsa.js b/test/tlsa.js index 0a17c7a..c54ce48 100644 --- a/test/tlsa.js +++ b/test/tlsa.js @@ -5,43 +5,43 @@ const base = require('./base') const TLSA = require('../rr/tlsa') +const defaults = { class: 'IN', ttl: 3600, type: 'TLSA' } + const validRecords = [ { - class : 'IN', + ...defaults, owner : '_443._tcp.www.example.com.', - type : 'TLSA', - ttl : 3600, 'certificate usage' : 0, selector : 0, 'matching type' : 1, - 'certificate association data': '( d2abde240d7cd3ee6b4b28c54df034b9 7983a1d16e8a410e4561cb106618e971 )', - testB : '_443._tcp.www.example.com.\t3600\tIN\tTLSA\t0\t0\t1\t( d2abde240d7cd3ee6b4b28c54df034b9 7983a1d16e8a410e4561cb106618e971 )\n', + 'certificate association data': 'd2abde240d7cd3ee6b4b28c54df034b9 7983a1d16e8a410e4561cb106618e971', + testB : '_443._tcp.www.example.com.\t3600\tIN\tTLSA\t0\t0\t1\td2abde240d7cd3ee6b4b28c54df034b9 7983a1d16e8a410e4561cb106618e971\n', // testT : '', }, { - class : 'IN', + ...defaults, owner : '_443._tcp.www.example.com.', - type : 'TLSA', - ttl : 3600, 'certificate usage' : 1, selector : 1, 'matching type' : 2, - 'certificate association data': '( 92003ba34942dc74152e2f2c408d29ec a5a520e7f2e06bb944f4dca346baf63c 1b177615d466f6c4b71c216a50292bd5 8c9ebdd2f74e38fe51ffd48c43326cbc )', - testB : `_443._tcp.www.example.com.\t3600\tIN\tTLSA\t1\t1\t2\t( 92003ba34942dc74152e2f2c408d29ec a5a520e7f2e06bb944f4dca346baf63c 1b177615d466f6c4b71c216a50292bd5 8c9ebdd2f74e38fe51ffd48c43326cbc )\n`, + 'certificate association data': '92003ba34942dc74152e2f2c408d29ec a5a520e7f2e06bb944f4dca346baf63c 1b177615d466f6c4b71c216a50292bd5 8c9ebdd2f74e38fe51ffd48c43326cbc', + testB : `_443._tcp.www.example.com.\t3600\tIN\tTLSA\t1\t1\t2\t92003ba34942dc74152e2f2c408d29ec a5a520e7f2e06bb944f4dca346baf63c 1b177615d466f6c4b71c216a50292bd5 8c9ebdd2f74e38fe51ffd48c43326cbc\n`, testT : '', }, ] const invalidRecords = [ { - // owner : 'test.example.com', + ...defaults, + owner : 'test.example.com.', selector: 6, // invalid + msg : /RFC/, }, ] describe('TLSA record', function () { base.valid(TLSA, validRecords) - base.invalid(TLSA, invalidRecords, { ttl: 3600 }) + base.invalid(TLSA, invalidRecords) base.getDescription(TLSA) base.getRFCs(TLSA, validRecords[0])