@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/smimea.js CHANGED
@@ -58,6 +58,10 @@ export default class SMIMEA extends RR {
58
58
  return 'S/MIME cert association'
59
59
  }
60
60
 
61
+ getTags() {
62
+ return ['security']
63
+ }
64
+
61
65
  getRdataFields(arg) {
62
66
  return ['certificate usage', 'selector', 'matching type', 'certificate association data']
63
67
  }
@@ -70,15 +74,28 @@ export default class SMIMEA extends RR {
70
74
  return 53
71
75
  }
72
76
 
77
+ getCanonical() {
78
+ return {
79
+ owner: '_443._tcp.www.example.com.',
80
+ ttl: 3600,
81
+ class: 'IN',
82
+ type: 'SMIMEA',
83
+ 'certificate usage': 0,
84
+ selector: 0,
85
+ 'matching type': 1,
86
+ 'certificate association data': 'ABCDEF...',
87
+ }
88
+ }
89
+
73
90
  getQuotedFields() {
74
91
  return []
75
92
  }
76
93
 
77
94
  /****** IMPORTERS *******/
78
95
 
79
- fromBind(opts) {
96
+ fromBind({ bindline }) {
80
97
  // test.example.com 3600 IN SMIMEA, usage, selector, match, data
81
- const [owner, ttl, c, type, usage, selector, match] = opts.bindline.split(/\s+/)
98
+ const [owner, ttl, c, type, usage, selector, match] = bindline.split(/\s+/)
82
99
  return new SMIMEA({
83
100
  owner,
84
101
  ttl: parseInt(ttl, 10),
@@ -87,12 +104,12 @@ export default class SMIMEA extends RR {
87
104
  'certificate usage': parseInt(usage, 10),
88
105
  selector: parseInt(selector, 10),
89
106
  'matching type': parseInt(match, 10),
90
- 'certificate association data': opts.bindline.split(/\s+/).slice(7).join(' ').trim(),
107
+ 'certificate association data': bindline.split(/\s+/).slice(7).join(' ').trim(),
91
108
  })
92
109
  }
93
110
 
94
- fromTinydns(opts) {
95
- const [owner, _typeId, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
111
+ fromTinydns({ tinyline }) {
112
+ const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
96
113
  const binaryRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
97
114
 
98
115
  return new SMIMEA({
@@ -104,7 +121,7 @@ export default class SMIMEA extends RR {
104
121
  'matching type': binaryRdata.readUInt8(2),
105
122
  'certificate association data': binaryRdata.slice(3).toString(),
106
123
  timestamp: ts,
107
- location: loc !== '' && loc !== '\n' ? loc : '',
124
+ location: loc?.trim() ?? '',
108
125
  })
109
126
  }
110
127
 
package/rr/soa.js CHANGED
@@ -83,11 +83,27 @@ export default class SOA extends RR {
83
83
  return 6
84
84
  }
85
85
 
86
+ getCanonical() {
87
+ return {
88
+ owner: 'example.com.',
89
+ ttl: 3600,
90
+ class: 'IN',
91
+ type: 'SOA',
92
+ mname: 'ns1.example.com.',
93
+ rname: 'admin.example.com.',
94
+ serial: 2023051001,
95
+ refresh: 7200,
96
+ retry: 3600,
97
+ expire: 1209600,
98
+ minimum: 3600,
99
+ }
100
+ }
101
+
86
102
  /****** IMPORTERS *******/
87
- fromBind(opts) {
103
+ fromBind({ bindline }) {
88
104
  // example.com TTL IN SOA mname rname serial refresh retry expire minimum
89
105
  const [owner, ttl, c, type, mname, rname, serial, refresh, retry, expire, minimum] =
90
- opts.bindline.split(/[\s+]/)
106
+ bindline.split(/[\s+]/)
91
107
 
92
108
  return new SOA({
93
109
  owner,
@@ -104,9 +120,9 @@ export default class SOA extends RR {
104
120
  })
105
121
  }
106
122
 
107
- fromTinydns(opts) {
123
+ fromTinydns({ tinyline }) {
108
124
  // Zfqdn:mname:rname:ser:ref:ret:exp:min:ttl:time:lo
109
- const [fqdn, mname, rname, ser, ref, ret, exp, min, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
125
+ const [fqdn, mname, rname, ser, ref, ret, exp, min, ttl, ts, loc] = tinyline.slice(1).split(':')
110
126
 
111
127
  return new SOA({
112
128
  owner: this.fullyQualify(fqdn),
@@ -114,13 +130,13 @@ export default class SOA extends RR {
114
130
  type: 'SOA',
115
131
  mname: this.fullyQualify(mname),
116
132
  rname: this.fullyQualify(rname),
117
- serial: parseInt(ser || opts.default?.serial, 10),
133
+ serial: parseInt(ser ?? this.default?.serial, 10),
118
134
  refresh: parseInt(ref, 10) || 16384,
119
135
  retry: parseInt(ret, 10) || 2048,
120
136
  expire: parseInt(exp, 10) || 1048576,
121
137
  minimum: parseInt(min, 10) || 2560,
122
138
  timestamp: parseInt(ts) || '',
123
- location: loc !== '' && loc !== '\n' ? loc : '',
139
+ location: loc?.trim() ?? '',
124
140
  })
125
141
  }
126
142
 
@@ -136,6 +152,20 @@ export default class SOA extends RR {
136
152
  .join('\t')} ~\n`
137
153
  }
138
154
 
155
+ getWireRdata() {
156
+ const nums = Buffer.alloc(20)
157
+ nums.writeUInt32BE(this.get('serial'), 0)
158
+ nums.writeUInt32BE(this.get('refresh'), 4)
159
+ nums.writeUInt32BE(this.get('retry'), 8)
160
+ nums.writeUInt32BE(this.get('expire'), 12)
161
+ nums.writeUInt32BE(this.get('minimum'), 16)
162
+ return Buffer.concat([
163
+ this.wirePackDomain(this.get('mname')),
164
+ this.wirePackDomain(this.get('rname')),
165
+ nums,
166
+ ])
167
+ }
168
+
139
169
  toTinydns() {
140
170
  return `Z${this.getTinyFQDN('owner')}:${this.getTinyFQDN('mname')}:${this.getTinyFQDN('rname')}:${this.getEmpty('serial')}:${this.getEmpty('refresh')}:${this.getEmpty('retry')}:${this.getEmpty('expire')}:${this.getEmpty('minimum')}:${this.getTinydnsPostamble()}\n`
141
171
  }
package/rr/spf.js CHANGED
@@ -18,6 +18,10 @@ export default class SPF extends TXT {
18
18
  return 'Sender Policy Framework'
19
19
  }
20
20
 
21
+ getTags() {
22
+ return ['deprecated']
23
+ }
24
+
21
25
  getRdataFields(arg) {
22
26
  return ['data']
23
27
  }
@@ -30,10 +34,20 @@ export default class SPF extends TXT {
30
34
  return 99
31
35
  }
32
36
 
37
+ getCanonical() {
38
+ return {
39
+ owner: 'example.com.',
40
+ ttl: 3600,
41
+ class: 'IN',
42
+ type: 'SPF',
43
+ data: 'v=spf1 mx -all',
44
+ }
45
+ }
46
+
33
47
  /****** IMPORTERS *******/
34
- fromTinydns(opts) {
48
+ fromTinydns({ tinyline }) {
35
49
  // SPF via generic, :fqdn:n:rdata:ttl:timestamp:lo
36
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
50
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
37
51
  if (n != 99) this.throwHelp('SPF fromTinydns, invalid n')
38
52
 
39
53
  return new SPF({
@@ -42,7 +56,7 @@ export default class SPF extends TXT {
42
56
  data: TINYDNS.octalToChar(rdata),
43
57
  ttl: parseInt(ttl, 10),
44
58
  timestamp: ts,
45
- location: loc !== '' && loc !== '\n' ? loc : '',
59
+ location: loc?.trim() ?? '',
46
60
  })
47
61
  }
48
62
 
package/rr/srv.js CHANGED
@@ -41,6 +41,10 @@ export default class SRV extends RR {
41
41
  return 'Service'
42
42
  }
43
43
 
44
+ getTags() {
45
+ return ['common']
46
+ }
47
+
44
48
  getRdataFields(arg) {
45
49
  return ['priority', 'weight', 'port', 'target']
46
50
  }
@@ -53,23 +57,36 @@ export default class SRV extends RR {
53
57
  return 33
54
58
  }
55
59
 
60
+ getCanonical() {
61
+ return {
62
+ owner: '_imaps._tcp.example.com.',
63
+ ttl: 3600,
64
+ class: 'IN',
65
+ type: 'SRV',
66
+ priority: 10,
67
+ weight: 10,
68
+ port: 993,
69
+ target: 'mail.example.com.',
70
+ }
71
+ }
72
+
56
73
  /****** IMPORTERS *******/
57
- fromTinydns(opts) {
58
- const str = opts.tinyline
74
+ fromTinydns({ tinyline }) {
75
+ const str = tinyline
59
76
  let fqdn, addr, port, pri, weight, ttl, ts, loc, n, rdata
60
77
 
61
78
  if (str[0] === 'S') {
62
79
  // patched tinydns with S records
63
- ;[fqdn, addr, port, pri, weight, ttl, ts, loc] = str.substring(1).split(':')
80
+ ;[fqdn, addr, port, pri, weight, ttl, ts, loc] = str.slice(1).split(':')
64
81
  } else {
65
82
  // tinydns generic record format
66
- ;[fqdn, n, rdata, ttl, ts, loc] = str.substring(1).split(':')
83
+ ;[fqdn, n, rdata, ttl, ts, loc] = str.slice(1).split(':')
67
84
  if (n != 33) this.throwHelp('SRV fromTinydns: invalid n')
68
85
 
69
- pri = TINYDNS.octalToUInt16(rdata.substring(0, 8))
70
- weight = TINYDNS.octalToUInt16(rdata.substring(8, 16))
71
- port = TINYDNS.octalToUInt16(rdata.substring(16, 24))
72
- addr = TINYDNS.unpackDomainName(rdata.substring(24))[0]
86
+ pri = TINYDNS.octalToUInt16(rdata.slice(0, 8))
87
+ weight = TINYDNS.octalToUInt16(rdata.slice(8, 16))
88
+ port = TINYDNS.octalToUInt16(rdata.slice(16, 24))
89
+ addr = TINYDNS.unpackDomainName(rdata.slice(24))[0]
73
90
  }
74
91
 
75
92
  return new SRV({
@@ -81,13 +98,13 @@ export default class SRV extends RR {
81
98
  port: parseInt(port, 10),
82
99
  target: this.fullyQualify(addr),
83
100
  timestamp: ts,
84
- location: loc !== '' && loc !== '\n' ? loc : '',
101
+ location: loc?.trim() ?? '',
85
102
  })
86
103
  }
87
104
 
88
- fromBind(opts) {
105
+ fromBind({ bindline }) {
89
106
  // test.example.com 3600 IN SRV Priority Weight Port Target
90
- const [owner, ttl, c, type, pri, weight, port, target] = opts.bindline.split(/\s+/)
107
+ const [owner, ttl, c, type, pri, weight, port, target] = bindline.split(/\s+/)
91
108
  return new SRV({
92
109
  owner: owner,
93
110
  ttl: parseInt(ttl, 10),
package/rr/sshfp.js CHANGED
@@ -46,6 +46,10 @@ export default class SSHFP extends RR {
46
46
  return 'Secure Shell Key Fingerprints'
47
47
  }
48
48
 
49
+ getTags() {
50
+ return ['security']
51
+ }
52
+
49
53
  getRdataFields() {
50
54
  return ['algorithm', 'fptype', 'fingerprint']
51
55
  }
@@ -58,27 +62,39 @@ export default class SSHFP extends RR {
58
62
  return 44
59
63
  }
60
64
 
65
+ getCanonical() {
66
+ return {
67
+ owner: 'mail.example.com.',
68
+ ttl: 3600,
69
+ class: 'IN',
70
+ type: 'SSHFP',
71
+ algorithm: 2,
72
+ fptype: 1,
73
+ fingerprint: '123456789abcdef6789abcdf6789abdf6789abcd',
74
+ }
75
+ }
76
+
61
77
  /****** IMPORTERS *******/
62
- fromTinydns(opts) {
78
+ fromTinydns({ tinyline }) {
63
79
  // SSHFP via generic, :fqdn:n:rdata:ttl:timestamp:lo
64
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
80
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
65
81
  if (n != 44) this.throwHelp('SSHFP fromTinydns, invalid n')
66
82
 
67
83
  return new SSHFP({
68
84
  owner: this.fullyQualify(fqdn),
69
85
  ttl: parseInt(ttl, 10),
70
86
  type: 'SSHFP',
71
- algorithm: TINYDNS.octalToUInt8(rdata.substring(0, 4)),
72
- fptype: TINYDNS.octalToUInt8(rdata.substring(4, 8)),
73
- fingerprint: TINYDNS.octalToHex(rdata.substring(8)),
87
+ algorithm: TINYDNS.octalToUInt8(rdata.slice(0, 4)),
88
+ fptype: TINYDNS.octalToUInt8(rdata.slice(4, 8)),
89
+ fingerprint: TINYDNS.octalToHex(rdata.slice(8)),
74
90
  timestamp: ts,
75
- location: loc !== '' && loc !== '\n' ? loc : '',
91
+ location: loc?.trim() ?? '',
76
92
  })
77
93
  }
78
94
 
79
- fromBind(opts) {
95
+ fromBind({ bindline }) {
80
96
  // test.example.com 3600 IN SSHFP algo fptype fp
81
- const [owner, ttl, c, type, algo, fptype, fp] = opts.bindline.split(/\s+/)
97
+ const [owner, ttl, c, type, algo, fptype, fp] = bindline.split(/\s+/)
82
98
  return new SSHFP({
83
99
  owner,
84
100
  ttl: parseInt(ttl, 10),
package/rr/svcb.js CHANGED
@@ -44,13 +44,25 @@ export default class SVCB extends RR {
44
44
  return 64
45
45
  }
46
46
 
47
+ getCanonical() {
48
+ return {
49
+ owner: '_8443._foo.api.example.com.',
50
+ ttl: 3600,
51
+ class: 'IN',
52
+ type: 'SVCB',
53
+ priority: 1,
54
+ 'target name': 'svc4.example.net.',
55
+ params: 'alpn="h2,h3"',
56
+ }
57
+ }
58
+
47
59
  /****** IMPORTERS *******/
48
60
 
49
- fromBind(opts) {
61
+ fromBind({ bindline }) {
50
62
  // test.example.com 3600 IN SVCB Priority TargetName Params
51
63
  // _8443._foo.api.example.com. 7200 IN SVCB 0 svc4.example.net.
52
64
  // svc4.example.net. 7200 IN SVCB 3 svc4.example.net. ( alpn="bar" port="8004" )
53
- const [owner, ttl, c, type, pri, fqdn] = opts.bindline.split(/\s+/)
65
+ const [owner, ttl, c, type, pri, fqdn] = bindline.split(/\s+/)
54
66
  return new SVCB({
55
67
  owner,
56
68
  ttl: parseInt(ttl, 10),
@@ -58,22 +70,35 @@ export default class SVCB extends RR {
58
70
  type,
59
71
  priority: parseInt(pri, 10),
60
72
  'target name': fqdn,
61
- params: opts.bindline.split(/\s+/).slice(6).join(' ').trim(),
73
+ params: bindline.split(/\s+/).slice(6).join(' ').trim(),
62
74
  })
63
75
  }
64
76
 
65
- fromTinydns({ rd, owner, ttl }) {
77
+ fromTinydns({ tinyline }) {
78
+ const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
79
+
66
80
  if (rd.length < 6) {
67
81
  this.throwHelp(`SVCB: RDATA too short: ${rd}`)
68
82
  }
69
83
 
70
- const priority = TINYDNS.octalToUInt16(rd.substring(0, 6))
71
- const remainingRdata = rd.slice(6)
84
+ // Convert escaped octal RDATA into a binary buffer for reliable parsing
85
+ const binary = Buffer.from(TINYDNS.octalToChar(rd), 'binary')
72
86
 
73
- const [targetName, consumedLength] = TINYDNS.unpackDomainName(remainingRdata)
87
+ const priority = binary.readUInt16BE(0)
74
88
 
75
- const params = remainingRdata.slice(consumedLength)
76
- const unescapedParams = TINYDNS.unescapeOctal(params)
89
+ // parse domain name from binary starting at offset 2
90
+ let pos = 2
91
+ const labels = []
92
+ while (true) {
93
+ const len = binary.readUInt8(pos)
94
+ pos += 1
95
+ if (len === 0) break
96
+ labels.push(binary.slice(pos, pos + len).toString())
97
+ pos += len
98
+ }
99
+ const targetName = `${labels.join('.')}.`
100
+ // remaining params are ASCII text after the domain
101
+ const params = binary.slice(pos).toString()
77
102
 
78
103
  return new SVCB({
79
104
  owner: this.fullyQualify(owner),
@@ -81,7 +106,9 @@ export default class SVCB extends RR {
81
106
  type: 'SVCB',
82
107
  priority: priority,
83
108
  'target name': targetName,
84
- params: unescapedParams,
109
+ params: params,
110
+ timestamp: ts,
111
+ location: loc?.trim() ?? '',
85
112
  })
86
113
  }
87
114
 
package/rr/tlsa.js CHANGED
@@ -58,30 +58,47 @@ export default class TLSA extends RR {
58
58
  return 'TLSA certificate association'
59
59
  }
60
60
 
61
+ getTags() {
62
+ return ['security']
63
+ }
64
+
61
65
  getRdataFields(arg) {
62
66
  return ['certificate usage', 'selector', 'matching type', 'certificate association data']
63
67
  }
64
68
 
65
69
  getRFCs() {
66
- return [6698]
70
+ return [6698, 7671]
67
71
  }
68
72
 
69
73
  getTypeId() {
70
74
  return 52
71
75
  }
72
76
 
77
+ getCanonical() {
78
+ return {
79
+ owner: '_443._tcp.www.example.com.',
80
+ ttl: 3600,
81
+ class: 'IN',
82
+ type: 'TLSA',
83
+ 'certificate usage': 3,
84
+ selector: 1,
85
+ 'matching type': 1,
86
+ 'certificate association data': 'ABCDEF...',
87
+ }
88
+ }
89
+
73
90
  getQuotedFields() {
74
91
  return []
75
92
  }
76
93
 
77
94
  /****** IMPORTERS *******/
78
95
 
79
- fromBind(opts) {
96
+ fromBind({ bindline }) {
80
97
  // test.example.com 3600 IN TLSA, usage, selector, match, data
81
98
  const regex =
82
- /^(?<owner>\S+)\s+(?<ttl>\d{1,10})\s+(?<cls>IN)\s+(?<type>TLSA)\s+(?<usage>\d+)\s+(?<selector>\d+)\s+(?<matchtype>\d+)\s+(?<cad>.+)$/i
83
- const match = opts.bindline.trim().match(regex)
84
- if (!match) this.throwHelp(`unable to parse TLSA: ${opts.bindline}`)
99
+ /^(?<owner>\S+)\s+(?<ttl>\d{1,10})\s+(?<cls>IN)\s+(?<type>TLSA)\s+(?<usage>\d+)\s+(?<selector>\d+)\s+(?<matchtype>\d+)\s+(?<cad>\S.*)$/i
100
+ const match = bindline.trim().match(regex)
101
+ if (!match) this.throwHelp(`unable to parse TLSA: ${bindline}`)
85
102
  const { owner, ttl, cls, type, usage, selector, matchtype, cad } = match.groups
86
103
 
87
104
  return new TLSA({
@@ -92,12 +109,12 @@ export default class TLSA extends RR {
92
109
  'certificate usage': parseInt(usage, 10),
93
110
  selector: parseInt(selector, 10),
94
111
  'matching type': parseInt(matchtype, 10),
95
- 'certificate association data': cad,
112
+ 'certificate association data': cad.trim(),
96
113
  })
97
114
  }
98
115
 
99
- fromTinydns(opts) {
100
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
116
+ fromTinydns({ tinyline }) {
117
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
101
118
  if (n != 52) this.throwHelp('TLSA fromTinydns, invalid n')
102
119
 
103
120
  const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
@@ -111,7 +128,7 @@ export default class TLSA extends RR {
111
128
  'matching type': bytes.readUInt8(2),
112
129
  'certificate association data': bytes.slice(3).toString(),
113
130
  timestamp: ts,
114
- location: loc.trim() || '',
131
+ location: loc?.trim() ?? '',
115
132
  })
116
133
  }
117
134
 
package/rr/tsig.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 TSIG extends RR {
4
5
  constructor(opts) {
@@ -17,32 +18,173 @@ export default class TSIG extends RR {
17
18
  }
18
19
 
19
20
  getRFCs() {
20
- return [2845]
21
+ return [2845, 8945]
21
22
  }
22
23
 
23
24
  getTypeId() {
24
25
  return 250
25
26
  }
26
27
 
28
+ getCanonical() {
29
+ return {
30
+ owner: 'test.example.',
31
+ ttl: 0,
32
+ class: 'ANY',
33
+ type: 'TSIG',
34
+ 'algorithm name': 'hmac-sha256.',
35
+ 'time signed': 1620650000,
36
+ fudge: 300,
37
+ mac: 'ABCDEF...',
38
+ 'original id': 12345,
39
+ error: 0,
40
+ other: '',
41
+ }
42
+ }
43
+
44
+ setClass(t) {
45
+ if (t !== 'ANY') this.throwHelp('TSIG: Class is required to be ANY')
46
+ this.set('class', t)
47
+ }
48
+
49
+ setTtl(t) {
50
+ if (t !== 0) this.throwHelp('TSIG: TTL is required to be 0')
51
+ this.set('ttl', t)
52
+ }
53
+
54
+ setAlgorithmName(val) {
55
+ if (!val) this.throwHelp(`TSIG: 'algorithm name' is required`)
56
+ this.set('algorithm name', val)
57
+ }
58
+
59
+ setTimeSigned(val) {
60
+ // a 48-bit unsigned integer, as seconds since the UNIX epoch
61
+ if (val === undefined) this.throwHelp(`TSIG: 'time signed' is required`)
62
+ this.set('time signed', val)
63
+ }
64
+
65
+ setFudge(val) {
66
+ // 16-bit unsigned
67
+ this.is16bitInt('TSIG', 'fudge', val)
68
+ this.set('fudge', val)
69
+ }
70
+
71
+ setMac(val) {
72
+ this.set('mac', val ?? '')
73
+ }
74
+
75
+ setOriginalId(val) {
76
+ this.is16bitInt('TSIG', 'original id', val)
77
+ this.set('original id', val)
78
+ }
79
+
80
+ setError(val) {
81
+ this.is16bitInt('TSIG', 'error', val)
82
+ this.set('error', val)
83
+ }
84
+
85
+ setOther(val) {
86
+ this.set('other', val ?? '')
87
+ }
88
+
27
89
  /****** IMPORTERS *******/
28
90
 
29
- fromBind(opts) {
30
- // test.example.com 3600 IN TSIG SAMPLE-ALG.EXAMPLE. 853804800 300 0 0 0
31
- const [owner, ttl, c, type, algorithm] = opts.bindline.split(/\s+/)
91
+ fromBind({ bindline }) {
92
+ // owner ttl ANY TSIG alg time fudge mac_size mac original_id error other_len
93
+ const parts = bindline.trimEnd().split('\t')
94
+ const [owner, ttl, cls, type, alg, time, fudge, , mac, origId, error] = parts
95
+
32
96
  return new TSIG({
33
97
  owner,
34
98
  ttl: parseInt(ttl, 10),
35
- class: c,
36
- type: type,
37
- algorithm: algorithm,
38
- // 'time signed': opts.bindline,
39
- // fudge
40
- // mac
41
- // original id
42
- // error
43
- // other
99
+ class: cls,
100
+ type: type.toUpperCase(),
101
+ 'algorithm name': alg,
102
+ 'time signed': parseInt(time, 10),
103
+ fudge: parseInt(fudge, 10),
104
+ mac: mac || '',
105
+ 'original id': parseInt(origId, 10),
106
+ error: parseInt(error, 10),
107
+ other: '',
108
+ })
109
+ }
110
+
111
+ fromTinydns({ tinyline }) {
112
+ const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
113
+
114
+ const algUnpacked = TINYDNS.unpackDomainName(rdata)
115
+ const algBinaryLen = algUnpacked[2]
116
+
117
+ const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
118
+ let bpos = algBinaryLen
119
+
120
+ const timeSigned = bytes.readUInt32BE(bpos)
121
+ bpos += 4
122
+ const fudge = bytes.readUInt16BE(bpos)
123
+ bpos += 2
124
+ const macSize = bytes.readUInt16BE(bpos)
125
+ bpos += 2
126
+ const mac = macSize > 0 ? bytes.slice(bpos, bpos + macSize).toString('hex') : ''
127
+ bpos += macSize
128
+ const originalId = bytes.readUInt16BE(bpos)
129
+ bpos += 2
130
+ const error = bytes.readUInt16BE(bpos)
131
+ bpos += 2
132
+ const other = bpos < bytes.length ? bytes.slice(bpos).toString() : ''
133
+
134
+ return new TSIG({
135
+ owner: this.fullyQualify(owner),
136
+ ttl: parseInt(ttl, 10),
137
+ class: 'ANY',
138
+ type: 'TSIG',
139
+ 'algorithm name': algUnpacked[0],
140
+ 'time signed': timeSigned,
141
+ fudge,
142
+ mac,
143
+ 'original id': originalId,
144
+ error,
145
+ other,
146
+ timestamp: ts,
147
+ location: loc?.trim() ?? '',
44
148
  })
45
149
  }
46
150
 
47
151
  /****** EXPORTERS *******/
152
+ toBind(zone_opts) {
153
+ const mac = this.get('mac') ?? ''
154
+ const macSize = mac.length > 0 ? mac.length : ''
155
+ const other = this.get('other') ?? ''
156
+ const otherLen = other.length > 0 ? other.length : 0
157
+ return (
158
+ [
159
+ this.getFQDN('owner', zone_opts),
160
+ this.get('ttl'),
161
+ this.get('class'),
162
+ this.get('type'),
163
+ this.get('algorithm name'),
164
+ this.get('time signed'),
165
+ this.get('fudge'),
166
+ macSize,
167
+ mac,
168
+ this.get('original id'),
169
+ this.get('error'),
170
+ otherLen,
171
+ ].join('\t') + '\n'
172
+ )
173
+ }
174
+
175
+ toTinydns() {
176
+ const dataRe = new RegExp(/[\r\n\t:\\/]/, 'g')
177
+ const alg = this.get('algorithm name') || ''
178
+
179
+ return this.getTinydnsGeneric(
180
+ TINYDNS.packDomainName(alg) +
181
+ TINYDNS.UInt32toOctal(this.get('time signed') ?? 0) +
182
+ TINYDNS.UInt16toOctal(this.get('fudge')) +
183
+ TINYDNS.UInt16toOctal(this.get('mac size') ?? 0) +
184
+ (this.get('mac size') > 0 ? TINYDNS.escapeOctal(dataRe, this.get('mac')) : '') +
185
+ TINYDNS.UInt16toOctal(this.get('original id') ?? 0) +
186
+ TINYDNS.UInt16toOctal(this.get('error') ?? 0) +
187
+ (this.get('other').length > 0 ? TINYDNS.escapeOctal(dataRe, this.get('other')) : ''),
188
+ )
189
+ }
48
190
  }