@nictool/dns-resource-record 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.codeclimate.yml +25 -0
  2. package/CHANGELOG.md +253 -0
  3. package/DEVELOP.md +23 -0
  4. package/LICENSE +29 -0
  5. package/README.md +267 -0
  6. package/index.js +67 -0
  7. package/lib/tinydns.js +186 -0
  8. package/package.json +40 -0
  9. package/rr/a.js +65 -0
  10. package/rr/aaaa.js +137 -0
  11. package/rr/caa.js +129 -0
  12. package/rr/cert.js +75 -0
  13. package/rr/cname.js +76 -0
  14. package/rr/dname.js +74 -0
  15. package/rr/dnskey.js +117 -0
  16. package/rr/ds.js +104 -0
  17. package/rr/hinfo.js +84 -0
  18. package/rr/ipseckey.js +159 -0
  19. package/rr/key.js +73 -0
  20. package/rr/loc.js +216 -0
  21. package/rr/mx.js +86 -0
  22. package/rr/naptr.js +146 -0
  23. package/rr/ns.js +74 -0
  24. package/rr/nsec.js +61 -0
  25. package/rr/nsec3.js +99 -0
  26. package/rr/nsec3param.js +84 -0
  27. package/rr/openpgpkey.js +44 -0
  28. package/rr/ptr.js +65 -0
  29. package/rr/rrsig.js +101 -0
  30. package/rr/sig.js +100 -0
  31. package/rr/smimea.js +73 -0
  32. package/rr/soa.js +140 -0
  33. package/rr/spf.js +54 -0
  34. package/rr/srv.js +122 -0
  35. package/rr/sshfp.js +86 -0
  36. package/rr/tlsa.js +106 -0
  37. package/rr/txt.js +122 -0
  38. package/rr/uri.js +95 -0
  39. package/rr.js +291 -0
  40. package/test/a.js +76 -0
  41. package/test/aaaa.js +79 -0
  42. package/test/base.js +138 -0
  43. package/test/caa.js +104 -0
  44. package/test/cert.js +48 -0
  45. package/test/cname.js +41 -0
  46. package/test/dname.js +53 -0
  47. package/test/dnskey.js +44 -0
  48. package/test/ds.js +44 -0
  49. package/test/fake.js +26 -0
  50. package/test/hinfo.js +75 -0
  51. package/test/ipseckey.js +115 -0
  52. package/test/key.js +37 -0
  53. package/test/loc.js +71 -0
  54. package/test/mx.js +75 -0
  55. package/test/naptr.js +40 -0
  56. package/test/ns.js +53 -0
  57. package/test/nsec.js +38 -0
  58. package/test/nsec3.js +26 -0
  59. package/test/nsec3param.js +26 -0
  60. package/test/openpgpkey.js +35 -0
  61. package/test/ptr.js +54 -0
  62. package/test/rr.js +196 -0
  63. package/test/smimea.js +45 -0
  64. package/test/soa.js +77 -0
  65. package/test/spf.js +48 -0
  66. package/test/srv.js +81 -0
  67. package/test/sshfp.js +62 -0
  68. package/test/tinydns.js +140 -0
  69. package/test/tlsa.js +54 -0
  70. package/test/txt.js +70 -0
  71. package/test/uri.js +61 -0
package/lib/tinydns.js ADDED
@@ -0,0 +1,186 @@
1
+
2
+ export const SPECIALCHARS = {
3
+ '+': [ 'A' ],
4
+ '-': [ undefined ], // disabled RR
5
+ '%': [ 'location' ],
6
+ '.': [ 'SOA', 'NS', 'A' ],
7
+ '&': [ 'NS', 'A' ],
8
+ '=': [ 'A', 'PTR' ],
9
+ '@': [ 'MX', 'A' ],
10
+ '#': [ 'comment' ],
11
+ "'": [ 'TXT' ],
12
+ '^': [ 'PTR' ],
13
+ 'C': [ 'CNAME' ],
14
+ 'Z': [ 'SOA' ],
15
+ ':': [ 'generic' ],
16
+ '3': [ 'AAAA' ],
17
+ '6': [ 'AAAA', 'PTR' ],
18
+ 'S': [ 'SRV' ],
19
+ }
20
+
21
+ const octalRe = new RegExp(/\\(?:[1-7][0-7]{0,2}|[0-7]{2,3})/, 'g')
22
+
23
+ export function escapeOctal (re, str) {
24
+ let escaped = ''
25
+ str.split(/(.{1})/g).map(c => {
26
+ escaped += re.test(c) ? charToOctal(c) : c
27
+ })
28
+ return escaped
29
+ }
30
+
31
+ export function octalToChar (str) {
32
+ // relace instances of \NNN with ASCII
33
+ return str.replace(octalRe, o => String.fromCharCode(parseInt(o.substring(1), 8)))
34
+ }
35
+
36
+ export function octalToHex (str) {
37
+ // relace instances of \NNN with Hex
38
+ return str.replace(octalRe, o => {
39
+ // parseInt(n, 8) -> from octal to decimal
40
+ // .toString(16) -> decimal to hex
41
+ return parseInt(o.substring(1), 8).toString(16).padStart(2, 0)
42
+ })
43
+ }
44
+
45
+ export function octalToUInt8 (str) {
46
+ const b = Buffer.alloc(1)
47
+ b.writeUInt8(parseInt(str.substring(1,4), 8), 0)
48
+ return b.readUInt8()
49
+ }
50
+
51
+ export function octalToUInt16 (str) {
52
+ const b = Buffer.alloc(2)
53
+ b.writeUInt8(parseInt(str.substring(1,4), 8), 0)
54
+ b.writeUInt8(parseInt(str.substring(5,8), 8), 1)
55
+ return b.readUInt16BE()
56
+ }
57
+
58
+ export function octalToUInt32 (str) {
59
+ const b = Buffer.alloc(4)
60
+ b.writeUInt8(parseInt(str.substring(1,4), 8), 0)
61
+ b.writeUInt8(parseInt(str.substring(5,8), 8), 1)
62
+ b.writeUInt8(parseInt(str.substring(9,12), 8), 2)
63
+ b.writeUInt8(parseInt(str.substring(13,16), 8), 3)
64
+ return b.readUInt32BE()
65
+ }
66
+
67
+ export function packString (str) {
68
+ return str.match(/(.{1,255})/g).map(s => {
69
+ const len = Buffer.alloc(1)
70
+ len.writeUInt8(s.length)
71
+ return `${UInt8toOctal(len.readUInt8(0))}${s}`
72
+ }).join('')
73
+ }
74
+
75
+ export function unpackString (str) {
76
+ const asBuf = Buffer.from(octalToChar(str.toString()))
77
+ const res = []
78
+ let pos = 0
79
+ let len
80
+ while ((len = asBuf.readUInt8(pos))) { // encoded length byte
81
+ pos++
82
+ res.push(asBuf.slice(pos, pos + len).toString())
83
+ pos = +(pos + len)
84
+ if (pos >= asBuf.length) break
85
+ }
86
+ return res
87
+ }
88
+
89
+ export function packDomainName (fqdn) {
90
+ const labelRegEx = new RegExp(/[^A-Za-z0-9-.]/, 'g')
91
+
92
+ // RFC 1035, 3.3 Standard RRs
93
+ // The standard wire format for DNS names. (1 octet length + octets)
94
+ let packed = ''
95
+ fqdn.split('.').map(label => {
96
+ if (label === undefined || !label.length) return
97
+
98
+ const len = Buffer.alloc(1)
99
+ len.writeUInt8(label.length)
100
+ packed += UInt8toOctal(len.readUInt8(0))
101
+
102
+ packed += escapeOctal(labelRegEx, label)
103
+ })
104
+ packed += '\\000' // terminates with a zero length label
105
+ return packed
106
+ }
107
+
108
+ export function unpackDomainName (fqdn) {
109
+
110
+ fqdn = Buffer.from(octalToChar(fqdn.toString()))
111
+
112
+ const labels = []
113
+ let pos = 0
114
+ let len
115
+ while ((len = fqdn.readUInt8(pos))) { // encoded length byte
116
+ pos++
117
+ labels.push(fqdn.slice(pos, pos + len).toString())
118
+ pos = +(pos + len)
119
+ }
120
+ const r = `${labels.join('.')}.`
121
+ // char position + length of last label + label length chars + null byte
122
+ const strLen = pos + len + labels.length * 4 + 1
123
+ return [ r, strLen ]
124
+ }
125
+
126
+ export function packHex (str) {
127
+ let r = ''
128
+ for (let i = 0; i < str.length; i = i+2) {
129
+ // nibble off 2 hex bytes, encode to octal
130
+ r += UInt8toOctal(parseInt(str.slice(i, i+2), 16))
131
+ }
132
+ return r
133
+ }
134
+
135
+ export function charToOctal (c) {
136
+ if (typeof c === 'number') return UInt8toOctal(c)
137
+
138
+ return UInt8toOctal(c.charCodeAt(0))
139
+ }
140
+
141
+ export function UInt8toOctal (n) {
142
+ if (n > 255) throw new Error('UInt8toOctal does not work on numbers > 255')
143
+
144
+ return `\\${parseInt(n, 10).toString(8).padStart(3, 0)}`
145
+ }
146
+
147
+ export function UInt16toOctal (n) {
148
+ let r = ''
149
+ const pri = Buffer.alloc(2)
150
+ pri.writeUInt16BE(n)
151
+ r += UInt8toOctal(pri.readUInt8(0))
152
+ r += UInt8toOctal(pri.readUInt8(1))
153
+ return r
154
+ }
155
+
156
+ export function UInt32toOctal (n) {
157
+ let r = ''
158
+ const pri = Buffer.alloc(4)
159
+ pri.writeUInt32BE(n)
160
+ for (let i = 0; i < 4; i++) {
161
+ r += UInt8toOctal(pri.readUInt8(i))
162
+ }
163
+ return r
164
+ }
165
+
166
+ export function ipv4toOctal (ip) {
167
+ return UInt32toOctal(ip.split`.`.reduce((int, value) => int * 256 + +value))
168
+ }
169
+
170
+ export function octalToIPv4 (str) {
171
+ const asInt = octalToUInt32(str)
172
+ return [ 24,16,8,0 ].map(n => (asInt >> n) & 0xff).join('.')
173
+ }
174
+
175
+ export function base64toOctal (str) {
176
+ const bytes = Buffer.from(str, 'base64')
177
+ let escaped = ''
178
+ for (const b of bytes) {
179
+ escaped += /[A-Za-z0-9\-.]/.test(String.fromCharCode(b)) ? String.fromCharCode(b) : UInt8toOctal(b)
180
+ }
181
+ return escaped
182
+ }
183
+
184
+ export function octalToBase64 (str) {
185
+ return Buffer.from(octalToChar(str), 'binary').toString('base64')
186
+ }
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@nictool/dns-resource-record",
3
+ "version": "1.1.3",
4
+ "description": "DNS Resource Records",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "lint": "npx eslint *.js lib rr test",
9
+ "lintfix": "npx eslint --fix *.js lib rr test",
10
+ "test": "npx mocha",
11
+ "versions": "npx dependency-version-checker check"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+ssh://git@github.com/NicTool/dns-resource-record.git"
16
+ },
17
+ "keywords": [
18
+ "DNS",
19
+ "resource",
20
+ "record",
21
+ "validator",
22
+ "validation",
23
+ "import",
24
+ "export",
25
+ "BIND",
26
+ "djbdns",
27
+ "tinydns",
28
+ "nictool"
29
+ ],
30
+ "author": "Matt Simerson <matt@tnpi.net>",
31
+ "license": "BSD-3-Clause",
32
+ "bugs": {
33
+ "url": "https://github.com/NicTool/dns-resource-record/issues"
34
+ },
35
+ "homepage": "https://github.com/NicTool/dns-resource-record#readme",
36
+ "devDependencies": {
37
+ "eslint": "^8.11.0",
38
+ "mocha": "^9.2.2"
39
+ }
40
+ }
package/rr/a.js ADDED
@@ -0,0 +1,65 @@
1
+
2
+ import net from 'net'
3
+
4
+ import RR from '../rr.js'
5
+
6
+ export default class A extends RR {
7
+ constructor (opts) {
8
+ super(opts)
9
+ }
10
+
11
+ /****** Resource record specific setters *******/
12
+ setAddress (val) {
13
+ if (!val) throw new Error('A: address is required')
14
+ if (!net.isIPv4(val)) throw new Error('A address must be IPv4')
15
+ this.set('address', val)
16
+ }
17
+
18
+ getDescription () {
19
+ return 'Address'
20
+ }
21
+
22
+ getRdataFields (arg) {
23
+ return [ 'address' ]
24
+ }
25
+
26
+ getRFCs () {
27
+ return [ 1035 ]
28
+ }
29
+
30
+ getTypeId () {
31
+ return 1
32
+ }
33
+
34
+ /****** IMPORTERS *******/
35
+ fromTinydns (opts) {
36
+ // +fqdn:ip:ttl:timestamp:lo
37
+ const [ owner, ip, ttl, ts, loc ] = opts.tinyline.substring(1).split(':')
38
+
39
+ return new A({
40
+ owner : this.fullyQualify(owner),
41
+ type : 'A',
42
+ address : ip,
43
+ ttl : parseInt(ttl, 10),
44
+ timestamp: ts,
45
+ location : loc !== '' && loc !== '\n' ? loc : '',
46
+ })
47
+ }
48
+
49
+ fromBind (opts) {
50
+ // test.example.com 3600 IN A 192.0.2.127
51
+ const [ owner, ttl, c, type, address ] = opts.bindline.split(/\s+/)
52
+ return new A({
53
+ owner,
54
+ ttl : parseInt(ttl, 10),
55
+ class: c,
56
+ type,
57
+ address,
58
+ })
59
+ }
60
+
61
+ /****** EXPORTERS *******/
62
+ toTinydns () {
63
+ return `+${this.getTinyFQDN('owner')}:${this.get('address')}:${this.getTinydnsPostamble()}\n`
64
+ }
65
+ }
package/rr/aaaa.js ADDED
@@ -0,0 +1,137 @@
1
+
2
+ import net from 'net'
3
+
4
+ import RR from '../rr.js'
5
+ import * as TINYDNS from '../lib/tinydns.js'
6
+
7
+ export default class AAAA extends RR {
8
+ constructor (opts) {
9
+ super(opts)
10
+ }
11
+
12
+ /****** Resource record specific setters *******/
13
+ setAddress (val) {
14
+ if (!val) throw new Error('AAAA: address is required')
15
+ if (!net.isIPv6(val)) throw new Error(`AAAA: address must be IPv6 (${val})`)
16
+
17
+ this.set('address', this.expand(val.toLowerCase())) // lower case: RFC 5952
18
+ }
19
+
20
+ getCompressed (val) {
21
+ this.compress(val || this.get('address'))
22
+ }
23
+
24
+ getDescription () {
25
+ return 'Address IPv6'
26
+ }
27
+
28
+ getRdataFields (arg) {
29
+ return [ 'address' ]
30
+ }
31
+
32
+ getRFCs () {
33
+ return [ 3596 ]
34
+ }
35
+
36
+ getTypeId () {
37
+ return 28
38
+ }
39
+
40
+ /****** IMPORTERS *******/
41
+ fromTinydns (opts) {
42
+ const str = opts.tinyline
43
+ let fqdn, ip, n, rdata, ttl, ts, loc
44
+
45
+ switch (str[0]) {
46
+ case ':':
47
+ // GENERIC => :fqdn:28:rdata:ttl:timestamp:lo
48
+ [ fqdn, n, rdata, ttl, ts, loc ] = str.substring(1).split(':')
49
+ if (n != 28) throw new Error('AAAA fromTinydns, invalid n')
50
+ ip = TINYDNS.octalToHex(rdata).match(/([0-9a-fA-F]{4})/g).join(':')
51
+ break
52
+ case '3':
53
+ case '6':
54
+ // AAAA => 3fqdn:ip:x:ttl:timestamp:lo
55
+ // AAAA,PTR => 6fqdn:ip:x:ttl:timestamp:lo
56
+ [ fqdn, rdata, ttl, ts, loc ] = str.substring(1).split(':')
57
+ ip = rdata.match(/(.{4})/g).join(':')
58
+ break
59
+ }
60
+
61
+ return new AAAA({
62
+ owner : this.fullyQualify(fqdn),
63
+ ttl : parseInt(ttl, 10),
64
+ type : 'AAAA',
65
+ address : ip,
66
+ timestamp: ts,
67
+ location : loc !== '' && loc !== '\n' ? loc : '',
68
+ })
69
+ }
70
+
71
+ fromBind (opts) {
72
+ // test.example.com 3600 IN AAAA ...
73
+ const [ owner, ttl, c, type, ip ] = opts.bindline.split(/\s+/)
74
+ return new AAAA({
75
+ owner,
76
+ ttl : parseInt(ttl, 10),
77
+ class : c,
78
+ type,
79
+ address: this.expand(ip),
80
+ })
81
+ }
82
+
83
+ compress (val) {
84
+ /*
85
+ * RFC 5952
86
+ * 4.1. Leading zeros MUST be suppressed...A single 16-bit 0000 field MUST be represented as 0.
87
+ * 4.2.1 The use of the symbol "::" MUST be used to its maximum capability.
88
+ * 4.2.2 The symbol "::" MUST NOT be used to shorten just one 16-bit 0 field.
89
+ * 4.2.3 When choosing placement of a "::", the longest run...MUST be shortened
90
+ * 4.3 The characters a-f in an IPv6 address MUST be represented in lowercase.
91
+ */
92
+ let r = val
93
+ .replace(/0000/g, '0') // 4.1 0000 -> 0
94
+ .replace(/:0+([1-9a-fA-F])/g, ':$1') // 4.1 remove leading zeros
95
+
96
+ const mostConsecutiveZeros = [
97
+ new RegExp(/0?(?::0){6,}:0?/),
98
+ new RegExp(/0?(?::0){5,}:0?/),
99
+ new RegExp(/0?(?::0){4,}:0?/),
100
+ new RegExp(/0?(?::0){3,}:0?/),
101
+ new RegExp(/0?(?::0){2,}:0?/),
102
+ ]
103
+
104
+ for (const re of mostConsecutiveZeros) {
105
+ if (re.test(r)) {
106
+ r = r.replace(re, '::')
107
+ break
108
+ }
109
+ }
110
+
111
+ return r
112
+ }
113
+
114
+ expand (val, delimiter) {
115
+ if (delimiter === undefined) delimiter = ':'
116
+
117
+ const colons = val.match(/:/g)
118
+ if (colons && colons.length < 7) {
119
+ // console.log(`AAAA: restoring compressed colons`)
120
+ val = val.replace(/::/, ':'.repeat(9 - colons.length))
121
+ }
122
+
123
+ // restore compressed leading zeros
124
+ return val.split(':').map(s => s.padStart(4, 0)).join(delimiter)
125
+ }
126
+
127
+ /****** EXPORTERS *******/
128
+ toBind (zone_opts) {
129
+ return `${this.getPrefix(zone_opts)}\t${this.compress(this.get('address'))}\n`
130
+ }
131
+
132
+ toTinydns () {
133
+ // from AAAA notation (8 groups of 4 hex digits) to 16 escaped octals
134
+ const rdata = TINYDNS.packHex(this.expand(this.get('address'), ''))
135
+ return this.getTinydnsGeneric(rdata)
136
+ }
137
+ }
package/rr/caa.js ADDED
@@ -0,0 +1,129 @@
1
+
2
+ import RR from '../rr.js'
3
+ import * as TINYDNS from '../lib/tinydns.js'
4
+
5
+ export default class CAA extends RR {
6
+ constructor (opts) {
7
+ super(opts)
8
+ }
9
+
10
+ /****** Resource record specific setters *******/
11
+ setFlags (val) {
12
+ this.is8bitInt('CAA', 'flags', val)
13
+
14
+ if (![ 0, 128 ].includes(val)) {
15
+ throw new Error(`CAA flags ${val} not recognized, ${this.citeRFC()}`)
16
+ }
17
+
18
+ this.set('flags', val)
19
+ }
20
+
21
+ setTag (val) {
22
+ if (typeof val !== 'string'
23
+ || val.length < 1
24
+ || /[^a-z0-9]/.test(val))
25
+ throw new Error(`CAA tag must be a sequence of ASCII letters and numbers in lowercase, ${this.citeRFC()}`)
26
+
27
+ if (![ 'issue', 'issuewild', 'iodef' ].includes(val)) {
28
+ throw new Error(`CAA tag ${val} not recognized: ${this.citeRFC()}`)
29
+ }
30
+ this.set('tag', val)
31
+ }
32
+
33
+ setValue (val) {
34
+ // either (2) a quoted string or
35
+ // (1) a contiguous set of characters without interior spaces
36
+ if (this.isQuoted(val)) {
37
+ val = val.replace(/^["']|["']$/g, '') // strip quotes
38
+ }
39
+ else {
40
+ // if (/\s/.test(val)) throw new Error(`CAA value may not have spaces unless quoted: RFC 8659`)
41
+ }
42
+
43
+ // check if val starts with one of iodefSchemes
44
+ if (this.get('tag') === 'iodef') {
45
+ const iodefSchemes = [ 'mailto:', 'http:', 'https:' ]
46
+ if (!iodefSchemes.filter(s => val.startsWith(s)).length) {
47
+ throw new Error(`CAA value must have valid iodefScheme prefix, ${this.citeRFC()}`)
48
+ }
49
+ }
50
+
51
+ this.set('value', val)
52
+ }
53
+
54
+ getDescription () {
55
+ return 'Certification Authority Authorization'
56
+ }
57
+
58
+ getQuotedFields () {
59
+ return [ 'value' ]
60
+ }
61
+
62
+ getRdataFields (arg) {
63
+ return [ 'flags', 'tag', 'value' ]
64
+ }
65
+
66
+ getRFCs () {
67
+ return [ 6844, 8659 ]
68
+ }
69
+
70
+ getTypeId () {
71
+ return 257
72
+ }
73
+
74
+ /****** IMPORTERS *******/
75
+ fromTinydns (opts) {
76
+ // CAA via generic, :fqdn:n:rdata:ttl:timestamp:lo
77
+ const [ fqdn, n, rdata, ttl, ts, loc ] = opts.tinyline.substring(1).split(':')
78
+ if (n != 257) throw new Error('CAA fromTinydns, invalid n')
79
+
80
+ const flags = TINYDNS.octalToUInt8(rdata.substring(0, 4))
81
+ const taglen = TINYDNS.octalToUInt8(rdata.substring(4, 8))
82
+
83
+ const unescaped = TINYDNS.octalToChar(rdata.substring(8))
84
+ const tag = unescaped.substring(0, taglen)
85
+ const fingerprint = unescaped.substring(taglen)
86
+
87
+ return new CAA({
88
+ owner : this.fullyQualify(fqdn),
89
+ ttl : parseInt(ttl, 10),
90
+ type : 'CAA',
91
+ flags,
92
+ tag,
93
+ value : fingerprint,
94
+ timestamp: ts,
95
+ location : loc !== '' && loc !== '\n' ? loc : '',
96
+ })
97
+ }
98
+
99
+ fromBind (opts) {
100
+ // test.example.com 3600 IN CAA flags, tags, value
101
+ const fields = opts.bindline.match(/^([^\s]+)\s+([0-9]+)\s+(\w+)\s+(\w+)\s+([0-9]+)\s+(\w+)\s+("[^"]+"|[^\s]+?)\s*$/i)
102
+ if (!fields) throw new Error(`unable to parse: ${opts.bindline}`)
103
+
104
+ const [ owner, ttl, c, type, flags, tag, value ] = fields.slice(1)
105
+ return new CAA({
106
+ owner,
107
+ ttl : parseInt(ttl, 10),
108
+ class: c,
109
+ type,
110
+ flags: parseInt(flags, 10),
111
+ tag,
112
+ value,
113
+ })
114
+ }
115
+
116
+ /****** EXPORTERS *******/
117
+
118
+ toTinydns () {
119
+ let rdata = ''
120
+ rdata += TINYDNS.UInt8toOctal(this.get('flags'))
121
+
122
+ rdata += TINYDNS.UInt8toOctal(this.get('tag').length)
123
+ rdata += TINYDNS.escapeOctal(/[\r\n\t:\\/]/, this.get('tag'))
124
+
125
+ rdata += TINYDNS.escapeOctal(/[\r\n\t:\\/]/, this.getQuoted('value'))
126
+
127
+ return this.getTinydnsGeneric(rdata)
128
+ }
129
+ }
package/rr/cert.js ADDED
@@ -0,0 +1,75 @@
1
+
2
+ import RR from '../rr.js'
3
+
4
+ export default class CERT extends RR {
5
+ constructor (opts) {
6
+ super(opts)
7
+ }
8
+
9
+ /****** Resource record specific setters *******/
10
+ setCertType (val) {
11
+ // The type field is the certificate type
12
+ // the type field as an unsigned decimal integer or as a mnemonic symbol
13
+ // this.is16bitInt('CERT', 'type', val)
14
+
15
+ this.set('cert type', val)
16
+ }
17
+
18
+ setKeyTag (val) {
19
+ // The key tag field is the 16-bit value
20
+ // The key tag field is represented as an unsigned decimal integer.
21
+
22
+ this.is16bitInt('CERT', 'key tag', val)
23
+
24
+ this.set('key tag', val)
25
+ }
26
+
27
+ setAlgorithm (val) {
28
+ // The algorithm field has the same meaning as the algorithm field in DNSKEY
29
+ // The algorithm field is represented as an unsigned decimal integer
30
+ this.is8bitInt('CERT', 'algorithm', val)
31
+
32
+ this.set('algorithm', val)
33
+ }
34
+
35
+ setCertificate (val) {
36
+ // certificate/CRL portion is represented in base 64 [16] and may be
37
+ // divided into any number of white-space-separated substrings
38
+ this.set('certificate', val)
39
+ }
40
+
41
+ getDescription () {
42
+ return 'Certificate'
43
+ }
44
+
45
+ getRdataFields () {
46
+ return [ 'cert type', 'key tag', 'algorithm', 'certificate' ]
47
+ }
48
+
49
+ getRFCs () {
50
+ return [ 2538, 4398 ]
51
+ }
52
+
53
+ getTypeId () {
54
+ return 37
55
+ }
56
+
57
+ /****** IMPORTERS *******/
58
+
59
+ fromBind (opts) {
60
+ // test.example.com 3600 IN CERT certtype, keytag, algo, cert
61
+ const [ owner, ttl, c, type, certtype, keytag, algo, certificate ] = opts.bindline.split(/\s+/)
62
+ return new CERT({
63
+ owner,
64
+ ttl : parseInt(ttl, 10),
65
+ class : c,
66
+ type,
67
+ 'cert type': /^[0-9]+$/.test(certtype) ? parseInt(certtype, 10) : certtype,
68
+ 'key tag' : parseInt(keytag, 10),
69
+ algorithm : parseInt(algo, 10),
70
+ certificate,
71
+ })
72
+ }
73
+
74
+ /****** EXPORTERS *******/
75
+ }
package/rr/cname.js ADDED
@@ -0,0 +1,76 @@
1
+
2
+ import net from 'net'
3
+
4
+ import RR from '../rr.js'
5
+
6
+ export default class CNAME extends RR {
7
+ constructor (opts) {
8
+ super(opts)
9
+ }
10
+
11
+ /****** Resource record specific setters *******/
12
+ setCname (val) {
13
+ // A <domain-name> which specifies the canonical or primary
14
+ // name for the owner. The owner name is an alias.
15
+
16
+ if (!val) throw new Error('CNAME: cname is required')
17
+
18
+ if (net.isIPv4(val) || net.isIPv6(val))
19
+ throw new Error(`CNAME: cname must be a FQDN: RFC 2181`)
20
+
21
+ if (!this.isFullyQualified('CNAME', 'cname', val)) return
22
+ if (!this.isValidHostname('CNAME', 'cname', val)) return
23
+
24
+ // RFC 4034: letters in the DNS names are lower cased
25
+ this.set('cname', val.toLowerCase())
26
+ }
27
+
28
+ getDescription () {
29
+ return 'Canonical Name'
30
+ }
31
+
32
+ getRdataFields (arg) {
33
+ return [ 'cname' ]
34
+ }
35
+
36
+ getRFCs () {
37
+ return [ 1035, 2181 ]
38
+ }
39
+
40
+ getTypeId () {
41
+ return 5
42
+ }
43
+
44
+ /****** IMPORTERS *******/
45
+ fromTinydns (opts) {
46
+ // Cfqdn:p:ttl:timestamp:lo
47
+ const [ fqdn, p, ttl, ts, loc ] = opts.tinyline.substring(1).split(':')
48
+
49
+ return new CNAME({
50
+ owner : this.fullyQualify(fqdn),
51
+ ttl : parseInt(ttl, 10),
52
+ type : 'CNAME',
53
+ cname : this.fullyQualify(p),
54
+ timestamp: ts,
55
+ location : loc !== '' && loc !== '\n' ? loc : '',
56
+ })
57
+ }
58
+
59
+ fromBind (opts) {
60
+ // test.example.com 3600 IN CNAME ...
61
+ const [ owner, ttl, c, type, cname ] = opts.bindline.split(/\s+/)
62
+ return new CNAME({
63
+ owner,
64
+ ttl : parseInt(ttl, 10),
65
+ class: c,
66
+ type,
67
+ cname,
68
+ })
69
+ }
70
+
71
+ /****** EXPORTERS *******/
72
+
73
+ toTinydns () {
74
+ return `C${this.getTinyFQDN('owner')}:${this.get('cname')}:${this.getTinydnsPostamble()}\n`
75
+ }
76
+ }