Skip to content

Commit

Permalink
Merge 834ec2c into acb9914
Browse files Browse the repository at this point in the history
  • Loading branch information
msimerson committed Mar 17, 2022
2 parents acb9914 + 834ec2c commit 0035253
Show file tree
Hide file tree
Showing 9 changed files with 416 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
with:
fetch-depth: 1

- uses: actions/setup-node@v1
- uses: actions/setup-node@v2
name: Use Node.js ${{ matrix.node-version }}
with:
node-version: ${{ matrix.node-version }}
Expand All @@ -34,3 +34,4 @@ jobs:

env:
CI: true
NODE_OPTIONS: --max-old-space-size=4096
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,5 @@ dist

# TernJS port file
.tern-port
grammar.js
package-lock.json
14 changes: 14 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.github
.DS_Store
.editorconfig
.gitignore
.gitmodules
.lgtm.yml
appveyor.yml
codecov.yml
.release
.travis.yml
.eslintrc.yaml
.eslintrc.json
package-lock.json
grammar.js
26 changes: 26 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

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

exports.parseZoneFile = str => {

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

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

// flatten the parser generated array
const flat = []
for (const e of parser.results[0][0]) {

// discard blank lines
if (Array.isArray(e[0][0]) && e[0][0][0] === null) continue

flat.push(e[0][0])
}
return flat
}
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{
"name": "dns-zone-validator",
"version": "0.0.2",
"version": "0.1.0",
"description": "DNS Zone Validator",
"main": "index.js",
"scripts": {
"cover": "NODE_ENV=cov npx nyc --reporter=lcovonly npm run test",
"lint": "npx eslint *.js test/*.js",
"lintfix": "npx eslint --fix *.js test/*.js",
"grammar": "npx -p nearley nearleyc src/bind-grammar.ne -o grammar.js",
"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"
},
"repository": {
Expand All @@ -30,5 +32,6 @@
"mocha": "^9.1.3"
},
"dependencies": {
"nearley": "^2.20.1"
}
}
187 changes: 187 additions & 0 deletions src/bind-grammar.ne
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@

@builtin "string.ne"

main -> (statement eol):+

statement -> blank | ttl | origin | soa | ns | mx | a | txt | aaaa | cname | dname

eol -> "\n" | "\r"

blank -> _

comment -> ";" [^\n\r]:*

ttl -> "$TTL" __ uint _ (comment):? _
{% (d) => ttlAsObject(d) %}

origin -> "$ORIGIN" __ hostname (comment):? _
{% (d) => originAsObject(d) %}

soa -> hostname ( __ uint ):? ( __ class ):? __ "SOA"
__ hostname
__ hostname
__ "("
__ uint (_ comment):?
__ uint (_ comment):?
__ uint (_ comment):?
__ uint (_ comment):?
__ uint (_ comment):?
_ ")" _ (comment):?
{% (d) => toResourceRecord(d) %}

ns -> hostname (__ uint):? (__ class):? __ "NS"
__ hostname _ (comment):? _
{% (d) => toResourceRecord(d) %}

mx -> hostname (__ uint):? (__ class):? __ "MX"
__ uint __ hostname _ (comment):?
{% (d) => toResourceRecord(d) %}

a -> hostname (__ uint):? (__ class):? __ "A"
__ ip4 _ (comment):? _
{% (d) => toResourceRecord(d) %}

txt -> hostname (__ uint):? (__ class):? __ "TXT"
__ (dqstring _):+ (comment):? _
{% (d) => toResourceRecord(d) %}

aaaa -> hostname (__ uint):? (__ class):? __ "AAAA"
__ ip6 _ (comment):? _
{% (d) => toResourceRecord(d) %}

cname -> hostname (__ uint):? (__ class):? __ "CNAME"
__ hostname _ (comment):? _
{% (d) => toResourceRecord(d) %}

dname -> hostname (__ uint):? (__ class):? __ "DNAME"
__ hostname _ (comment):? _
{% (d) => toResourceRecord(d) %}

uint -> [0-9]:+ {% (d) => parseInt(d[0].join("")) %}

hostname -> ALPHA_NUM_DASH_U:* {% (d) => d[0].join("") %}

ALPHA_NUM_DASH_U -> [-0-9A-Za-z\u0080-\uFFFF._@] {% id %}

class -> "IN" | "CH" | "HS" | "CHAOS" | "ANY"

times_3[X] -> $X $X $X
times_5[X] -> $X $X $X $X $X
times_7[X] -> $X $X $X $X $X $X $X

ip4 -> Snum times_3["." Snum] {% (d) => flat_string(d) %}

ip6 -> IPv6_full | IPv6_comp | IPv6v4_full | IPv6v4_comp

Snum -> DIGIT |
( [1-9] DIGIT ) |
( "1" DIGIT DIGIT ) |
( "2" [0-4] DIGIT ) |
( "2" "5" [0-5] )

DIGIT -> [0-9] {% id %}
HEXDIG -> [0-9A-Fa-f] {% id %}

IPv6_hex -> HEXDIG |
( HEXDIG HEXDIG ) |
( HEXDIG HEXDIG HEXDIG ) |
( HEXDIG HEXDIG HEXDIG HEXDIG )

IPv6_full -> IPv6_hex times_7[":" IPv6_hex]
{% (d) => flat_string(d) %}

IPv6_comp -> (IPv6_hex times_5[":" IPv6_hex]):? "::"
(IPv6_hex times_5[":" IPv6_hex]):?
{% (d) => flat_string(d) %}

IPv6v4_full -> IPv6_hex times_5[":" IPv6_hex] ":" ip4
{% (d) => flat_string(d) %}

IPv6v4_comp -> (IPv6_hex times_3[":" IPv6_hex]):? "::"
(IPv6_hex times_3[":" IPv6_hex] ":"):?
ip4
{% (d) => flat_string(d) %}

# Whitespace: `_` is optional, `__` is mandatory.
_ -> wschar:* {% function(d) {return null;} %}
__ -> wschar:+ {% function(d) {return null;} %}

wschar -> [ \t\n\r\v\f] {% id %}

#ALPHA_NUM -> [0-9A-Za-z]
#ALPHA_NUM_U -> [0-9A-Za-z\u0080-\uFFFF] {% id %}


# https://datatracker.ietf.org/doc/html/rfc1035#page-12
#domain -> subdomain | " "
#subdomain -> label | subdomain "." label
#label -> letter ldh-str let-dig
#ldh-str -> let-dig-hyp | let-dig-hyp ldh-str
#let-dig-hyp -> let-dig | "-"
#let-dig -> letter | digit
#letter -> [a-zA-Z]
#digit -> [0-9]


@{%
function flat_string(d) {
if (d) {
if (Array.isArray(d)) return d.flat(Infinity).join("")
return d
}
return ''
}

function ttlAsObject (d) {
return { ttl: d[2] }
}

function originAsObject (d) {
return { origin: d[2] }
}

function toResourceRecord (d) {
const r = {
name: d[0],
ttl : d[1] ? d[1][1] : d[1],
class: d[2] ? d[2][1][0] : d[2],
type: d[4],
}

switch (r.type) {
case 'A':
r.address = d[6]
break
case 'AAAA':
r.address = d[6][0]
break
case 'CNAME':
r.cname = d[6][0]
break
case 'DNAME':
r.target = d[6][0]
break
case 'MX':
r.preference = d[6]
r.exchange = d[8]
break
case 'NS':
r.dname = d[6]
break
case 'SOA':
r.mname = d[6]
r.rname = d[8]
r.serial = d[12]
r.refresh = d[15]
r.retry = d[18]
r.expire = d[21]
r.minimum = d[24]
break
case 'TXT':
r.data = d[6].map(e => e[0])
break
}
return r
}

%}
56 changes: 56 additions & 0 deletions test/fixtures/zones/cadillac.net
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

$TTL 86400;
$ORIGIN cadillac.net.
cadillac.net. 86400 IN SOA ns1.cadillac.net. hostmaster.cadillac.net. (
2021102100 ; serial
16384 ; refresh
2048 ; retry
604800 ; expiry
2560 ; minimum
)

cadillac.net. 14400 IN NS ns1.cadillac.net.
cadillac.net. 14400 IN NS ns2.cadillac.net.
cadillac.net. 14400 IN NS ns3.cadillac.net.

cadillac.net. 86400 IN MX 0 mail.theartfarm.com.
cadillac.net. 86400 IN A 66.128.51.173
cadillac.net. 86400 IN TXT "v=spf1 mx a include:mx.theartfarm.com -all"

ns1 86400 IN MX 10 ns1.cadillac.net.
ns1 86400 IN A 138.210.133.61

ns2 86400 IN A 192.48.85.146
ns2 86400 IN AAAA 2605:7900:0020:000a:0000:0000:0000:0004
ns2 86400 IN A 204.11.99.4
ns2 86400 IN A 173.45.131.4
ns2 86400 IN MX 10 ns2.cadillac.net.

ns3 86400 IN MX 10 ns3.cadillac.net.
ns3 86400 IN AAAA 2605:ae00:0329:0000:0000:0000:0000:000f
ns3 86400 IN A 66.128.51.174
ns3 86400 IN AAAA 2001:41d0:0302:2100:0000:0000:0000:00a7
ns3 86400 IN A 217.182.64.150

ns4 3600 IN A 208.78.70.12
ns4 3600 IN AAAA 2001:0500:0090:0001:0000:0000:0000:0012
ns4 3600 IN A 204.13.250.12
ns4 3600 IN A 208.78.71.12
ns4 3600 IN A 204.13.251.12
ns4 3600 IN AAAA 2001:0500:0094:0001:0000:0000:0000:0012

localhost 86400 IN A 127.0.0.1
localhost 86400 IN AAAA 0000:0000:0000:0000:0000:0000:0000:0001

www 28800 IN CNAME vhost0.theartfarm.com.
matt 86400 IN CNAME matt.simerson.net.
dns 3600 IN A 66.128.51.171
jaycees 86400 IN CNAME www.cadillacjaycees.org.
kyna 86400 IN CNAME vhost0.theartfarm.com.
_tcp 86400 IN DNAME _tcp.theartfarm.com.
_dmarc 86400 IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc-feedback@theartfarm.com; ruf=mailto:dmarc-feedback@theartfarm.com; pct=100"
apr2013._domainkey 86400 IN TXT "v=DKIM1;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5BgmgaBHxIQSHCzFlkJ8/dCYFgppfpGxQB2mbB/nlspbZZPQPX7JrS8fglt6lVOQ/A82ErayWiABQd6GziiHbe+mA5glQSxG2o2LUtDa1AU269W1sZrgVEFkIq5sZ+T+s3KbcSjca21YOZt8NWxw5UvP1xTRHHO77JbcwUEB4rBAiZOs8eU9kuMLAuh8AQw0w17JW0+tN" "SNSphz0dY5S/5upHSdRqyOVrCJNE/Zuyzo1Ck+T1NIPt4ttd1VPkAMnjqXXjBQWP4BRObVEdmRqCxy4CRfbbiPJiNcut+iV2YezJqsVxBXwPFfsMwVb68aAHKKpdwrBfmNfv/yLdXY6RwIDAQAB"
_domainkey 86400 IN TXT "o=-; t=y; r=postmaster@cadillac.net"
france.ns3 86400 IN A 217.182.64.150
dallas.ns3 86400 IN A 66.128.51.174
xn--ber-goa 86400 IN A 127.0.0.1
20 changes: 20 additions & 0 deletions test/fixtures/zones/isi.edu
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@ IN SOA VENERA Action.domains (
20 ; SERIAL
7200 ; REFRESH
600 ; RETRY
3600000; EXPIRE
60) ; MINIMUM

NS A.ISI.EDU.
NS VENERA
NS VAXA
MX 10 VENERA
MX 20 VAXA

A A 26.3.0.103

VENERA A 10.1.0.52
A 128.9.0.32

VAXA A 10.2.0.27
A 128.9.0.33
Loading

0 comments on commit 0035253

Please sign in to comment.