Skip to content

Commit

Permalink
more
Browse files Browse the repository at this point in the history
  • Loading branch information
msimerson committed Mar 24, 2022
1 parent 39790b9 commit ef66357
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 21 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
- zone_opts, for influencing output of toBind
- normalize hostnames to lower case
- add tests: fullyQualify, getFQDN
- AAAA.compress: rewrote per RFC 5952, added tests
- AAAA: internally store address in expanded notation
- fromTinydns: apply correct semantics for 'x' handling


#### 0.9.3 - 2022-03-22
Expand Down
2 changes: 1 addition & 1 deletion lib/tinydns.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

const SPECIALCHARS = {
'+': [ 'A' ],
'-': [ undefined ],
'-': [ undefined ], // disabled RR
'%': [ 'location' ],
'.': [ 'SOA', 'NS', 'A' ],
'&': [ 'NS', 'A' ],
Expand Down
51 changes: 38 additions & 13 deletions rr/aaaa.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class AAAA extends RR {
/****** Resource record specific setters *******/
setAddress (val) {
if (!val) throw new Error('AAAA: address is required')
if (!net.isIPv6(val)) throw new Error('AAAA: address must be IPv6')
if (!net.isIPv6(val)) throw new Error(`AAAA: address must be IPv6 (${val})`)

this.set('address', val.toLowerCase()) // IETFs suggest only lower case
this.set('address', this.expand(val.toLowerCase())) // lower case: RFC 5952
}

getCompressed (val) {
Expand Down Expand Up @@ -45,14 +45,13 @@ class AAAA extends RR {
case ':':
// GENERIC => :fqdn:28:rdata:ttl:timestamp:lo
[ fqdn, n, rdata, ttl, ts, loc ] = str.substring(1).split(':')
if (n != 28) throw new Error('SRV fromTinydns, invalid n')
// compressed format is needed for test comparisons
ip = this.compress(TINYDNS.octalToHex(rdata).match(/([0-9a-fA-F]{4})/g).join(':'))
if (n != 28) throw new Error('AAAA fromTinydns, invalid n')
ip = TINYDNS.octalToHex(rdata).match(/([0-9a-fA-F]{4})/g).join(':')
break
case '3':
case '6':
// AAAA => 3 fqdn:rdata:x:ttl:timestamp:lo
// AAAA,PTR => 6 fqdn:rdata:x:ttl:timestamp:lo
// AAAA => 3fqdn:ip:x:ttl:timestamp:lo
// AAAA,PTR => 6fqdn:ip:x:ttl:timestamp:lo
[ fqdn, rdata, ttl, ts, loc ] = str.substring(1).split(':')
ip = rdata.match(/(.{4})/g).join(':')
break
Expand All @@ -75,17 +74,40 @@ class AAAA extends RR {
class : c,
type : type,
name : fqdn,
address: ip,
address: this.expand(ip),
ttl : parseInt(ttl, 10),
})
}

compress (val) {
return val
.replace(/\b(?:0+:){2,}/, ':') // compress all zero
.split(':')
.map(o => o.replace(/\b0+/g, '')) // strip leading zero
.join(':')
/*
* RFC 5952
* 4.1. Leading zeros MUST be suppressed...A single 16-bit 0000 field MUST be represented as 0.
* 4.2.1 The use of the symbol "::" MUST be used to its maximum capability.
* 4.2.2 The symbol "::" MUST NOT be used to shorten just one 16-bit 0 field.
* 4.2.3 When choosing placement of a "::", the longest run...MUST be shortened
* 4.3 The characters a-f in an IPv6 address MUST be represented in lowercase.
*/
let r = val
.replace(/0000/g, '0') // 4.1 0000 -> 0
.replace(/:0+([1-9a-f])/g, ':$1') // 4.1 remove leading zeros

const mostConsecutiveZeros = [
new RegExp(/0?(?::0){6,}:0?/),
new RegExp(/0?(?::0){5,}:0?/),
new RegExp(/0?(?::0){4,}:0?/),
new RegExp(/0?(?::0){3,}:0?/),
new RegExp(/0?(?::0){2,}:0?/),
]

for (const re of mostConsecutiveZeros) {
if (re.test(r)) {
r = r.replace(re, '::')
break
}
}

return r
}

expand (val, delimiter) {
Expand All @@ -102,6 +124,9 @@ class AAAA extends RR {
}

/****** EXPORTERS *******/
toBind (zone_opts) {
return `${this.getPrefix(zone_opts)}\t${this.compress(this.get('address'))}\n`
}

toTinydns () {
// from AAAA notation (8 groups of 4 hex digits) to 16 escaped octals
Expand Down
10 changes: 6 additions & 4 deletions rr/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,11 @@ class RR extends Map {
this.set('type', t)
}

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

getPrefix (zone_opts = {}) {
Expand Down Expand Up @@ -265,6 +266,7 @@ class RR extends Map {

module.exports = {
RR,
TINYDNS: require('../lib/tinydns'),
}

const files = fs.readdirSync(path.join(__dirname))
Expand Down
2 changes: 1 addition & 1 deletion rr/mx.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class MX extends RR {
return new this.constructor({
type : 'MX',
name : this.fullyQualify(fqdn),
exchange : this.fullyQualify(x),
exchange : this.fullyQualify(/\./.test(x) ? x : `${x}.mx.${fqdn}`),
preference: parseInt(preference, 10) || 0,
ttl : parseInt(ttl, 10),
timestamp : ts,
Expand Down
2 changes: 1 addition & 1 deletion rr/ns.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class NS extends RR {
return new this.constructor({
type : 'NS',
name : this.fullyQualify(fqdn),
dname : this.fullyQualify(dname),
dname : this.fullyQualify(/\./.test(dname) ? dname : `${dname}.ns.${fqdn}`),
ttl : parseInt(ttl, 10),
timestamp: ts,
location : loc !== '' && loc !== '\n' ? loc : '',
Expand Down
28 changes: 27 additions & 1 deletion test/aaaa.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const validRecords = [
class : 'IN',
name : 'test.example.com.',
type : 'AAAA',
address: '2001:db8:20:a::4',
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',
Expand Down Expand Up @@ -50,4 +50,30 @@ describe('AAAA record', function () {
}
})
}

const tests = [
{ e: '2001:0db8:0020:000a:0000:0000:0000:0004', c: '2001:db8:20:a::4' },
{ e: '0000:0000:0000:0000:0000:0000:0000:0000', c: '::' },
{ e: '0000:0000:0000:0000:0000:0000:0000:0001', c: '::1' },
{ e: '2001:0db8:0000:0000:0000:0000:0002:0001', c: '2001:db8::2:1' },
{ e: '2001:0db8:0000:0001:0001:0001:0001:0001', c: '2001:db8:0:1:1:1:1:1' },
]

describe('compress', function () {
const r = new AAAA(null)
for (const t of tests) {
it(`compresses IPv6 address (${t.e})`, function () {
assert.equal(r.compress(t.e), t.c)
})
}
})

describe('expand', function () {
const r = new AAAA(null)
for (const t of tests) {
it(`expands IPv6 address (${t.c})`, function () {
assert.equal(r.expand(t.c), t.e)
})
}
})
})
6 changes: 6 additions & 0 deletions test/tinydns.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ describe('TINYDNS', function () {
}
})

describe('octalToHex', function () {
it('unescapes octal to hex digits', function () {
assert.strictEqual(TINYDNS.octalToHex('\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\001'), '00000000000000000000000000000001')
})
})

describe('UInt16toOctal', function () {
it('converts a 16-bit number to escaped octal', function () {
assert.strictEqual(TINYDNS.UInt16toOctal(65535), '\\377\\377')
Expand Down

0 comments on commit ef66357

Please sign in to comment.