@nictool/dns-resource-record 1.4.0 → 1.6.0

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.
package/rr/nsec3param.js CHANGED
@@ -11,7 +11,7 @@ export default class NSEC3PARAM extends RR {
11
11
  setHashAlgorithm(val) {
12
12
  // Hash Algorithm is a single octet.
13
13
  // The Hash Algorithm field is represented as an unsigned decimal integer.
14
- if (!val) this.throwHelp(`NSEC3PARAM: 'hash algorithm' is required`)
14
+ if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'hash algorithm' is required`)
15
15
 
16
16
  this.is8bitInt('NSEC3PARAM', 'hash algorithm', val)
17
17
 
@@ -20,7 +20,7 @@ export default class NSEC3PARAM extends RR {
20
20
 
21
21
  setFlags(val) {
22
22
  // The Flags field is represented as an unsigned decimal integer.
23
- if (!val) this.throwHelp(`NSEC3PARAM: 'flags' is required`)
23
+ if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'flags' is required`)
24
24
 
25
25
  this.is8bitInt('NSEC3PARAM', 'flags', val)
26
26
 
@@ -29,7 +29,7 @@ export default class NSEC3PARAM extends RR {
29
29
 
30
30
  setIterations(val) {
31
31
  // The Iterations field is represented as an unsigned decimal integer. 0-65535
32
- if (!val) this.throwHelp(`NSEC3PARAM: 'iterations' is required`)
32
+ if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'iterations' is required`)
33
33
 
34
34
  this.is16bitInt('NSEC3PARAM', 'iterations', val)
35
35
 
@@ -41,6 +41,15 @@ export default class NSEC3PARAM extends RR {
41
41
  // hexadecimal digits. Whitespace is not allowed within the
42
42
  // sequence. The Salt field is represented as "-" (without the
43
43
  // quotes) when the Salt Length field has a value of 0
44
+ if (val === '-') {
45
+ this.set('salt', val)
46
+ return
47
+ }
48
+
49
+ if (val !== undefined && val !== null && !/^[0-9A-Fa-f]*$/.test(val)) {
50
+ this.throwHelp(`NSEC3PARAM: 'salt' must be hex or '-'`)
51
+ }
52
+
44
53
  this.set('salt', val)
45
54
  }
46
55
 
@@ -48,6 +57,10 @@ export default class NSEC3PARAM extends RR {
48
57
  return 'Next Secure Parameters'
49
58
  }
50
59
 
60
+ getTags() {
61
+ return ['dnssec']
62
+ }
63
+
51
64
  getRdataFields(arg) {
52
65
  return ['hash algorithm', 'flags', 'iterations', 'salt']
53
66
  }
@@ -60,12 +73,25 @@ export default class NSEC3PARAM extends RR {
60
73
  return 51
61
74
  }
62
75
 
76
+ getCanonical() {
77
+ return {
78
+ owner: 'example.com.',
79
+ ttl: 3600,
80
+ class: 'IN',
81
+ type: 'NSEC3PARAM',
82
+ 'hash algorithm': 1,
83
+ flags: 1,
84
+ iterations: 12,
85
+ salt: 'aabbccdd',
86
+ }
87
+ }
88
+
63
89
  /****** IMPORTERS *******/
64
90
 
65
- fromBind(str) {
91
+ fromBind({ bindline }) {
66
92
  // test.example.com 3600 IN NSEC3PARAM <hash> <flags> <iterations> <salt>
67
93
  // Example: test.example.com. 3600 IN NSEC3PARAM 1 1 12 aabbccdd
68
- const [owner, ttl, c, type, ha, flags, iterations, salt] = str.split(/\s+/)
94
+ const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
69
95
  return new NSEC3PARAM({
70
96
  owner,
71
97
  ttl: parseInt(ttl, 10),
@@ -78,22 +104,27 @@ export default class NSEC3PARAM extends RR {
78
104
  })
79
105
  }
80
106
 
81
- fromTinydns(opts) {
82
- const rd = opts.rd
83
-
107
+ fromTinydns({ tinyline }) {
84
108
  // RDATA format: Hash Algorithm (3 octal chars) + Flags (3 octal chars) + Iterations (6 octal chars) + Salt (escaped hex string)
85
- if (rd.length < 12) {
109
+ const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
110
+ if (rd.length < 4) {
86
111
  this.throwHelp(`NSEC3PARAM: RDATA too short: ${rd}`)
87
112
  }
88
113
 
114
+ // rd may contain actual binary characters (from JS string '\\001' -> char 0x01),
115
+ // so convert via octalToChar and read bytes from a Buffer for robust parsing.
116
+ const bytes = Buffer.from(TINYDNS.octalToChar(rd), 'binary')
117
+
89
118
  return new NSEC3PARAM({
90
- owner: this.fullyQualify(opts.owner),
91
- ttl: parseInt(opts.ttl, 10),
119
+ owner: this.fullyQualify(owner),
120
+ ttl: parseInt(ttl, 10),
92
121
  type: 'NSEC3PARAM',
93
- 'hash algorithm': TINYDNS.octalToUInt8(rd.substring(0, 3)),
94
- flags: TINYDNS.octalToUInt8(rd.substring(3, 6)),
95
- iterations: TINYDNS.octalToUInt16(rd.substring(6, 12)),
96
- salt: TINYDNS.unescapeOctal(rd.substring(12)),
122
+ 'hash algorithm': bytes.readUInt8(0),
123
+ flags: bytes.readUInt8(1),
124
+ iterations: bytes.readUInt16BE(2),
125
+ salt: bytes.slice(4).toString('utf8'),
126
+ timestamp: ts,
127
+ location: loc?.trim() ?? '',
97
128
  })
98
129
  }
99
130
 
@@ -108,12 +139,11 @@ export default class NSEC3PARAM extends RR {
108
139
  toTinydns() {
109
140
  const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
110
141
 
111
- let rdata = ''
112
- rdata += TINYDNS.UInt8toOctal(this.get('hash algorithm'))
113
- rdata += TINYDNS.UInt8toOctal(this.get('flags'))
114
- rdata += TINYDNS.UInt16toOctal(this.get('iterations'))
115
- rdata += TINYDNS.escapeOctal(dataRe, this.get('salt'))
116
-
117
- return this.getTinydnsGeneric(rdata)
142
+ return this.getTinydnsGeneric(
143
+ TINYDNS.UInt8toOctal(this.get('hash algorithm')) +
144
+ TINYDNS.UInt8toOctal(this.get('flags')) +
145
+ TINYDNS.UInt16toOctal(this.get('iterations')) +
146
+ TINYDNS.escapeOctal(dataRe, this.get('salt')),
147
+ )
118
148
  }
119
149
  }
package/rr/nxt.js CHANGED
@@ -29,6 +29,10 @@ export default class NXT extends RR {
29
29
  return 'Next Secure'
30
30
  }
31
31
 
32
+ getTags() {
33
+ return ['deprecated']
34
+ }
35
+
32
36
  getRdataFields(arg) {
33
37
  return ['next domain', 'type bit map']
34
38
  }
@@ -41,11 +45,22 @@ export default class NXT extends RR {
41
45
  return 30
42
46
  }
43
47
 
48
+ getCanonical() {
49
+ return {
50
+ owner: 'big.example.com.',
51
+ ttl: 3600,
52
+ class: 'IN',
53
+ type: 'NXT',
54
+ 'next domain': 'host.example.com.',
55
+ 'type bit map': 'A MX NXT',
56
+ }
57
+ }
58
+
44
59
  /****** IMPORTERS *******/
45
60
 
46
- fromTinydns(opts) {
47
- const [owner, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
48
- if (n != 30) this.throwHelp('NXT fromTinydns, invalid n')
61
+ fromTinydns({ tinyline }) {
62
+ const [owner, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
63
+ if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('NXT fromTinydns, invalid n')
49
64
 
50
65
  const binaryRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
51
66
  const [nextDomain, _escapedLen, binaryLen] = TINYDNS.unpackDomainName(rdata)
@@ -57,20 +72,20 @@ export default class NXT extends RR {
57
72
  'next domain': nextDomain,
58
73
  'type bit map': binaryRdata.slice(binaryLen).toString(),
59
74
  timestamp: ts,
60
- location: loc !== '' && loc !== '\n' ? loc : '',
75
+ location: loc?.trim() ?? '',
61
76
  })
62
77
  }
63
78
 
64
- fromBind(opts) {
79
+ fromBind({ bindline }) {
65
80
  // test.example.com 3600 IN NXT NextDomain TypeBitMap
66
- const [owner, ttl, c, type, next] = opts.bindline.split(/\s+/)
81
+ const [owner, ttl, c, type, next] = bindline.split(/\s+/)
67
82
  return new NXT({
68
83
  owner,
69
84
  ttl: parseInt(ttl, 10),
70
85
  class: c,
71
86
  type: type,
72
87
  'next domain': next,
73
- 'type bit map': opts.bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
88
+ 'type bit map': bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
74
89
  })
75
90
  }
76
91
 
package/rr/openpgpkey.js CHANGED
@@ -15,6 +15,10 @@ export default class OPENPGPKEY extends RR {
15
15
  return 'OpenPGP Public Key'
16
16
  }
17
17
 
18
+ getTags() {
19
+ return ['security']
20
+ }
21
+
18
22
  getRdataFields() {
19
23
  return ['public key']
20
24
  }
@@ -27,12 +31,25 @@ export default class OPENPGPKEY extends RR {
27
31
  return 61
28
32
  }
29
33
 
34
+ getCanonical() {
35
+ return {
36
+ owner: 'matt.example.com.',
37
+ ttl: 3600,
38
+ class: 'IN',
39
+ type: 'OPENPGPKEY',
40
+ 'public key': 'mQINBFY...',
41
+ }
42
+ }
43
+
30
44
  /****** IMPORTERS *******/
31
- fromBind(obj) {
45
+ fromBind({ bindline: bindline }) {
32
46
  // test.example.com 3600 IN OPENPGPKEY <base64 public key>
33
- const regex = /^([\S]+)\s+(\d{1,10})\s+(IN)\s+(OPENPGPKEY)\s+([\W\w]*)$/
34
- // eslint-disable-next-line no-unused-vars
35
- const [ignore, owner, ttl, c, type, publickey] = obj.bindline.trim().match(regex)
47
+ const regex =
48
+ /^(?<owner>\S+)\s+(?<ttl>\d{1,10})\s+(?<class>IN)\s+(?<type>OPENPGPKEY)\s+(?<publickey>\S[\s\S]*)$/i
49
+ const match = bindline.trim().match(regex)
50
+ if (!match) this.throwHelp(`unable to parse OPENPGPKEY: ${bindline}`)
51
+
52
+ const { owner, ttl, class: c, type, publickey } = match.groups
36
53
 
37
54
  return new OPENPGPKEY({
38
55
  owner,
@@ -43,12 +60,15 @@ export default class OPENPGPKEY extends RR {
43
60
  })
44
61
  }
45
62
 
46
- fromTinydns(opts) {
63
+ fromTinydns({ tinyline }) {
64
+ const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
47
65
  return new OPENPGPKEY({
48
- owner: this.fullyQualify(opts.owner),
49
- ttl: parseInt(opts.ttl, 10),
66
+ owner: this.fullyQualify(owner),
67
+ ttl: parseInt(ttl, 10),
50
68
  type: 'OPENPGPKEY',
51
- 'public key': Buffer.from(TINYDNS.unescapeOctal(opts.rd), 'base64').toString('utf-8'),
69
+ 'public key': Buffer.from(TINYDNS.unescapeOctal(rd), 'base64').toString('utf-8'),
70
+ timestamp: ts,
71
+ location: loc?.trim() ?? '',
52
72
  })
53
73
  }
54
74
 
package/rr/ptr.js CHANGED
@@ -18,6 +18,10 @@ export default class PTR extends RR {
18
18
  return 'Pointer'
19
19
  }
20
20
 
21
+ getTags() {
22
+ return ['common']
23
+ }
24
+
21
25
  getRdataFields(arg) {
22
26
  return ['dname']
23
27
  }
@@ -30,10 +34,20 @@ export default class PTR extends RR {
30
34
  return 12
31
35
  }
32
36
 
37
+ getCanonical() {
38
+ return {
39
+ owner: '2.2.0.192.in-addr.arpa.',
40
+ ttl: 3600,
41
+ class: 'IN',
42
+ type: 'PTR',
43
+ dname: 'host.example.com.',
44
+ }
45
+ }
46
+
33
47
  /****** IMPORTERS *******/
34
- fromTinydns(opts) {
48
+ fromTinydns({ tinyline }) {
35
49
  // ^fqdn:p:ttl:timestamp:lo
36
- const [fqdn, p, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
50
+ const [fqdn, p, ttl, ts, loc] = tinyline.slice(1).split(':')
37
51
 
38
52
  return new PTR({
39
53
  owner: this.fullyQualify(fqdn),
@@ -41,13 +55,13 @@ export default class PTR extends RR {
41
55
  type: 'PTR',
42
56
  dname: this.fullyQualify(p),
43
57
  timestamp: ts,
44
- location: loc !== '' && loc !== '\n' ? loc : '',
58
+ location: loc?.trim() ?? '',
45
59
  })
46
60
  }
47
61
 
48
- fromBind(opts) {
62
+ fromBind({ bindline }) {
49
63
  // test.example.com 3600 IN PTR dname
50
- const [owner, ttl, c, type, dname] = opts.bindline.split(/\s+/)
64
+ const [owner, ttl, c, type, dname] = bindline.split(/\s+/)
51
65
  return new PTR({
52
66
  owner,
53
67
  ttl: parseInt(ttl, 10),
@@ -58,6 +72,10 @@ export default class PTR extends RR {
58
72
  }
59
73
 
60
74
  /****** EXPORTERS *******/
75
+ getWireRdata() {
76
+ return this.wirePackDomain(this.get('dname'))
77
+ }
78
+
61
79
  toTinydns() {
62
80
  return `^${this.getTinyFQDN('owner')}:${this.getTinyFQDN('dname')}:${this.getTinydnsPostamble()}\n`
63
81
  }
package/rr/rp.js CHANGED
@@ -28,6 +28,10 @@ export default class RP extends RR {
28
28
  return 'Responsible Person'
29
29
  }
30
30
 
31
+ getTags() {
32
+ return ['obsolete']
33
+ }
34
+
31
35
  getRdataFields(arg) {
32
36
  return ['mbox', 'txt']
33
37
  }
@@ -52,9 +56,9 @@ export default class RP extends RR {
52
56
  }
53
57
 
54
58
  /****** IMPORTERS *******/
55
- fromBind(opts) {
59
+ fromBind({ bindline }) {
56
60
  // test.example.com 3600 IN RP mbox txt
57
- const [owner, ttl, c, type, mbox, txt] = opts.bindline.split(/\s+/)
61
+ const [owner, ttl, c, type, mbox, txt] = bindline.split(/\s+/)
58
62
  return new RP({
59
63
  owner,
60
64
  ttl: parseInt(ttl, 10),
@@ -65,6 +69,23 @@ export default class RP extends RR {
65
69
  })
66
70
  }
67
71
 
72
+ fromTinydns({ tinyline }) {
73
+ const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
74
+
75
+ const [mbox, consumed] = TINYDNS.unpackDomainName(rdata)
76
+ const txt = TINYDNS.unpackDomainName(rdata.slice(consumed))[0]
77
+
78
+ return new RP({
79
+ owner: this.fullyQualify(owner),
80
+ ttl: parseInt(ttl, 10),
81
+ type: 'RP',
82
+ mbox,
83
+ txt,
84
+ timestamp: ts,
85
+ location: loc?.trim() ?? '',
86
+ })
87
+ }
88
+
68
89
  /****** EXPORTERS *******/
69
90
  toBind(zone_opts) {
70
91
  return `${this.getPrefix(zone_opts)}\t${this.getFQDN('mbox', zone_opts)}\t${this.getFQDN('txt', zone_opts)}\n`
package/rr/rrsig.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import RR from '../rr.js'
2
+ import * as TINYDNS from '../lib/tinydns.js'
2
3
 
3
4
  export default class RRSIG extends RR {
4
5
  constructor(opts) {
@@ -77,6 +78,10 @@ export default class RRSIG extends RR {
77
78
  return 'Resource Record Signature'
78
79
  }
79
80
 
81
+ getTags() {
82
+ return ['dnssec']
83
+ }
84
+
80
85
  getRdataFields(arg) {
81
86
  return [
82
87
  'type covered',
@@ -99,18 +104,109 @@ export default class RRSIG extends RR {
99
104
  return 46
100
105
  }
101
106
 
107
+ getCanonical() {
108
+ return {
109
+ owner: 'example.com.',
110
+ ttl: 3600,
111
+ class: 'IN',
112
+ type: 'RRSIG',
113
+ 'type covered': 1,
114
+ algorithm: 5,
115
+ labels: 3,
116
+ 'original ttl': 3600,
117
+ 'signature expiration': 1045053120,
118
+ 'signature inception': 1042461120,
119
+ 'key tag': 12345,
120
+ 'signers name': 'example.com.',
121
+ signature: 'ABCDEF...',
122
+ }
123
+ }
124
+
102
125
  /****** IMPORTERS *******/
103
126
 
104
- // fromBind (str) {
105
- // // test.example.com 3600 IN RRSIG ...
106
- // const [ owner, ttl, c, type ] = str.split(/\s+/)
107
- // return new RRSIG({
108
- // owner,
109
- // ttl : parseInt(ttl, 10),
110
- // class : c,
111
- // type : type,
112
- // })
113
- // }
127
+ fromBind({ bindline }) {
128
+ // example.com. 3600 IN RRSIG typecovered algorithm labels origttl sigexp siginc keytag signersname ( signature )
129
+ const parts = bindline.trim().split(/\s+/)
130
+ return new RRSIG({
131
+ owner: parts[0],
132
+ ttl: parseInt(parts[1], 10),
133
+ class: parts[2],
134
+ type: 'RRSIG',
135
+ 'type covered': parseInt(parts[4], 10),
136
+ algorithm: parseInt(parts[5], 10),
137
+ labels: parseInt(parts[6], 10),
138
+ 'original ttl': parseInt(parts[7], 10),
139
+ 'signature expiration': parseInt(parts[8], 10),
140
+ 'signature inception': parseInt(parts[9], 10),
141
+ 'key tag': parseInt(parts[10], 10),
142
+ 'signers name': parts[11],
143
+ signature: parts
144
+ .slice(12)
145
+ .filter((a) => a !== '(' && a !== ')')
146
+ .join(' ')
147
+ .trim(),
148
+ })
149
+ }
150
+
151
+ fromTinydns({ tinyline }) {
152
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
153
+ if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('RRSIG fromTinydns, invalid n')
154
+
155
+ const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
156
+ const typeCovered = bytes.readUInt16BE(0)
157
+ const algorithm = bytes.readUInt8(2)
158
+ const labels = bytes.readUInt8(3)
159
+ const originalTtl = bytes.readUInt32BE(4)
160
+ const signatureExpiration = bytes.readUInt32BE(8)
161
+ const signatureInception = bytes.readUInt32BE(12)
162
+ const keyTag = bytes.readUInt16BE(16)
163
+
164
+ let pos = 18
165
+ const labelArr = []
166
+ while (pos < bytes.length) {
167
+ const len = bytes.readUInt8(pos++)
168
+ if (len === 0) break
169
+ labelArr.push(bytes.slice(pos, pos + len).toString())
170
+ pos += len
171
+ }
172
+ const signersName = `${labelArr.join('.')}.`
173
+ const signature = bytes.slice(pos).toString()
174
+
175
+ return new RRSIG({
176
+ owner: this.fullyQualify(fqdn),
177
+ ttl: parseInt(ttl, 10),
178
+ type: 'RRSIG',
179
+ 'type covered': typeCovered,
180
+ algorithm,
181
+ labels,
182
+ 'original ttl': originalTtl,
183
+ 'signature expiration': signatureExpiration,
184
+ 'signature inception': signatureInception,
185
+ 'key tag': keyTag,
186
+ 'signers name': signersName,
187
+ signature,
188
+ timestamp: ts,
189
+ location: loc?.trim() ?? '',
190
+ })
191
+ }
114
192
 
115
193
  /****** EXPORTERS *******/
194
+ toBind(zone_opts) {
195
+ return `${this.getPrefix(zone_opts)}\t${this.get('type covered')}\t${this.get('algorithm')}\t${this.get('labels')}\t${this.get('original ttl')}\t${this.get('signature expiration')}\t${this.get('signature inception')}\t${this.get('key tag')}\t${this.getFQDN('signers name', zone_opts)}\t${this.get('signature')}\n`
196
+ }
197
+
198
+ toTinydns() {
199
+ const dataRe = new RegExp(/[\r\n\t:]/, 'g')
200
+ return this.getTinydnsGeneric(
201
+ TINYDNS.UInt16toOctal(this.get('type covered')) +
202
+ TINYDNS.UInt8toOctal(this.get('algorithm')) +
203
+ TINYDNS.UInt8toOctal(this.get('labels')) +
204
+ TINYDNS.UInt32toOctal(this.get('original ttl')) +
205
+ TINYDNS.UInt32toOctal(this.get('signature expiration')) +
206
+ TINYDNS.UInt32toOctal(this.get('signature inception')) +
207
+ TINYDNS.UInt16toOctal(this.get('key tag')) +
208
+ TINYDNS.packDomainName(this.get('signers name')) +
209
+ TINYDNS.escapeOctal(dataRe, this.get('signature')),
210
+ )
211
+ }
116
212
  }
package/rr/sig.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import RR from '../rr.js'
2
2
 
3
+ import * as TINYDNS from '../lib/tinydns.js'
4
+
3
5
  export default class SIG extends RR {
4
6
  constructor(opts) {
5
7
  super(opts)
@@ -15,8 +17,7 @@ export default class SIG extends RR {
15
17
 
16
18
  setAlgorithm(val) {
17
19
  // a 1 octet Algorithm field
18
-
19
- this.is8bitInt('SIG', 'labels', val)
20
+ this.is8bitInt('SIG', 'algorithm', val)
20
21
 
21
22
  this.set('algorithm', val)
22
23
  }
@@ -67,6 +68,10 @@ export default class SIG extends RR {
67
68
  return 'Signature'
68
69
  }
69
70
 
71
+ getTags() {
72
+ return ['obsolete']
73
+ }
74
+
70
75
  getRdataFields(arg) {
71
76
  return [
72
77
  'type covered',
@@ -82,26 +87,119 @@ export default class SIG extends RR {
82
87
  }
83
88
 
84
89
  getRFCs() {
85
- return [2535]
90
+ return [2535, 3755]
86
91
  }
87
92
 
88
93
  getTypeId() {
89
94
  return 24
90
95
  }
91
96
 
97
+ getCanonical() {
98
+ return {
99
+ owner: 'example.com.',
100
+ ttl: 3600,
101
+ class: 'IN',
102
+ type: 'SIG',
103
+ 'type covered': 1,
104
+ algorithm: 5,
105
+ labels: 3,
106
+ 'original ttl': 3600,
107
+ 'signature expiration': 1045053120,
108
+ 'signature inception': 1042461120,
109
+ 'key tag': 12345,
110
+ 'signers name': 'example.com.',
111
+ signature: 'ABCDEF...',
112
+ }
113
+ }
114
+
92
115
  /****** IMPORTERS *******/
93
- // fromBind (str) {
94
- // // test.example.com 3600 IN SIG ...
95
- // const [ owner, ttl, c, type ] = str.split(/\s+/)
96
- // return new SIG({
97
- // owner,
98
- // ttl : parseInt(ttl, 10),
99
- // class : c,
100
- // type : type,
101
- // })
102
- // }
116
+
117
+ fromBind({ bindline }) {
118
+ // example.com. 3600 IN SIG TypeCovered Algorithm Labels OrigTTL SigExpiration SigInception KeyTag SignersName ( Signature )
119
+ const parts = bindline.trim().split(/\s+/)
120
+
121
+ return new SIG({
122
+ owner: parts[0],
123
+ ttl: parseInt(parts[1], 10),
124
+ class: parts[2],
125
+ type: 'SIG',
126
+ 'type covered': parseInt(parts[4], 10),
127
+ algorithm: parseInt(parts[5], 10),
128
+ labels: parseInt(parts[6], 10),
129
+ 'original ttl': parseInt(parts[7], 10),
130
+ 'signature expiration': parseInt(parts[8], 10),
131
+ 'signature inception': parseInt(parts[9], 10),
132
+ 'key tag': parseInt(parts[10], 10),
133
+ 'signers name': parts[11],
134
+ signature: parts
135
+ .slice(12)
136
+ .filter((a) => a !== '(' && a !== ')')
137
+ .join(' ')
138
+ .trim(),
139
+ })
140
+ }
103
141
 
104
142
  /****** EXPORTERS *******/
143
+ toTinydns() {
144
+ const dataRe = new RegExp(/[\r\n\t:]/, 'g')
145
+
146
+ return this.getTinydnsGeneric(
147
+ TINYDNS.UInt16toOctal(this.get('type covered')) +
148
+ TINYDNS.UInt8toOctal(this.get('algorithm')) +
149
+ TINYDNS.UInt8toOctal(this.get('labels')) +
150
+ TINYDNS.UInt32toOctal(this.get('original ttl')) +
151
+ TINYDNS.UInt32toOctal(this.get('signature expiration')) +
152
+ TINYDNS.UInt32toOctal(this.get('signature inception')) +
153
+ TINYDNS.UInt16toOctal(this.get('key tag')) +
154
+ TINYDNS.packDomainName(this.get('signers name')) +
155
+ TINYDNS.escapeOctal(dataRe, this.get('signature')),
156
+ )
157
+ }
158
+
159
+ fromTinydns({ tinyline }) {
160
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.substring(1).split(':')
161
+ if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('SIG fromTinydns, invalid n')
162
+
163
+ const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
164
+
165
+ const typeCovered = bytes.readUInt16BE(0)
166
+ const algorithm = bytes.readUInt8(2)
167
+ const labels = bytes.readUInt8(3)
168
+ const originalTtl = bytes.readUInt32BE(4)
169
+ const signatureExpiration = bytes.readUInt32BE(8)
170
+ const signatureInception = bytes.readUInt32BE(12)
171
+ const keyTag = bytes.readUInt16BE(16)
172
+
173
+ // parse signers name from binary buffer starting at offset 18
174
+ let pos = 18
175
+ const labelsArr = []
176
+ while (pos < bytes.length) {
177
+ const len = bytes.readUInt8(pos++)
178
+ if (len === 0) break
179
+ labelsArr.push(bytes.slice(pos, pos + len).toString())
180
+ pos += len
181
+ }
182
+ const signersName = `${labelsArr.join('.')}.`
183
+
184
+ const signature = bytes.slice(pos).toString()
185
+
186
+ return new SIG({
187
+ owner: this.fullyQualify(fqdn),
188
+ ttl: parseInt(ttl, 10),
189
+ type: 'SIG',
190
+ 'type covered': typeCovered,
191
+ algorithm,
192
+ labels,
193
+ 'original ttl': originalTtl,
194
+ 'signature expiration': signatureExpiration,
195
+ 'signature inception': signatureInception,
196
+ 'key tag': keyTag,
197
+ 'signers name': signersName,
198
+ signature,
199
+ timestamp: ts,
200
+ location: loc?.trim() ?? '',
201
+ })
202
+ }
105
203
 
106
204
  toBind(zone_opts) {
107
205
  return `${this.getFQDN('owner', zone_opts)} ${this.get('ttl')} ${this.get('class')} SIG${this.getRdataFields()