@nictool/dns-resource-record 1.3.1 → 1.5.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/nsec3.js CHANGED
@@ -78,10 +78,10 @@ export default class NSEC3 extends RR {
78
78
 
79
79
  /****** IMPORTERS *******/
80
80
 
81
- fromBind(opts) {
81
+ fromBind({ bindline }) {
82
82
  // test.example.com. 3600 IN NSEC3 1 1 12 aabbccdd (2vptu5timamqttgl4luu9kg21e0aor3s A RRSIG)
83
- const [owner, ttl, c, type, ha, flags, iterations, salt] = opts.bindline.split(/\s+/)
84
- const rdata = opts.bindline.split(/\(|\)/)[1]
83
+ const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
84
+ const rdata = bindline.split(/\(|\)/)[1]
85
85
 
86
86
  return new NSEC3({
87
87
  owner,
@@ -93,41 +93,53 @@ export default class NSEC3 extends RR {
93
93
  iterations: parseInt(iterations, 10),
94
94
  salt,
95
95
  'next hashed owner name': rdata.split(/\s+/)[0],
96
- 'type bit maps': rdata.split(/\s+/).slice(1).join('\t'),
96
+ 'type bit maps': rdata.split(/\s+/).slice(1).join(' '),
97
97
  })
98
98
  }
99
99
 
100
- fromTinydns(opts) {
101
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
100
+ fromTinydns({ tinyline }) {
101
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
102
102
  if (n != 50) this.throwHelp('NSEC3 fromTinydns, invalid n')
103
103
 
104
104
  const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
105
105
 
106
+ const hashAlgorithm = bytes.readUInt8(0)
107
+ const flags = bytes.readUInt8(1)
108
+ const iterations = bytes.readUInt16BE(2)
109
+
110
+ // The remaining bytes in the buffer contain:
111
+ // Salt Length (1 octet)
112
+ // Salt (variable length based on Salt Length)
113
+ // Next Hashed Owner Name (variable length)
114
+ // Type Bit Maps (variable length)
115
+ const { salt, nextHashedOwnerName, typeBitMaps } = parseNSEC3Buffer(bytes)
116
+
106
117
  return new NSEC3({
107
118
  owner: this.fullyQualify(fqdn),
108
119
  ttl: parseInt(ttl, 10),
109
120
  type: 'NSEC3',
110
- 'hash algorithm': bytes.readUInt8(0),
111
- flags: bytes.readUInt8(1),
112
- iterations: bytes.readUInt16BE(2),
113
- // salt : ,
114
- // 'next hashed owner name': ,
115
- // 'type bit maps' : ,
121
+ 'hash algorithm': hashAlgorithm,
122
+ flags: flags,
123
+ iterations: iterations,
124
+ salt: salt,
125
+ 'next hashed owner name': nextHashedOwnerName,
126
+ 'type bit maps': typeBitMaps,
116
127
  timestamp: ts,
117
- location: loc !== '' && loc !== '\n' ? loc : '',
128
+ location: loc?.trim() ?? '',
118
129
  })
119
130
  }
120
131
 
121
132
  /****** EXPORTERS *******/
122
133
 
123
134
  toBind(zone_opts) {
124
- return `${this.getFQDN('owner', zone_opts)}\t${this.get('ttl')}\t${this.get('class')}\tNSEC3${this.getRdataFields()
135
+ return `${this.getFQDN('owner', zone_opts)} ${this.get('ttl')} ${this.get('class')} NSEC3${this.getRdataFields()
125
136
  .slice(0, 4)
126
- .map((f) => '\t' + this.get(f))
127
- .join('')}\t(${this.getRdataFields()
137
+ .map((f) => ' ' + this.get(f))
138
+ .join('')} (${this.getRdataFields()
128
139
  .slice(4)
129
140
  .map((f) => this.get(f))
130
- .join('\t')})\n`
141
+ .join(' ')})
142
+ `
131
143
  }
132
144
 
133
145
  toTinydns() {
@@ -143,3 +155,48 @@ export default class NSEC3 extends RR {
143
155
  )
144
156
  }
145
157
  }
158
+
159
+ function parseNSEC3Buffer(bytes) {
160
+ // bytes is a Buffer containing the full RDATA binary (hash alg, flags, iterations, then ASCII salt + next-hashed + type bit maps)
161
+ // Start after the first 4 bytes (hash alg, flags, iterations)
162
+ const rest = bytes.slice(4).toString('utf8')
163
+
164
+ // determine expected next hashed owner name length from hash algorithm
165
+ const hashAlgorithm = bytes.readUInt8(0)
166
+ // common mapping: algorithm 1 => SHA-1 => 20 bytes => base32 length 32
167
+ const expectedLen = hashAlgorithm === 1 ? 32 : hashAlgorithm === 2 ? 52 : 32
168
+
169
+ // salt length is ambiguous in this representation; try to find a split where
170
+ // the following segment matches expected base32 length
171
+ let salt = ''
172
+ let nextHashedOwnerName = ''
173
+ let typeBitMaps = ''
174
+
175
+ const maxSl = Math.min(64, rest.length)
176
+ for (let sl = maxSl; sl >= 1; sl--) {
177
+ const candNext = rest.slice(sl, sl + expectedLen)
178
+ if (candNext.length !== expectedLen) continue
179
+ if (!/^[0-9a-z]+$/.test(candNext)) continue
180
+ // candidate looks like a base32 name; accept and treat remainder as type bit maps
181
+ const saltCandidate = rest.slice(0, sl)
182
+ if (!/^[0-9A-Fa-f]+$/.test(saltCandidate)) continue
183
+ salt = saltCandidate
184
+ nextHashedOwnerName = candNext
185
+ typeBitMaps = rest.slice(sl + expectedLen)
186
+ break
187
+ }
188
+
189
+ // fallback: if we couldn't find a split, treat everything up to first non-hex as salt
190
+ if (!nextHashedOwnerName) {
191
+ const saltMatch = rest.match(/^([0-9A-Fa-f]*)/)
192
+ salt = saltMatch ? saltMatch[1] : ''
193
+ nextHashedOwnerName = rest.slice(salt.length)
194
+ typeBitMaps = ''
195
+ }
196
+
197
+ return {
198
+ salt,
199
+ nextHashedOwnerName,
200
+ typeBitMaps,
201
+ }
202
+ }
package/rr/nsec3param.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 NSEC3PARAM extends RR {
4
5
  constructor(opts) {
@@ -10,7 +11,7 @@ export default class NSEC3PARAM extends RR {
10
11
  setHashAlgorithm(val) {
11
12
  // Hash Algorithm is a single octet.
12
13
  // The Hash Algorithm field is represented as an unsigned decimal integer.
13
- if (!val) this.throwHelp(`NSEC3PARAM: 'hash algorithm' is required`)
14
+ if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'hash algorithm' is required`)
14
15
 
15
16
  this.is8bitInt('NSEC3PARAM', 'hash algorithm', val)
16
17
 
@@ -19,7 +20,7 @@ export default class NSEC3PARAM extends RR {
19
20
 
20
21
  setFlags(val) {
21
22
  // The Flags field is represented as an unsigned decimal integer.
22
- if (!val) this.throwHelp(`NSEC3PARAM: 'flags' is required`)
23
+ if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'flags' is required`)
23
24
 
24
25
  this.is8bitInt('NSEC3PARAM', 'flags', val)
25
26
 
@@ -28,7 +29,7 @@ export default class NSEC3PARAM extends RR {
28
29
 
29
30
  setIterations(val) {
30
31
  // The Iterations field is represented as an unsigned decimal integer. 0-65535
31
- if (!val) this.throwHelp(`NSEC3PARAM: 'iterations' is required`)
32
+ if (val === undefined || val === null) this.throwHelp(`NSEC3PARAM: 'iterations' is required`)
32
33
 
33
34
  this.is16bitInt('NSEC3PARAM', 'iterations', val)
34
35
 
@@ -40,6 +41,15 @@ export default class NSEC3PARAM extends RR {
40
41
  // hexadecimal digits. Whitespace is not allowed within the
41
42
  // sequence. The Salt field is represented as "-" (without the
42
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
+
43
53
  this.set('salt', val)
44
54
  }
45
55
 
@@ -61,22 +71,62 @@ export default class NSEC3PARAM extends RR {
61
71
 
62
72
  /****** IMPORTERS *******/
63
73
 
64
- fromBind(str) {
65
- // test.example.com 3600 IN NSEC3PARAM
66
- const [owner, ttl, c, type] = str.split(/\s+/)
74
+ fromBind({ bindline }) {
75
+ // test.example.com 3600 IN NSEC3PARAM <hash> <flags> <iterations> <salt>
76
+ // Example: test.example.com. 3600 IN NSEC3PARAM 1 1 12 aabbccdd
77
+ const [owner, ttl, c, type, ha, flags, iterations, salt] = bindline.split(/\s+/)
67
78
  return new NSEC3PARAM({
68
79
  owner,
69
80
  ttl: parseInt(ttl, 10),
70
81
  class: c,
71
82
  type: type,
72
- 'hash algorithm': '',
73
- flags: '',
74
- iterations: '',
75
- salt: '',
76
- 'next hashed owner name': '',
77
- 'type bit maps': '',
83
+ 'hash algorithm': parseInt(ha, 10),
84
+ flags: parseInt(flags, 10),
85
+ iterations: parseInt(iterations, 10),
86
+ salt: salt,
87
+ })
88
+ }
89
+
90
+ fromTinydns({ tinyline }) {
91
+ // RDATA format: Hash Algorithm (3 octal chars) + Flags (3 octal chars) + Iterations (6 octal chars) + Salt (escaped hex string)
92
+ const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
93
+ if (rd.length < 4) {
94
+ this.throwHelp(`NSEC3PARAM: RDATA too short: ${rd}`)
95
+ }
96
+
97
+ // rd may contain actual binary characters (from JS string '\\001' -> char 0x01),
98
+ // so convert via octalToChar and read bytes from a Buffer for robust parsing.
99
+ const bytes = Buffer.from(TINYDNS.octalToChar(rd), 'binary')
100
+
101
+ return new NSEC3PARAM({
102
+ owner: this.fullyQualify(owner),
103
+ ttl: parseInt(ttl, 10),
104
+ type: 'NSEC3PARAM',
105
+ 'hash algorithm': bytes.readUInt8(0),
106
+ flags: bytes.readUInt8(1),
107
+ iterations: bytes.readUInt16BE(2),
108
+ salt: bytes.slice(4).toString('utf8'),
109
+ timestamp: ts,
110
+ location: loc?.trim() ?? '',
78
111
  })
79
112
  }
80
113
 
81
114
  /****** EXPORTERS *******/
115
+
116
+ toBind(zone_opts) {
117
+ // Example: test.example.com. 3600 IN NSEC3PARAM 1 1 12 aabbccdd
118
+ return `${this.getFQDN('owner', zone_opts)} ${this.get('ttl')} ${this.get('class')} NSEC3PARAM ${this.get('hash algorithm')} ${this.get('flags')} ${this.get('iterations')} ${this.get('salt')}
119
+ `
120
+ }
121
+
122
+ toTinydns() {
123
+ const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
124
+
125
+ return this.getTinydnsGeneric(
126
+ TINYDNS.UInt8toOctal(this.get('hash algorithm')) +
127
+ TINYDNS.UInt8toOctal(this.get('flags')) +
128
+ TINYDNS.UInt16toOctal(this.get('iterations')) +
129
+ TINYDNS.escapeOctal(dataRe, this.get('salt')),
130
+ )
131
+ }
82
132
  }
package/rr/nxt.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 NXT extends RR {
4
6
  constructor(opts) {
5
7
  super(opts)
@@ -41,20 +43,46 @@ export default class NXT extends RR {
41
43
 
42
44
  /****** IMPORTERS *******/
43
45
 
44
- fromBind(opts) {
46
+ fromTinydns({ tinyline }) {
47
+ const [owner, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
48
+ if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('NXT fromTinydns, invalid n')
49
+
50
+ const binaryRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
51
+ const [nextDomain, _escapedLen, binaryLen] = TINYDNS.unpackDomainName(rdata)
52
+
53
+ return new NXT({
54
+ owner: this.fullyQualify(owner),
55
+ ttl: parseInt(ttl, 10),
56
+ type: 'NXT',
57
+ 'next domain': nextDomain,
58
+ 'type bit map': binaryRdata.slice(binaryLen).toString(),
59
+ timestamp: ts,
60
+ location: loc?.trim() ?? '',
61
+ })
62
+ }
63
+
64
+ fromBind({ bindline }) {
45
65
  // test.example.com 3600 IN NXT NextDomain TypeBitMap
46
- const [owner, ttl, c, type, next] = opts.bindline.split(/\s+/)
66
+ const [owner, ttl, c, type, next] = bindline.split(/\s+/)
47
67
  return new NXT({
48
68
  owner,
49
69
  ttl: parseInt(ttl, 10),
50
70
  class: c,
51
71
  type: type,
52
72
  'next domain': next,
53
- 'type bit map': opts.bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
73
+ 'type bit map': bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
54
74
  })
55
75
  }
56
76
 
57
77
  /****** EXPORTERS *******/
78
+
79
+ toTinydns() {
80
+ const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
81
+
82
+ return this.getTinydnsGeneric(
83
+ TINYDNS.packDomainName(this.get('next domain')) + TINYDNS.escapeOctal(dataRe, this.get('type bit map')),
84
+ )
85
+ }
58
86
  }
59
87
 
60
88
  const removeParens = (a) => !['(', ')'].includes(a)
package/rr/openpgpkey.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 OPENPGPKEY extends RR {
4
5
  constructor(opts) {
@@ -27,11 +28,14 @@ export default class OPENPGPKEY extends RR {
27
28
  }
28
29
 
29
30
  /****** IMPORTERS *******/
30
- fromBind(obj) {
31
+ fromBind({ bindline: bindline }) {
31
32
  // test.example.com 3600 IN OPENPGPKEY <base64 public key>
32
- const regex = /^([\S]+)\s+(\d{1,10})\s+(IN)\s+(OPENPGPKEY)\s+([\W\w]*)$/
33
- // eslint-disable-next-line no-unused-vars
34
- const [ignore, owner, ttl, c, type, publickey] = obj.bindline.trim().match(regex)
33
+ const regex =
34
+ /^(?<owner>\S+)\s+(?<ttl>\d{1,10})\s+(?<class>IN)\s+(?<type>OPENPGPKEY)\s+(?<publickey>\S[\s\S]*)$/i
35
+ const match = bindline.trim().match(regex)
36
+ if (!match) this.throwHelp(`unable to parse OPENPGPKEY: ${bindline}`)
37
+
38
+ const { owner, ttl, class: c, type, publickey } = match.groups
35
39
 
36
40
  return new OPENPGPKEY({
37
41
  owner,
@@ -42,5 +46,25 @@ export default class OPENPGPKEY extends RR {
42
46
  })
43
47
  }
44
48
 
49
+ fromTinydns({ tinyline }) {
50
+ const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
51
+ return new OPENPGPKEY({
52
+ owner: this.fullyQualify(owner),
53
+ ttl: parseInt(ttl, 10),
54
+ type: 'OPENPGPKEY',
55
+ 'public key': Buffer.from(TINYDNS.unescapeOctal(rd), 'base64').toString('utf-8'),
56
+ timestamp: ts,
57
+ location: loc?.trim() ?? '',
58
+ })
59
+ }
60
+
45
61
  /****** EXPORTERS *******/
62
+ toTinydns() {
63
+ const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
64
+ const escapedPublicKey = TINYDNS.escapeOctal(
65
+ dataRe,
66
+ Buffer.from(this.get('public key'), 'utf-8').toString('base64'),
67
+ )
68
+ return this.getTinydnsGeneric(escapedPublicKey)
69
+ }
46
70
  }
package/rr/ptr.js CHANGED
@@ -31,9 +31,9 @@ export default class PTR extends RR {
31
31
  }
32
32
 
33
33
  /****** IMPORTERS *******/
34
- fromTinydns(opts) {
34
+ fromTinydns({ tinyline }) {
35
35
  // ^fqdn:p:ttl:timestamp:lo
36
- const [fqdn, p, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
36
+ const [fqdn, p, ttl, ts, loc] = tinyline.slice(1).split(':')
37
37
 
38
38
  return new PTR({
39
39
  owner: this.fullyQualify(fqdn),
@@ -41,13 +41,13 @@ export default class PTR extends RR {
41
41
  type: 'PTR',
42
42
  dname: this.fullyQualify(p),
43
43
  timestamp: ts,
44
- location: loc !== '' && loc !== '\n' ? loc : '',
44
+ location: loc?.trim() ?? '',
45
45
  })
46
46
  }
47
47
 
48
- fromBind(opts) {
48
+ fromBind({ bindline }) {
49
49
  // test.example.com 3600 IN PTR dname
50
- const [owner, ttl, c, type, dname] = opts.bindline.split(/\s+/)
50
+ const [owner, ttl, c, type, dname] = bindline.split(/\s+/)
51
51
  return new PTR({
52
52
  owner,
53
53
  ttl: parseInt(ttl, 10),
package/rr/rp.js CHANGED
@@ -52,9 +52,9 @@ export default class RP extends RR {
52
52
  }
53
53
 
54
54
  /****** IMPORTERS *******/
55
- fromBind(opts) {
55
+ fromBind({ bindline }) {
56
56
  // test.example.com 3600 IN RP mbox txt
57
- const [owner, ttl, c, type, mbox, txt] = opts.bindline.split(/\s+/)
57
+ const [owner, ttl, c, type, mbox, txt] = bindline.split(/\s+/)
58
58
  return new RP({
59
59
  owner,
60
60
  ttl: parseInt(ttl, 10),
@@ -65,6 +65,23 @@ export default class RP extends RR {
65
65
  })
66
66
  }
67
67
 
68
+ fromTinydns({ tinyline }) {
69
+ const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
70
+
71
+ const [mbox, consumed] = TINYDNS.unpackDomainName(rdata)
72
+ const txt = TINYDNS.unpackDomainName(rdata.slice(consumed))[0]
73
+
74
+ return new RP({
75
+ owner: this.fullyQualify(owner),
76
+ ttl: parseInt(ttl, 10),
77
+ type: 'RP',
78
+ mbox,
79
+ txt,
80
+ timestamp: ts,
81
+ location: loc?.trim() ?? '',
82
+ })
83
+ }
84
+
68
85
  /****** EXPORTERS *******/
69
86
  toBind(zone_opts) {
70
87
  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) {
@@ -101,16 +102,89 @@ export default class RRSIG extends RR {
101
102
 
102
103
  /****** IMPORTERS *******/
103
104
 
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
- // }
105
+ fromBind({ bindline }) {
106
+ // example.com. 3600 IN RRSIG typecovered algorithm labels origttl sigexp siginc keytag signersname ( signature )
107
+ const parts = bindline.trim().split(/\s+/)
108
+ return new RRSIG({
109
+ owner: parts[0],
110
+ ttl: parseInt(parts[1], 10),
111
+ class: parts[2],
112
+ type: 'RRSIG',
113
+ 'type covered': parseInt(parts[4], 10),
114
+ algorithm: parseInt(parts[5], 10),
115
+ labels: parseInt(parts[6], 10),
116
+ 'original ttl': parseInt(parts[7], 10),
117
+ 'signature expiration': parseInt(parts[8], 10),
118
+ 'signature inception': parseInt(parts[9], 10),
119
+ 'key tag': parseInt(parts[10], 10),
120
+ 'signers name': parts[11],
121
+ signature: parts
122
+ .slice(12)
123
+ .filter((a) => a !== '(' && a !== ')')
124
+ .join(' ')
125
+ .trim(),
126
+ })
127
+ }
128
+
129
+ fromTinydns({ tinyline }) {
130
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
131
+ if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('RRSIG fromTinydns, invalid n')
132
+
133
+ const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
134
+ const typeCovered = bytes.readUInt16BE(0)
135
+ const algorithm = bytes.readUInt8(2)
136
+ const labels = bytes.readUInt8(3)
137
+ const originalTtl = bytes.readUInt32BE(4)
138
+ const signatureExpiration = bytes.readUInt32BE(8)
139
+ const signatureInception = bytes.readUInt32BE(12)
140
+ const keyTag = bytes.readUInt16BE(16)
141
+
142
+ let pos = 18
143
+ const labelArr = []
144
+ while (pos < bytes.length) {
145
+ const len = bytes.readUInt8(pos++)
146
+ if (len === 0) break
147
+ labelArr.push(bytes.slice(pos, pos + len).toString())
148
+ pos += len
149
+ }
150
+ const signersName = `${labelArr.join('.')}.`
151
+ const signature = bytes.slice(pos).toString()
152
+
153
+ return new RRSIG({
154
+ owner: this.fullyQualify(fqdn),
155
+ ttl: parseInt(ttl, 10),
156
+ type: 'RRSIG',
157
+ 'type covered': typeCovered,
158
+ algorithm,
159
+ labels,
160
+ 'original ttl': originalTtl,
161
+ 'signature expiration': signatureExpiration,
162
+ 'signature inception': signatureInception,
163
+ 'key tag': keyTag,
164
+ 'signers name': signersName,
165
+ signature,
166
+ timestamp: ts,
167
+ location: loc?.trim() ?? '',
168
+ })
169
+ }
114
170
 
115
171
  /****** EXPORTERS *******/
172
+ toBind(zone_opts) {
173
+ 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`
174
+ }
175
+
176
+ toTinydns() {
177
+ const dataRe = new RegExp(/[\r\n\t:]/, 'g')
178
+ return this.getTinydnsGeneric(
179
+ TINYDNS.UInt16toOctal(this.get('type covered')) +
180
+ TINYDNS.UInt8toOctal(this.get('algorithm')) +
181
+ TINYDNS.UInt8toOctal(this.get('labels')) +
182
+ TINYDNS.UInt32toOctal(this.get('original ttl')) +
183
+ TINYDNS.UInt32toOctal(this.get('signature expiration')) +
184
+ TINYDNS.UInt32toOctal(this.get('signature inception')) +
185
+ TINYDNS.UInt16toOctal(this.get('key tag')) +
186
+ TINYDNS.packDomainName(this.get('signers name')) +
187
+ TINYDNS.escapeOctal(dataRe, this.get('signature')),
188
+ )
189
+ }
116
190
  }
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
  }
@@ -90,16 +91,101 @@ export default class SIG extends RR {
90
91
  }
91
92
 
92
93
  /****** 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
- // }
94
+
95
+ fromBind({ bindline }) {
96
+ // example.com. 3600 IN SIG TypeCovered Algorithm Labels OrigTTL SigExpiration SigInception KeyTag SignersName ( Signature )
97
+ const parts = bindline.trim().split(/\s+/)
98
+
99
+ return new SIG({
100
+ owner: parts[0],
101
+ ttl: parseInt(parts[1], 10),
102
+ class: parts[2],
103
+ type: 'SIG',
104
+ 'type covered': parseInt(parts[4], 10),
105
+ algorithm: parseInt(parts[5], 10),
106
+ labels: parseInt(parts[6], 10),
107
+ 'original ttl': parseInt(parts[7], 10),
108
+ 'signature expiration': parseInt(parts[8], 10),
109
+ 'signature inception': parseInt(parts[9], 10),
110
+ 'key tag': parseInt(parts[10], 10),
111
+ 'signers name': parts[11],
112
+ signature: parts
113
+ .slice(12)
114
+ .filter((a) => a !== '(' && a !== ')')
115
+ .join(' ')
116
+ .trim(),
117
+ })
118
+ }
103
119
 
104
120
  /****** EXPORTERS *******/
121
+ toTinydns() {
122
+ const dataRe = new RegExp(/[\r\n\t:]/, 'g')
123
+
124
+ return this.getTinydnsGeneric(
125
+ TINYDNS.UInt16toOctal(this.get('type covered')) +
126
+ TINYDNS.UInt8toOctal(this.get('algorithm')) +
127
+ TINYDNS.UInt8toOctal(this.get('labels')) +
128
+ TINYDNS.UInt32toOctal(this.get('original ttl')) +
129
+ TINYDNS.UInt32toOctal(this.get('signature expiration')) +
130
+ TINYDNS.UInt32toOctal(this.get('signature inception')) +
131
+ TINYDNS.UInt16toOctal(this.get('key tag')) +
132
+ TINYDNS.packDomainName(this.get('signers name')) +
133
+ TINYDNS.escapeOctal(dataRe, this.get('signature')),
134
+ )
135
+ }
136
+
137
+ fromTinydns({ tinyline }) {
138
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.substring(1).split(':')
139
+ if (parseInt(n, 10) !== this.getTypeId()) this.throwHelp('SIG fromTinydns, invalid n')
140
+
141
+ const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
142
+
143
+ const typeCovered = bytes.readUInt16BE(0)
144
+ const algorithm = bytes.readUInt8(2)
145
+ const labels = bytes.readUInt8(3)
146
+ const originalTtl = bytes.readUInt32BE(4)
147
+ const signatureExpiration = bytes.readUInt32BE(8)
148
+ const signatureInception = bytes.readUInt32BE(12)
149
+ const keyTag = bytes.readUInt16BE(16)
150
+
151
+ // parse signers name from binary buffer starting at offset 18
152
+ let pos = 18
153
+ const labelsArr = []
154
+ while (pos < bytes.length) {
155
+ const len = bytes.readUInt8(pos++)
156
+ if (len === 0) break
157
+ labelsArr.push(bytes.slice(pos, pos + len).toString())
158
+ pos += len
159
+ }
160
+ const signersName = `${labelsArr.join('.')}.`
161
+
162
+ const signature = bytes.slice(pos).toString()
163
+
164
+ return new SIG({
165
+ owner: this.fullyQualify(fqdn),
166
+ ttl: parseInt(ttl, 10),
167
+ type: 'SIG',
168
+ 'type covered': typeCovered,
169
+ algorithm,
170
+ labels,
171
+ 'original ttl': originalTtl,
172
+ 'signature expiration': signatureExpiration,
173
+ 'signature inception': signatureInception,
174
+ 'key tag': keyTag,
175
+ 'signers name': signersName,
176
+ signature,
177
+ timestamp: ts,
178
+ location: loc?.trim() ?? '',
179
+ })
180
+ }
181
+
182
+ toBind(zone_opts) {
183
+ return `${this.getFQDN('owner', zone_opts)} ${this.get('ttl')} ${this.get('class')} SIG${this.getRdataFields()
184
+ .slice(0, 4)
185
+ .map((f) => ' ' + this.get(f))
186
+ .join('')} ${this.getRdataFields()
187
+ .slice(4, 8)
188
+ .map((f) => this.get(f))
189
+ .join(' ')} ( ${this.get('signature')} )`
190
+ }
105
191
  }