diff --git a/CHANGELOG.md b/CHANGELOG.md index b7c6275..97a633d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,4 +2,11 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.1](https://github.com/NicTool/dns-nameserver/compare/v0.1.0...v0.1.1) (2022-04-07) + + +### Features + +* add knot config parser ([a15014f](https://github.com/NicTool/dns-nameserver/commit/a15014f92268e361a4a37952d6e2c66173395083)) + ## 0.1.0 (2022-04-02) diff --git a/README.md b/README.md index 783a6bb..6cd8578 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Name servers have configuration files, each with their own format. This package - config parser - [x] bind - [ ] nsd - - [ ] knot + - [x] knot - [ ] maradns - [ ] tinydns - [ ] powerdns diff --git a/index.js b/index.js index 3560e44..4e5fa8b 100644 --- a/index.js +++ b/index.js @@ -23,3 +23,33 @@ exports.parseBindConfig = async str => { .filter(z => z[0] && z[0].type) // weed out nulls .map(z => z[0]) // remove array nesting } + +exports.parseKnotConfig = async str => { + + // eslint-disable-next-line node/no-unpublished-require + const grammar = nearley.Grammar.fromCompiled(require('./dist/knot.js')) + grammar.start = 'main' + + const parser = new nearley.Parser(grammar) + parser.feed(str) + if (!str.endsWith(os.EOL)) parser.feed(os.EOL) + + if (parser.results.length === 0) return [] + + const r = {} + + const sections = [ 'acl', 'key', 'log', 'policy', 'remote', 'server', 'template', 'zone' ] + + parser.results[0][0] + .filter(z => z !== null) + .map(z => { + for (const s of sections) { + if (z[s]) { + if (!r[s]) r[s] = [] + r[s].push(...z[s]) + } + } + }) + + return r +} diff --git a/package.json b/package.json index b094fa2..d07bacb 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,20 @@ { "name": "dns-nameserver", - "version": "0.1.0", + "version": "0.1.1", "description": "DNS Nameserver", "main": "index.js", "bin": {}, - "engines" : { "node" : ">=12.0" }, + "engines": { + "node": ">=12.0" + }, "scripts": { "bind": "npx -p nearley nearleyc src/bind.ne -o dist/bind.js", "knot": "npx -p nearley nearleyc src/knot.ne -o dist/knot.js", - "nsd": "npx -p nearley nearleyc src/nsd.ne -o dist/nsd.js", + "nsd": "npx -p nearley nearleyc src/nsd.ne -o dist/nsd.js", "mara": "npx -p nearley nearleyc src/mara.ne -o dist/mara.js", "lint": "npx eslint index.js test/*.js", "lintfix": "npx eslint --fix index.js test/*.js", - "postinstall": "npm run bind", + "postinstall": "npm run bind && npm run knot", "release": "npx standard-version", "test": "npx mocha", "versions": "npx dependency-version-checker check" diff --git a/src/knot.ne b/src/knot.ne new file mode 100644 index 0000000..3b0ae83 --- /dev/null +++ b/src/knot.ne @@ -0,0 +1,57 @@ + +main -> ( + blank {% id %} + | comment {% id %} + | server {% id %} + | zone {% id %} + | log {% id %} + | acl {% id %} + | key {% id %} + | policy {% id %} + | remote {% id %} + | template {% id %} + ):* + +blank -> _ eol {% asNull %} +comment -> _ "#" notEol eol {% asNull %} + +key_val -> __ word ":" __ word eol {% asKeyVal %} + +array_block -> __ "-" __ word ":" __ word _ eol (key_val {% id %}):* eol_ {% asArray %} + +acl -> "acl" ":" eol (array_block {% id %}):* eol_ {% asTopObj %} +key -> "key" ":" eol (array_block {% id %}):* eol_ {% asTopObj %} +policy -> "policy" ":" eol (key_val {% id %}):* eol {% asTopObj %} +remote -> "remote" ":" eol (key_val {% id %}):* eol {% asTopObj %} +server -> "server" ":" eol (key_val {% id %}):* eol {% asTopObj %} +template -> "template" ":" eol (array_block {% id %}):* eol_ {% asTopObj %} +zone -> "zone" ":" eol (array_block {% id %}):* eol_ {% asTopObj %} +log -> "log" ":" eol (array_block {% id %}):* eol_ {% asTopObj %} + +_ -> [ \t]:* +__ -> [ \t]:+ +eol -> [\r\n] {% id %} +eol_ -> [\r\n]:* {% id %} +hex -> [A-Fa-f0-9] {% id %} +notEol -> [^\r\n]:* +word -> [A-Za-z-0-9_.@:/-]:+ {% flatten %} +dqstring -> "\"" dqcharacters "\"" {% d => Array.isArray(d[1]) ? d[1].join("") : d[1] %} +dqcharacters -> dqcharacter {% id %} + | dqcharacter dqcharacters {% d => [d[0], ...d[1]] %} +dqcharacter -> [^\"\\] {% id %} + | "\\" escape {% d => d[1] %} +escape -> ["\\/bfnrt] {% id %} + | "u" hex hex hex hex {% (d) => String.fromCharCode(`0x${d.slice(1,5).join("")}`) %} + +@{% +function asArray (d) { + return Object.assign({ [d[3]]: d[6] }, ...d[9].filter(e => 'object' === typeof e)) +} +function asTopObj (d) { return { [d[0]]: d[3] } } +function asKeyVal (d) { return { [d[1]]: d[4] } } +function asNull (d) { return null; } +function flatten (d) { + if (!d) return '' + return Array.isArray(d) ? d.flat(Infinity).join('') : d +} +%} \ No newline at end of file diff --git a/test/fixtures/bind/named.conf-ztrax-master b/test/fixtures/bind/named.conf-ztrax-master new file mode 100644 index 0000000..77c4091 --- /dev/null +++ b/test/fixtures/bind/named.conf-ztrax-master @@ -0,0 +1,69 @@ +// MASTER & CACHING NAME SERVER for EXAMPLE, INC. +// maintained by: me myself alone +// CHANGELOG: +// 1. 9 july 2003 - did something +// 2. 16 july 2003 - did something else +// 3. 23 july 2003 - did something more +// +options { + directory "/var/named"; + // version statement - inhibited for security + // (avoids hacking any known weaknesses) + version "get lost"; + // optional - disables all transfers + // slaves allowed in zone clauses + allow-transfer {"none";}; + // Closed DNS - permits only local IPs to issue recursive queries + // remove if an Open DNS required to support all users + // or add additional ranges + allow-recursion {192.168.3.0/24;}; +}; +// +// log to /var/log/named/example.log all events from +// info UP in severity (no debug) +// defaults to use 3 files in rotation +// BIND 8.x logging MUST COME FIRST in this file +// BIND 9.x parses the whole file before using the log +// failure messages up to this point are in (syslog) +// typically /var/log/messages +// + logging{ + channel example_log{ + file "/var/log/named/example.log" versions 3 size 2m; + severity info; + print-severity yes; + print-time yes; + print-category yes; + }; + category default{ + example_log; + }; +}; +// required zone for recursive queries +zone "." { + type hint; + file "root.servers"; +}; +zone "example.com" in{ + type master; + file "master/master.example.com"; + // enable slaves only + allow-transfer {192.168.23.1;192.168.23.2;}; +}; +// required local host domain +zone "localhost" in{ + type master; + file "master.localhost"; + allow-update{none;}; +}; +// localhost reverse map +zone "0.0.127.in-addr.arpa" in{ + type master; + file "localhost.rev"; + allow-update{none;}; +}; +// reverse map for class C 192.168.0.0 +zone "0.168.192.IN-ADDR.ARPA" in{ + type master; + file "192.168.0.rev"; +}; diff --git a/test/fixtures/knot/knot-flex.conf b/test/fixtures/knot/knot-flex.conf new file mode 100644 index 0000000..856524f --- /dev/null +++ b/test/fixtures/knot/knot-flex.conf @@ -0,0 +1,25 @@ +server: + listen: 0.0.0.0@53 + listen: ::@53 + +zone: + - domain: example.com + storage: /var/lib/knot/zones/ + file: example.com.zone + - domain: example2.com + file: example2.com.zone + + - domain: example3.com + file: example3.com.zone + +zone: + - domain: example4.com + file: example4.com.zone + - domain: example5.com + file: example5.com.zone + +log: + - target: syslog + any: info + - target: /var/log/knotd.log + any: info \ No newline at end of file diff --git a/test/fixtures/knot/knotd-v2.conf b/test/fixtures/knot/knotd-v2.conf new file mode 100644 index 0000000..439cb12 --- /dev/null +++ b/test/fixtures/knot/knotd-v2.conf @@ -0,0 +1,14 @@ +# Example of a very simple Knot DNS configuration. + +server: + listen: 0.0.0.0@53 + listen: ::@53 + +zone: + - domain: example.com + storage: /var/lib/knot/zones/ + file: example.com.zone + +log: + - target: syslog + any: info \ No newline at end of file diff --git a/test/fixtures/knot/knotd-v3.conf b/test/fixtures/knot/knotd-v3.conf new file mode 100644 index 0000000..439cb12 --- /dev/null +++ b/test/fixtures/knot/knotd-v3.conf @@ -0,0 +1,14 @@ +# Example of a very simple Knot DNS configuration. + +server: + listen: 0.0.0.0@53 + listen: ::@53 + +zone: + - domain: example.com + storage: /var/lib/knot/zones/ + file: example.com.zone + +log: + - target: syslog + any: info \ No newline at end of file diff --git a/test/fixtures/knot/ns2.cadillac.net.conf b/test/fixtures/knot/ns2.cadillac.net.conf new file mode 100644 index 0000000..7a86e35 --- /dev/null +++ b/test/fixtures/knot/ns2.cadillac.net.conf @@ -0,0 +1,36 @@ +zone: + - domain: 0.0.127.in-addr.arpa + file: /data/knot/0.0.127.in-addr.arpa +zone: + - domain: 160/27.51.128.66.in-addr.arpa + file: /data/knot/160/27.51.128.66.in-addr.arpa +zone: + - domain: horseshoeing.org + file: /data/knot/horseshoeing.org +zone: + - domain: txalamako.org + file: /data/knot/txalamako.org +zone: + - domain: theartfarm.com + file: /data/knot/theartfarm.com +zone: + - domain: _report._dmarc.theartfarm.com + file: /data/knot/_report._dmarc.theartfarm.com +zone: + - domain: w8ct.net + file: /data/knot/w8ct.net +zone: + - domain: tikismikis.org + file: /data/knot/tikismikis.org +zone: + - domain: lynboyer.com + file: /data/knot/lynboyer.com +zone: + - domain: _tcp.theartfarm.com + file: /data/knot/_tcp.theartfarm.com +zone: + - domain: horsenetwork.com + file: /data/knot/horsenetwork.com +zone: + - domain: ctacllc.com + file: /data/knot/ctacllc.com \ No newline at end of file diff --git a/test/index.js b/test/index.js index 75d6ab1..d82ad70 100644 --- a/test/index.js +++ b/test/index.js @@ -7,8 +7,8 @@ const ns = require('../index.js') describe('parseBindConfig', function () { - it('parses named.conf zone file', async () => { - const file = './test/fixtures/named.conf-ztrax-master' + it('parses named.conf file', async () => { + const file = './test/fixtures/bind/named.conf-ztrax-master' const buf = await fs.readFile(file) const r = await ns.parseBindConfig(buf.toString()) @@ -16,3 +16,108 @@ describe('parseBindConfig', function () { assert.equal(r.length, 5) }) }) + + +describe('parseKnotConfig', function () { + + it('parses v2 knot.conf', async () => { + const file = './test/fixtures/knot/knotd-v2.conf' + const buf = await fs.readFile(file) + + const r = await ns.parseKnotConfig(buf.toString()) + // console.dir(r, { depth: null }) + assert.deepEqual(r, { + server: [{ listen: '0.0.0.0@53' }, { listen: '::@53' }], + zone : [ + { + domain : 'example.com', + storage: '/var/lib/knot/zones/', + file : 'example.com.zone', + }, + ], + log: [{ target: 'syslog', any: 'info' }], + }) + }) + + it('parses v3 knot.conf', async () => { + const file = './test/fixtures/knot/knotd-v3.conf' + const buf = await fs.readFile(file) + + const r = await ns.parseKnotConfig(buf.toString()) + // console.dir(r, { depth: null }) + assert.deepEqual(r, { + server: [{ listen: '0.0.0.0@53' }, { listen: '::@53' }], + zone : [ + { + domain : 'example.com', + storage: '/var/lib/knot/zones/', + file : 'example.com.zone', + }, + ], + log: [{ target: 'syslog', any: 'info' }], + }) + }) + + it('parses knot-flex.conf', async () => { + const file = './test/fixtures/knot/knot-flex.conf' + const buf = await fs.readFile(file) + + const r = await ns.parseKnotConfig(buf.toString()) + // console.dir(r, { depth: null }) + assert.deepStrictEqual(r, { + server: [{ listen: '0.0.0.0@53' }, { listen: '::@53' }], + zone : [ + { + domain : 'example.com', + storage: '/var/lib/knot/zones/', + file : 'example.com.zone', + }, + { domain: 'example2.com', file: 'example2.com.zone' }, + { domain: 'example3.com', file: 'example3.com.zone' }, + { domain: 'example4.com', file: 'example4.com.zone' }, + { domain: 'example5.com', file: 'example5.com.zone' }, + ], + log: [ + { target: 'syslog', any: 'info' }, + { target: '/var/log/knotd.log', any: 'info' }, + ], + }) + }) + + it('parses ns2.cadillac.net.conf', async () => { + const file = './test/fixtures/knot/ns2.cadillac.net.conf' + const buf = await fs.readFile(file) + + const r = await ns.parseKnotConfig(buf.toString()) + // console.dir(r, { depth: null }) + assert.deepEqual(r, { + zone: [ + { + domain: '0.0.127.in-addr.arpa', + file : '/data/knot/0.0.127.in-addr.arpa', + }, + { + domain: '160/27.51.128.66.in-addr.arpa', + file : '/data/knot/160/27.51.128.66.in-addr.arpa', + }, + { domain: 'horseshoeing.org', file: '/data/knot/horseshoeing.org' }, + { domain: 'txalamako.org', file: '/data/knot/txalamako.org' }, + { domain: 'theartfarm.com', file: '/data/knot/theartfarm.com' }, + { + domain: '_report._dmarc.theartfarm.com', + file : '/data/knot/_report._dmarc.theartfarm.com', + }, + { domain: 'w8ct.net', file: '/data/knot/w8ct.net' }, + { domain: 'tikismikis.org', file: '/data/knot/tikismikis.org' }, + { domain: 'lynboyer.com', file: '/data/knot/lynboyer.com' }, + { + domain: '_tcp.theartfarm.com', + file : '/data/knot/_tcp.theartfarm.com', + }, + { domain: 'horsenetwork.com', file: '/data/knot/horsenetwork.com' }, + { domain: 'ctacllc.com', file: '/data/knot/ctacllc.com' }, + ], + }) + }) + +})