Skip to content

Commit

Permalink
feat: add maradns config parser (#5)
Browse files Browse the repository at this point in the history
* docs(nsd): tidy up and add comments
* split ns implementations into separate files
* nsd: strip double quotes off values
  • Loading branch information
msimerson committed Apr 9, 2022
1 parent 1c3d3bc commit 4496497
Show file tree
Hide file tree
Showing 17 changed files with 533 additions and 276 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

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.3.0](https://github.com/NicTool/dns-nameserver/compare/v0.1.0...v0.3.0) (2022-04-09)


### ⚠ BREAKING CHANGES

* add maradns config parser

### Features

* add maradns config parser ([1b4608e](https://github.com/NicTool/dns-nameserver/commit/1b4608e22b60250a67823851772538418e59d187))
* parsers added for Knot ([#2](https://github.com/NicTool/dns-nameserver/issues/2)) ([139235e](https://github.com/NicTool/dns-nameserver/commit/139235e8acd105ce872cddb22224e1470c972cbe))

## [0.2.0](https://github.com/NicTool/dns-nameserver/compare/v0.1.2...v0.2.0) (2022-04-08)

### [0.1.2](https://github.com/NicTool/dns-nameserver/compare/v0.1.0...v0.1.2) (2022-04-08)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Name servers have configuration files, each with their own format. This package
- [x] bind
- [x] nsd
- [x] knot
- [ ] maradns
- [x] maradns
- [ ] tinydns
- [ ] powerdns
- [ ] config generator
Expand Down
77 changes: 9 additions & 68 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,85 +1,26 @@

const os = require('os')

const nearley = require('nearley')

exports.parseBindConfig = async str => {

// eslint-disable-next-line node/no-unpublished-require
const grammar = nearley.Grammar.fromCompiled(require('./dist/bind.js'))
grammar.start = 'main'

const parser = new nearley.Parser(grammar)
parser.feed(str)
if (!str.endsWith(os.EOL)) parser.feed(os.EOL)

if (parser.length > 1) {
console.error(`ERROR: ambigious parser rule`)
}

if (parser.results.length === 0) return []

return parser.results[0]
.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
}

exports.parseNsdConfig = async str => {

// eslint-disable-next-line node/no-unpublished-require
const grammar = nearley.Grammar.fromCompiled(require('./dist/nsd.js'))
grammar.start = 'main'

const parser = new nearley.Parser(grammar)
parser.feed(str + os.EOL)
if (!str.endsWith(os.EOL)) parser.feed(os.EOL)

if (parser.results.length === 0) return []

const r = {}

const sections = [ 'key', 'pattern', 'remote-control', 'server', 'tls-auth', 'zone' ]
exports.valueCleanup = function (str) {
// strip double quotes
if (str.charAt(0) === '"' && str.charAt(str.length -1) === '"') {
str = str.substr(1,str.length -2)
}

parser.results[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])
}
}
})
if (/^[0-9.]+$/.test(str) && Number(str).toString() === str) {
return Number(str)
}

return r
return str
}
25 changes: 25 additions & 0 deletions lib/bind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

const os = require('os')

const nearley = require('nearley')

exports.parseConfig = async str => {

// eslint-disable-next-line node/no-unpublished-require
const grammar = nearley.Grammar.fromCompiled(require('../dist/bind.js'))
grammar.start = 'main'

const parser = new nearley.Parser(grammar)
parser.feed(str)
if (!str.endsWith(os.EOL)) parser.feed(os.EOL)

if (parser.length > 1) {
console.error(`ERROR: ambigious parser rule`)
}

if (parser.results.length === 0) return []

return parser.results[0]
.filter(z => z[0] && z[0].type) // weed out nulls
.map(z => z[0]) // remove array nesting
}
34 changes: 34 additions & 0 deletions lib/knot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

const os = require('os')

const nearley = require('nearley')

exports.parseConfig = 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
}
31 changes: 31 additions & 0 deletions lib/maradns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

const ns = require('../index')

exports.parseConfig = async str => {
// https://maradns.samiam.org/tutorial/man.mararc.html

const re = {
blank : /^\s*?$/,
comment : /^\s*(?:#)[^\r\n]*?$/,
variable: /^([^ \t]*)\s*=\s*([^\r\n]*)$/,
}

const res = {}

for (const line of str.split(/[\r\n]/)) {

if (re.blank.test(line)) continue
if (re.comment.test(line)) continue

const match = line.match(re.variable)
if (match) {
res[match[1]] = ns.valueCleanup(match[2])
}
else {
console.log(line)
throw ('parser failed')
}
}

return res
}
36 changes: 36 additions & 0 deletions lib/nsd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

const os = require('os')

const nearley = require('nearley')

const ns = require('../index')

exports.parseConfig = async str => {

// eslint-disable-next-line node/no-unpublished-require
const grammar = nearley.Grammar.fromCompiled(require('../dist/nsd.js'))
grammar.start = 'main'

const parser = new nearley.Parser(grammar)
parser.feed(str + os.EOL)
if (!str.endsWith(os.EOL)) parser.feed(os.EOL)

if (parser.results.length === 0) return []

const r = {}

const sections = [ 'key', 'pattern', 'remote-control', 'server', 'tls-auth', 'zone' ]

parser.results[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
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dns-nameserver",
"version": "0.2.0",
"version": "0.3.0",
"description": "DNS Nameserver",
"main": "index.js",
"bin": {},
Expand Down
30 changes: 30 additions & 0 deletions src/mara.ne
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@lexer lexer

main -> ( comment {% id %}
| blank {% id %}
| variable {% id %}
):* {% id %}

blank -> %blank {% asNull %}
comment -> %comment {% asNull %}
variable -> %variable {% asVariable %}

@{%
const moo = require("moo");

const lexer = moo.compile({
variable: /^[^ \t]*\s*=\s*[^\r\n]*?[\r\n]/,
blank : /^\s*?$[\r\n]/,
comment: /^\s*(?:#)[^\r\n]*?[\r\n]/,
});

function asNull (d) { return null }

const varRe = /^([^ \t]*)\s*=\s*([^\r\n]*?)[\r\n]/

function asVariable (d) {
const match = d[0].value.match(varRe)
return { [match[1]]: match[2].trim().replace(/^"(.*)"$/, '$1') }
}

%}
33 changes: 19 additions & 14 deletions src/nsd-moo.ne
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# https://nsd.docs.nlnetlabs.nl/en/latest/manpages/nsd.conf.html
@lexer lexer

main -> (
comment {% id %}
main -> ( comment {% id %}
| blank {% id %}
| key {% id %}
| pattern {% id %}
Expand All @@ -11,15 +11,17 @@ main -> (
| zone {% id %}
):* {% id %}

blank -> %blank {% comment %}
comment -> %comment {% comment %}
key_val -> %key_val {% id %}
key -> %key (key_val {% asKeyValue %} | %comment {% comment %}):* {% asGroup %}
pattern -> %pattern (key_val {% asKeyValue %} | %comment {% comment %}):* {% asGroup %}
remote -> %remote (key_val {% asKeyValue %} | %comment {% comment %}):* {% asGroup %}
server -> %server (key_val {% asKeyValue %} | %comment {% comment %}):* {% asGroup %}
tls -> %tls (key_val {% asKeyValue %} | %comment {% comment %}):* {% asGroup %}
zone -> %zone (key_val {% asKeyValue %} | %comment {% comment %}):* {% asGroup %}
blank -> %blank {% asComment %}
comment -> %comment {% asComment %}
key_val -> %key_val {% asKeyValue %}
attrib -> key_val {% id %}
| comment {% id %}
key -> %key (attrib {% id %}):* {% asGroup %}
pattern -> %pattern (attrib {% id %}):* {% asGroup %}
remote -> %remote (attrib {% id %}):* {% asGroup %}
server -> %server (attrib {% id %}):* {% asGroup %}
tls -> %tls (attrib {% id %}):* {% asGroup %}
zone -> %zone (attrib {% id %}):* {% asGroup %}

@{%
const moo = require("moo");
Expand All @@ -36,16 +38,19 @@ const lexer = moo.compile({
comment: /^\s*(?:#)[^\r\n]*?[\r\n]/,
});

function comment (d) { return null }
function asComment (d) { return null }

function asGroup (d) {
return { [d[0].type]: Object.assign({}, ...d[1].filter(e => typeof e !== 'null')) }
return {
// merge the attributes into a top level "group"
[d[0].type]: Object.assign({}, ...d[1].filter(e => typeof e !== 'null'))
}
}

const kvRe = /^[ \t]+([^\s]+?):\s*?([^\r\n#]+)(#[^\r\n]+)?[\r\n]/

function asKeyValue (d) {
const match = d[0].value.match(kvRe)
return { [match[1]]: match[2].trim() }
return { [match[1]]: match[2].trim().replace(/^"|"$/g, '') }
}
%}
22 changes: 22 additions & 0 deletions test/bind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

const assert = require('assert')
const fs = require('fs').promises

const ns = require('../lib/bind.js')


describe('bind', function () {

describe('parseConfig', function () {

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.parseConfig(buf.toString())
// console.dir(r, { depth: null })
assert.equal(r.length, 5)
})
})

})
20 changes: 20 additions & 0 deletions test/fixtures/maradns/example.com
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

csv2 = {}
csv2["example.com."] = "db.example.com"
csv2["example2.com."] = "db.example2.com"

ipv4_bind_addresses = "10.1.2.3,10.1.2.4,127.0.0.1"

chroot_dir = "/etc/maradns"

maradns_uid = 99
maradns_gid = 99

no_fingerprint = 0

max_chain = 8
max_ar_chain = 1
max_total = 20

verbose_level = 3
zone_transfer_acl = "10.1.1.1/24, 10.100.100.100/255.255.255.224"
Loading

0 comments on commit 4496497

Please sign in to comment.