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

release 0.2.0 #2

Merged
merged 3 commits into from
Mar 23, 2022
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-test.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

name: Module Tests
name: Tests

on: [ push ]

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coveralls.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

on: [ pull_request ]

name: Test Coverage
name: Coverage

jobs:

Expand Down
23 changes: 23 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

#### 1.N.N - YYYY-MM-DD


#### 0.2.0 - 2022-03-22

- add expandShortcuts
- added bin/import
- use async for parseZoneFile and expandShortcuts
- SOA: capture comments


#### 0.1.0 - 2022-03-17

- Bind zonefile parser, using nearley: #1

- stab #1: parses cadillac.net
- test #2: add isi.edu zone
- allow comments after SOA closing parens
- DRY the grammar -> object functions
- add \r to eol, for windows
- local copy of builtin-whitespace, adds \r char
- ci: remove windows support, times out, I think upstream nearley issue
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,24 @@ DNS zones have numerous rules regarding the records that can exist in them. Exam

- serial numbers must increment when changes are made
- multiple identical RRs are not allowed - RFC 2181
- CAA takes tag into account, SRV: port
- multiple CNAMES with the same name are not allowed
- CNAME cannot coexist with SIG,NXT,KEY,RRSIG,NSEC
- A cannot coexist with CNAME
- CNAME cannot coexist with SIG,NXT,KEY,RRSIG,NSEC,A,AAAA
- MX and NS records cannot point to CNAME

Etc, etc, etc..

This module will input a collection of [dns-resource-records](https://github.com/msimerson/dns-resource-record) and validate that all the zone records can coexist.
This module will input a collection of [dns-resource-records](https://github.com/msimerson/dns-resource-record) and validate that all the zone records can coexist.


## TODO

- [ ] write a named.conf file parser
- [x] write a bind zone file parser
- normalize the zone records
- [x] expand `@` to zone name
- [x] empty names are same as previous RR record
- [x] missing TTLs inherit zone TTL, or zone MINIMUM
- [x] expand hostnames to FQDNs
- [x] ALL: name field
- [x] MX: exchange, CNAME: cname, SOA: mname, rname, NS: dname
69 changes: 69 additions & 0 deletions bin/import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!node

const fs = require('fs')
const path = require('path')
const os = require('os')

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

const filePath = process.argv[2]
if (!filePath) usage()

function usage () {
console.log(`\n ${process.argv[1]} file\n`)
process.exit(1)
}

console.log(`reading file ${filePath}`)

fs.readFile(filePath, (err, buf) => {
if (err) throw err

const base = path.basename(filePath)
const asString = fileAsString(buf, base)

dz.parseZoneFile(asString)
.then(dz.expandShortcuts)
.then(zoneArray => {
// console.log(zoneArray)
switch (process.argv[3]) {
case 'toBind':
toBind(zoneArray, base)
break
case 'toTinydns':
toTinydns(zoneArray)
break
default:
console.log(zoneArray)
}
})
.catch(e => {
console.error(e.message)
})
})

function fileAsString (buf, base) {
let str = buf.toString()

if (!/^\$ORIGIN/m.test(str)) {
console.log(`inserting $ORIGIN ${base}`)
str = `$ORIGIN ${base}.${os.EOL}${str}`
}
// console.log(str)
return str
}

function toBind (zoneArray, origin) {
for (const rr of zoneArray) {
let out = rr.toBind()
const reduceRE = new RegExp(`^([^\\s]+).${origin}.`)
out = out.replace(reduceRE, '$1')
process.stdout.write(out)
}
}

function toTinydns (zoneArray) {
for (const rr of zoneArray) {
process.stdout.write(rr.toTinydns())
}
}
95 changes: 91 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@

const nearley = require('nearley')
const grammar = require('./grammar.js')
const RR = require('dns-resource-record')

const grammar = nearley.Grammar.fromCompiled(require('./grammar.js'))
grammar.start = 'main'

exports.parseZoneFile = str => {
exports.parseZoneFile = async str => {

const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar))
const parser = new nearley.Parser(grammar)
parser.feed(str)
parser.feed(`\n`) // for no EOL after last record
parser.feed(`\n`) // in case no EOL after last record

if (parser.length > 1) {
console.error(`ERROR: ambigious parser rule`)
Expand All @@ -24,3 +26,88 @@ exports.parseZoneFile = str => {
}
return flat
}

exports.expandShortcuts = async zoneArray => {
let ttl = 0
let implicitOrigin = ''
let origin = ''
let lastName = ''
const expanded = []
const empty = [ undefined, null ]
// console.log(zoneArray)

for (let i = 0; i < zoneArray.length; i++) {
const entry = zoneArray[i]

if (entry.$TTL) {
ttl = entry.$TTL; continue
}

// When a zone is first read, there is an implicit $ORIGIN <zone_name>.
// note the trailing dot. The current $ORIGIN is appended to the domain
// specified in the $ORIGIN argument if it is not absolute. -- BIND 9
if (entry.implicitOrigin) { // zone 'name' in named.conf
implicitOrigin = origin = fullyQualify(entry.implicitOrigin)
continue
}
if (entry.$ORIGIN) { // declared $ORIGIN within zone file
origin = fullyQualify(entry.$ORIGIN, implicitOrigin)
continue
}
if (!origin) throw new Error(`zone origin ambiguous, cowardly bailing out`)

if (ttl === 0 && entry.type === 'SOA' && entry.minimum) ttl = entry.minimum
if (empty.includes(entry.ttl )) entry.ttl = ttl
if (empty.includes(entry.class)) entry.class = 'IN'

// expand NAME shortcuts
if (entry.name === '@') entry.name = origin

// "If a line begins with a blank, then the owner is assumed to be the
// same as that of the previous RR" -- BIND 9 manual
if (entry.name === '' && lastName) entry.name = lastName

if (entry.name) {
entry.name = fullyQualify(entry.name, origin)
}
else {
entry.name = `${origin}`.toLowerCase()
}

if (entry.name !== lastName) lastName = entry.name

expandRdata(entry, origin, ttl)

try {
expanded.push(new RR[entry.type](entry))
}
catch (e) {
console.error(`I encounted this error: \n`)
console.error(e.message)
console.error(`\nwhile processing this RR: \n`)
console.log(entry)
}
}
return expanded
}

function fullyQualify (hostname, origin) {
if (hostname.endsWith('.')) return hostname
return `${hostname}.${origin}`.toLowerCase()
}

function expandRdata (entry, origin, ttl) {
switch (entry.type) {
case 'SOA':
for (const f of [ 'mname', 'rname' ]) {
entry[f] = fullyQualify(entry[f], origin)
}
break
case 'MX':
entry.exchange = fullyQualify(entry.exchange, origin)
break
case 'NS':
entry.dname = fullyQualify(entry.dname, origin)
break
}
}
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dns-zone-validator",
"version": "0.1.0",
"version": "0.2.0",
"description": "DNS Zone Validator",
"main": "index.js",
"scripts": {
Expand All @@ -9,7 +9,8 @@
"lint": "npx eslint index.js test/*.js",
"lintfix": "npx eslint --fix index.js test/*.js",
"postinstall": "npx -p nearley nearleyc src/bind-grammar.ne -o grammar.js",
"test": "npx mocha"
"test": "npx mocha",
"versions": "npx dependency-version-checker check"
},
"repository": {
"type": "git",
Expand All @@ -28,10 +29,11 @@
},
"homepage": "https://github.com/msimerson/dns-zone-validator#readme",
"devDependencies": {
"eslint": "^8.0.1",
"mocha": "^9.1.3"
"eslint": "^8.11.0",
"mocha": "^9.2.2"
},
"dependencies": {
"dns-resource-record": "^0.9.3",
"nearley": "^2.20.1"
}
}
Loading