@nictool/dns-resource-record 1.4.0 → 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/hinfo.js CHANGED
@@ -39,26 +39,29 @@ export default class HINFO extends RR {
39
39
  }
40
40
 
41
41
  /****** IMPORTERS *******/
42
- fromBind(opts) {
42
+ fromBind({ bindline }) {
43
43
  // test.example.com 3600 IN HINFO DEC-2060 TOPS20
44
- const regex = /^([\S]+)\s+([0-9]{1,10})\s+(IN)\s+(HINFO)\s+("[^"]+"|[\S]+)\s+("[^"]+"|[\S]+)/i
45
- const match = opts.bindline.trim().match(regex)
46
- if (!match) this.throwHelp(`unable to parse HINFO: ${opts.bindline}`)
47
- const [owner, ttl, c, type, cpu, os] = match.slice(1)
44
+ const regex =
45
+ /^(?<owner>\S+)\s+(?<ttl>\d{1,10})\s+(?<class>IN)\s+(?<type>HINFO)\s+(?:"(?<qCPU>[^"]*)"|(?<uCPU>\S+))\s+(?:"(?<qOS>[^"]*)"|(?<uOS>\S+))$/i
46
+
47
+ const match = bindline.trim().match(regex)
48
+ if (!match) this.throwHelp(`unable to parse HINFO: ${bindline}`)
49
+
50
+ const { owner, ttl, class: c, type, qCPU, uCPU, qOS, uOS } = match.groups
48
51
 
49
52
  return new HINFO({
50
53
  owner,
51
54
  ttl: parseInt(ttl, 10),
52
55
  class: c,
53
56
  type,
54
- cpu,
55
- os,
57
+ cpu: qCPU ?? uCPU,
58
+ os: qOS ?? uOS,
56
59
  })
57
60
  }
58
61
 
59
- fromTinydns(opts) {
62
+ fromTinydns({ tinyline }) {
60
63
  // HINFO via generic, :fqdn:n:rdata:ttl:timestamp:lo
61
- const [fqdn, , rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
64
+ const [fqdn, , rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
62
65
  const [cpu, os] = [...TINYDNS.unpackString(rdata)]
63
66
 
64
67
  return new this.constructor({
@@ -68,7 +71,7 @@ export default class HINFO extends RR {
68
71
  cpu,
69
72
  os,
70
73
  timestamp: ts,
71
- location: loc !== '' && loc !== '\n' ? loc : '',
74
+ location: loc?.trim() ?? '',
72
75
  })
73
76
  }
74
77
 
package/rr/hip.js CHANGED
@@ -59,9 +59,50 @@ export default class HIP extends RR {
59
59
  }
60
60
 
61
61
  /****** IMPORTERS *******/
62
- fromBind(opts) {
62
+ fromTinydns({ tinyline }) {
63
+ // HIP via generic, :fqdn:55:rdata:ttl:timestamp:lo
64
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
65
+ if (n != 55) this.throwHelp('HIP fromTinydns, invalid n')
66
+
67
+ const bytes = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
68
+ const hitLen = bytes.readUInt8(0)
69
+ const pkAlgorithm = bytes.readUInt8(1)
70
+ const pkLen = bytes.readUInt16BE(2)
71
+
72
+ const hit = bytes
73
+ .slice(4, 4 + hitLen)
74
+ .toString('hex')
75
+ .toUpperCase()
76
+ const publicKey = bytes.slice(4 + hitLen, 4 + hitLen + pkLen).toString('base64')
77
+
78
+ const rvsNames = []
79
+ let pos = 4 + hitLen + pkLen
80
+ while (pos < bytes.length) {
81
+ const [name, newPos] = TINYDNS.unpackDomainName(
82
+ [...bytes.slice(pos)]
83
+ .map((b) => (b < 32 || b > 126 ? TINYDNS.UInt8toOctal(b) : String.fromCharCode(b)))
84
+ .join(''),
85
+ )
86
+ pos += newPos
87
+ if (name !== '.') rvsNames.push(name)
88
+ }
89
+
90
+ return new HIP({
91
+ owner: this.fullyQualify(fqdn),
92
+ ttl: parseInt(ttl, 10),
93
+ type: 'HIP',
94
+ 'pk algorithm': pkAlgorithm,
95
+ hit,
96
+ 'public key': publicKey,
97
+ 'rendezvous servers': rvsNames.join(' '),
98
+ timestamp: ts,
99
+ location: loc?.trim() ?? '',
100
+ })
101
+ }
102
+
103
+ fromBind({ bindline }) {
63
104
  // owner ttl IN HIP pk-algorithm HIT public-key [rendezvous-server...]
64
- const parts = opts.bindline.split(/\s+/)
105
+ const parts = bindline.split(/\s+/)
65
106
  const [owner, ttl, c, type, pkAlgorithm, hit, publicKey] = parts
66
107
  return new HIP({
67
108
  owner,
package/rr/https.js CHANGED
@@ -46,9 +46,9 @@ export default class HTTPS extends RR {
46
46
 
47
47
  /****** IMPORTERS *******/
48
48
 
49
- fromBind(opts) {
49
+ fromBind({ bindline }) {
50
50
  // test.example.com 3600 IN HTTPS Priority TargetName Params
51
- const [owner, ttl, c, type, pri, fqdn] = opts.bindline.split(/\s+/)
51
+ const [owner, ttl, c, type, pri, fqdn] = bindline.split(/\s+/)
52
52
  return new HTTPS({
53
53
  owner,
54
54
  ttl: parseInt(ttl, 10),
@@ -56,20 +56,31 @@ export default class HTTPS extends RR {
56
56
  type,
57
57
  priority: parseInt(pri, 10),
58
58
  'target name': fqdn,
59
- params: opts.bindline.split(/\s+/).slice(6).join(' ').trim(),
59
+ params: bindline.split(/\s+/).slice(6).join(' ').trim(),
60
60
  })
61
61
  }
62
62
 
63
- fromTinydns({ rd, owner, ttl }) {
63
+ fromTinydns({ tinyline }) {
64
+ const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
65
+
64
66
  if (rd.length < 6) {
65
67
  this.throwHelp(`HTTPS: RDATA too short: ${rd}`)
66
68
  }
67
69
 
68
- const priority = TINYDNS.octalToUInt16(rd.substring(0, 6))
69
- const remaining = rd.slice(6)
70
-
71
- const [targetName, consumed] = TINYDNS.unpackDomainName(remaining)
72
- const params = TINYDNS.escapeOctal(remaining.slice(consumed))
70
+ const binary = Buffer.from(TINYDNS.octalToChar(rd), 'binary')
71
+ const priority = binary.readUInt16BE(0)
72
+
73
+ let pos = 2
74
+ const labels = []
75
+ while (true) {
76
+ const len = binary.readUInt8(pos)
77
+ pos += 1
78
+ if (len === 0) break
79
+ labels.push(binary.slice(pos, pos + len).toString())
80
+ pos += len
81
+ }
82
+ const targetName = `${labels.join('.')}.`
83
+ const params = binary.slice(pos).toString()
73
84
 
74
85
  return new HTTPS({
75
86
  owner: this.fullyQualify(owner),
@@ -78,6 +89,8 @@ export default class HTTPS extends RR {
78
89
  priority,
79
90
  'target name': targetName,
80
91
  params,
92
+ timestamp: ts,
93
+ location: loc?.trim() ?? '',
81
94
  })
82
95
  }
83
96
 
package/rr/ipseckey.js CHANGED
@@ -98,9 +98,9 @@ export default class IPSECKEY extends RR {
98
98
  }
99
99
 
100
100
  /****** IMPORTERS *******/
101
- fromBind(opts) {
101
+ fromBind({ bindline }) {
102
102
  // FQDN TTL CLASS IPSECKEY Precedence GatewayType Algorithm Gateway PublicKey
103
- const [owner, ttl, c, type, prec, gwt, algo, gateway, publickey] = opts.bindline.split(/\s+/)
103
+ const [owner, ttl, c, type, prec, gwt, algo, gateway, publickey] = bindline.split(/\s+/)
104
104
  return new IPSECKEY({
105
105
  owner,
106
106
  ttl: parseInt(ttl, 10),
@@ -114,32 +114,32 @@ export default class IPSECKEY extends RR {
114
114
  })
115
115
  }
116
116
 
117
- fromTinydns(opts) {
118
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
117
+ fromTinydns({ tinyline }) {
118
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
119
119
  if (n != 45) this.throwHelp('IPSECKEY fromTinydns, invalid n')
120
120
 
121
- const precedence = TINYDNS.octalToUInt8(rdata.substring(0, 4))
122
- const gwType = TINYDNS.octalToUInt8(rdata.substring(4, 8))
123
- const algorithm = TINYDNS.octalToUInt8(rdata.substring(8, 12))
121
+ const precedence = TINYDNS.octalToUInt8(rdata.slice(0, 4))
122
+ const gwType = TINYDNS.octalToUInt8(rdata.slice(4, 8))
123
+ const algorithm = TINYDNS.octalToUInt8(rdata.slice(8, 12))
124
124
 
125
125
  let len, gateway, octalKey
126
126
 
127
127
  switch (gwType) {
128
128
  case 0: // no gateway
129
- gateway = rdata.substring(12, 13) // should always be: '.'
130
- octalKey = rdata.substring(13)
129
+ gateway = rdata.slice(12, 13) // should always be: '.'
130
+ octalKey = rdata.slice(13)
131
131
  break
132
132
  case 1: // 4-byte IPv4 address
133
- gateway = TINYDNS.octalToIPv4(rdata.substring(12, 28))
134
- octalKey = rdata.substring(28)
133
+ gateway = TINYDNS.octalToIPv4(rdata.slice(12, 28))
134
+ octalKey = rdata.slice(28)
135
135
  break
136
136
  case 2: // 16-byte IPv6
137
- gateway = TINYDNS.octalToHex(rdata.substring(12, 76))
138
- octalKey = rdata.substring(76)
137
+ gateway = TINYDNS.octalToHex(rdata.slice(12, 76))
138
+ octalKey = rdata.slice(76)
139
139
  break
140
140
  case 3: // wire encoded domain name
141
- ;[gateway, len] = TINYDNS.unpackDomainName(rdata.substring(12))
142
- octalKey = rdata.substring(12 + len)
141
+ ;[gateway, len] = TINYDNS.unpackDomainName(rdata.slice(12))
142
+ octalKey = rdata.slice(12 + len)
143
143
  break
144
144
  }
145
145
 
@@ -153,7 +153,7 @@ export default class IPSECKEY extends RR {
153
153
  gateway,
154
154
  publickey: TINYDNS.octalToBase64(octalKey),
155
155
  timestamp: ts,
156
- location: loc !== '' && loc !== '\n' ? loc : '',
156
+ location: loc?.trim() ?? '',
157
157
  })
158
158
  }
159
159
 
package/rr/key.js CHANGED
@@ -65,9 +65,9 @@ export default class KEY extends RR {
65
65
 
66
66
  /****** IMPORTERS *******/
67
67
 
68
- fromBind(opts) {
68
+ fromBind({ bindline }) {
69
69
  // test.example.com 3600 IN KEY Flags Protocol Algorithm PublicKey
70
- const [owner, ttl, c, type, flags, protocol, algorithm] = opts.bindline.split(/\s+/)
70
+ const [owner, ttl, c, type, flags, protocol, algorithm] = bindline.split(/\s+/)
71
71
  return new KEY({
72
72
  owner,
73
73
  ttl: parseInt(ttl, 10),
@@ -76,31 +76,27 @@ export default class KEY extends RR {
76
76
  flags: parseInt(flags, 10),
77
77
  protocol: parseInt(protocol, 10),
78
78
  algorithm: parseInt(algorithm, 10),
79
- publickey: opts.bindline.split(/\s+/).slice(7).join(' ').trim(),
79
+ publickey: bindline.split(/\s+/).slice(7).join(' ').trim(),
80
80
  })
81
81
  }
82
82
 
83
- fromTinydns(opts) {
84
- const rd = opts.rd
85
-
83
+ fromTinydns({ tinyline }) {
86
84
  // RDATA format: Flags (6 octal chars) + Protocol (3 octal chars) + Algorithm (3 octal chars) + Public Key (escaped data)
85
+ const [owner, _typeId, rd, ttl, ts, loc] = tinyline.slice(1).split(':')
87
86
  if (rd.length < 12) {
88
87
  this.throwHelp(`KEY: RDATA too short: ${rd}`)
89
88
  }
90
89
 
91
- const flags = TINYDNS.octalToUInt16(rd.substring(0, 6))
92
- const protocol = TINYDNS.octalToUInt8(rd.substring(6, 9))
93
- const algorithm = TINYDNS.octalToUInt8(rd.substring(9, 12))
94
- const publicKeyData = TINYDNS.unescapeOctal(rd.substring(12))
95
-
96
90
  return new KEY({
97
- owner: this.fullyQualify(opts.owner),
98
- ttl: parseInt(opts.ttl, 10),
91
+ owner: this.fullyQualify(owner),
92
+ ttl: parseInt(ttl, 10),
99
93
  type: 'KEY',
100
- flags: flags,
101
- protocol: protocol,
102
- algorithm: algorithm,
103
- publickey: publicKeyData,
94
+ flags: TINYDNS.octalToUInt16(rd.slice(0, 6)),
95
+ protocol: TINYDNS.octalToUInt8(rd.slice(6, 9)),
96
+ algorithm: TINYDNS.octalToUInt8(rd.slice(9, 12)),
97
+ publickey: TINYDNS.unescapeOctal(rd.slice(12)),
98
+ timestamp: ts,
99
+ location: loc?.trim() ?? '',
104
100
  })
105
101
  }
106
102
 
package/rr/kx.js CHANGED
@@ -51,9 +51,25 @@ export default class KX extends RR {
51
51
  }
52
52
 
53
53
  /****** IMPORTERS *******/
54
- fromBind(opts) {
54
+ fromTinydns({ tinyline }) {
55
+ // KX via generic, :fqdn:36:rdata:ttl:timestamp:lo
56
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
57
+ if (n != 36) this.throwHelp('KX fromTinydns, invalid n')
58
+
59
+ return new KX({
60
+ owner: this.fullyQualify(fqdn),
61
+ ttl: parseInt(ttl, 10),
62
+ type: 'KX',
63
+ preference: TINYDNS.octalToUInt16(rdata.slice(0, 8)),
64
+ exchanger: TINYDNS.unpackDomainName(rdata.slice(8))[0],
65
+ timestamp: ts,
66
+ location: loc?.trim() ?? '',
67
+ })
68
+ }
69
+
70
+ fromBind({ bindline }) {
55
71
  // test.example.com 3600 IN KX preference exchanger
56
- const [owner, ttl, c, type, preference, exchanger] = opts.bindline.split(/\s+/)
72
+ const [owner, ttl, c, type, preference, exchanger] = bindline.split(/\s+/)
57
73
  return new KX({
58
74
  owner,
59
75
  ttl: parseInt(ttl, 10),
package/rr/loc.js CHANGED
@@ -85,22 +85,22 @@ export default class LOC extends RR {
85
85
  }
86
86
 
87
87
  /****** IMPORTERS *******/
88
- fromTinydns(opts) {
88
+ fromTinydns({ tinyline }) {
89
89
  // LOC via generic, :fqdn:n:rdata:ttl:timestamp:lo
90
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
90
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
91
91
  if (n != 29) this.throwHelp('LOC fromTinydns, invalid n')
92
92
 
93
93
  // divide by 100 is to convert cm to meters
94
94
  const l = {
95
- version: TINYDNS.octalToUInt8(rdata.substring(0, 4)),
96
- size: this.fromExponent(TINYDNS.octalToUInt8(rdata.substring(4, 8))),
95
+ version: TINYDNS.octalToUInt8(rdata.slice(0, 4)),
96
+ size: this.fromExponent(TINYDNS.octalToUInt8(rdata.slice(4, 8))),
97
97
  precision: {
98
- horizontal: this.fromExponent(TINYDNS.octalToUInt8(rdata.substring(8, 12))),
99
- vertical: this.fromExponent(TINYDNS.octalToUInt8(rdata.substring(12, 16))),
98
+ horizontal: this.fromExponent(TINYDNS.octalToUInt8(rdata.slice(8, 12))),
99
+ vertical: this.fromExponent(TINYDNS.octalToUInt8(rdata.slice(12, 16))),
100
100
  },
101
- latitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.substring(16, 32)), 'lat'),
102
- longitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.substring(32, 48)), 'lon'),
103
- altitude: TINYDNS.octalToUInt32(rdata.substring(48, 64)) - REF.ALTITUDE,
101
+ latitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.slice(16, 32)), 'lat'),
102
+ longitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.slice(32, 48)), 'lon'),
103
+ altitude: TINYDNS.octalToUInt32(rdata.slice(48, 64)) - REF.ALTITUDE,
104
104
  }
105
105
 
106
106
  return new LOC({
@@ -109,24 +109,24 @@ export default class LOC extends RR {
109
109
  address: this.toHuman(l),
110
110
  ttl: parseInt(ttl, 10),
111
111
  timestamp: ts,
112
- location: loc !== '' && loc !== '\n' ? loc : '',
112
+ location: loc?.trim() ?? '',
113
113
  })
114
114
  }
115
115
 
116
- fromBind(opts) {
117
- const [owner, ttl, c, type] = opts.bindline.split(/\s+/)
116
+ fromBind({ bindline }) {
117
+ const [owner, ttl, c, type] = bindline.split(/\s+/)
118
118
 
119
119
  return new LOC({
120
120
  owner,
121
121
  ttl: parseInt(ttl, 10),
122
122
  class: c,
123
123
  type: type,
124
- address: opts.bindline.split(/\s+/).slice(4).join(' ').trim(),
124
+ address: bindline.split(/\s+/).slice(4).join(' ').trim(),
125
125
  })
126
126
  }
127
127
 
128
128
  dmsToArcSec(obj) {
129
- let retval = obj.degrees * CONV.deg + (obj.minutes || 0) * CONV.min + (obj.seconds || 0) * CONV.sec
129
+ let retval = obj.degrees * CONV.deg + (obj.minutes ?? 0) * CONV.min + (obj.seconds ?? 0) * CONV.sec
130
130
  switch (obj.hemisphere.toUpperCase()) {
131
131
  case 'W':
132
132
  case 'S':
package/rr/mx.js CHANGED
@@ -53,10 +53,10 @@ export default class MX extends RR {
53
53
  }
54
54
 
55
55
  /****** IMPORTERS *******/
56
- fromTinydns(opts) {
56
+ fromTinydns({ tinyline }) {
57
57
  // @fqdn:ip:x:dist:ttl:timestamp:lo
58
58
  // eslint-disable-next-line no-unused-vars
59
- const [owner, ip, x, preference, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
59
+ const [owner, ip, x, preference, ttl, ts, loc] = tinyline.slice(1).split(':')
60
60
 
61
61
  return new MX({
62
62
  type: 'MX',
@@ -65,13 +65,13 @@ export default class MX extends RR {
65
65
  preference: parseInt(preference, 10) || 0,
66
66
  ttl: parseInt(ttl, 10),
67
67
  timestamp: ts,
68
- location: loc !== '' && loc !== '\n' ? loc : '',
68
+ location: loc?.trim() ?? '',
69
69
  })
70
70
  }
71
71
 
72
- fromBind(opts) {
72
+ fromBind({ bindline }) {
73
73
  // test.example.com 3600 IN MX preference exchange
74
- const [owner, ttl, c, type, preference, exchange] = opts.bindline.split(/\s+/)
74
+ const [owner, ttl, c, type, preference, exchange] = bindline.split(/\s+/)
75
75
 
76
76
  return new MX({
77
77
  owner,
package/rr/naptr.js CHANGED
@@ -62,9 +62,9 @@ export default class NAPTR extends RR {
62
62
  }
63
63
 
64
64
  /****** IMPORTERS *******/
65
- fromTinydns(opts) {
65
+ fromTinydns({ tinyline }) {
66
66
  // NAPTR via generic, :fqdn:n:rdata:ttl:timestamp:lo
67
- const [fqdn, n, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
67
+ const [fqdn, n, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
68
68
  if (n != 35) this.throwHelp('NAPTR fromTinydns, invalid n')
69
69
 
70
70
  const binRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
@@ -74,7 +74,7 @@ export default class NAPTR extends RR {
74
74
  owner: this.fullyQualify(fqdn),
75
75
  ttl: parseInt(ttl, 10),
76
76
  timestamp: ts,
77
- location: loc !== '' && loc !== '\n' ? loc : '',
77
+ location: loc?.trim() ?? '',
78
78
  order: binRdata.readUInt16BE(0, 2),
79
79
  preference: binRdata.readUInt16BE(2, 4),
80
80
  }
@@ -102,26 +102,30 @@ export default class NAPTR extends RR {
102
102
  return new NAPTR(rec)
103
103
  }
104
104
 
105
- fromBind(opts) {
106
- const str = opts.bindline
107
- // test.example.com 3600 IN NAPTR order, preference, "flags", "service", "regexp", replacement
108
- const [owner, ttl, c, type, order, preference] = str.split(/\s+/)
109
- const [flags, service, regexp] = str.match(/(?:").*?(?:"\s)/g)
110
- const replacement = str.trim().split(/\s+/).pop()
105
+ fromBind({ bindline }) {
106
+ const regex =
107
+ /^(?<owner>\S+)\s+(?<ttl>\d+)\s+(?<class>\S+)\s+(?<type>NAPTR)\s+(?<order>\d+)\s+(?<preference>\d+)\s+["'](?<flags>[^"']*)["']\s+["'](?<service>[^"']*)["']\s+["'](?<regexp>[^"']*)["']\s+(?<replacement>\S+)$/
111
108
 
112
- const bits = {
113
- owner: owner,
109
+ const match = bindline.trim().match(regex)
110
+
111
+ if (!match) {
112
+ throw new Error(`Invalid NAPTR BIND line: ${bindline}`)
113
+ }
114
+
115
+ const { owner, ttl, type, order, preference, flags, service, regexp, replacement } = match.groups
116
+
117
+ return new NAPTR({
118
+ owner: this.fullyQualify(owner),
114
119
  ttl: parseInt(ttl, 10),
115
- class: c,
116
- type: type,
120
+ class: match.groups.class,
121
+ type,
117
122
  order: parseInt(order, 10),
118
123
  preference: parseInt(preference, 10),
119
- flags: flags.trim().replace(/^['"]|['"]$/g, ''),
120
- service: service.trim().replace(/^['"]|['"]$/g, ''),
121
- regexp: regexp.trim().replace(/^['"]|['"]/g, ''),
122
- replacement: replacement,
123
- }
124
- return new NAPTR(bits)
124
+ flags,
125
+ service,
126
+ regexp,
127
+ replacement,
128
+ })
125
129
  }
126
130
 
127
131
  /****** EXPORTERS *******/
package/rr/ns.js CHANGED
@@ -34,10 +34,10 @@ export default class NS extends RR {
34
34
  }
35
35
 
36
36
  /****** IMPORTERS *******/
37
- fromTinydns(opts) {
37
+ fromTinydns({ tinyline }) {
38
38
  // &fqdn:ip:x:ttl:timestamp:lo
39
39
  // eslint-disable-next-line no-unused-vars
40
- const [fqdn, ip, dname, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
40
+ const [fqdn, ip, dname, ttl, ts, loc] = tinyline.slice(1).split(':')
41
41
 
42
42
  return new NS({
43
43
  type: 'NS',
@@ -45,13 +45,13 @@ export default class NS extends RR {
45
45
  dname: this.fullyQualify(/\./.test(dname) ? dname : `${dname}.ns.${fqdn}`),
46
46
  ttl: parseInt(ttl, 10),
47
47
  timestamp: ts,
48
- location: loc !== '' && loc !== '\n' ? loc : '',
48
+ location: loc?.trim() ?? '',
49
49
  })
50
50
  }
51
51
 
52
- fromBind(opts) {
52
+ fromBind({ bindline }) {
53
53
  // test.example.com 3600 IN NS dname
54
- const [owner, ttl, c, type, dname] = opts.bindline.split(/\s+/)
54
+ const [owner, ttl, c, type, dname] = bindline.split(/\s+/)
55
55
 
56
56
  return new NS({
57
57
  owner,
package/rr/nsec.js CHANGED
@@ -43,8 +43,8 @@ export default class NSEC extends RR {
43
43
 
44
44
  /****** IMPORTERS *******/
45
45
 
46
- fromTinydns(opts) {
47
- const [owner, _typeId, rdata, ttl, ts, loc] = opts.tinyline.substring(1).split(':')
46
+ fromTinydns({ tinyline }) {
47
+ const [owner, _typeId, rdata, ttl, ts, loc] = tinyline.slice(1).split(':')
48
48
  const binaryRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
49
49
  const [nextDomain, _escapedLen, binaryLen] = TINYDNS.unpackDomainName(rdata)
50
50
 
@@ -55,20 +55,20 @@ export default class NSEC extends RR {
55
55
  'next domain': nextDomain,
56
56
  'type bit maps': binaryRdata.slice(binaryLen).toString(),
57
57
  timestamp: ts,
58
- location: loc !== '' && loc !== '\n' ? loc : '',
58
+ location: loc?.trim() ?? '',
59
59
  })
60
60
  }
61
61
 
62
- fromBind(opts) {
62
+ fromBind({ bindline }) {
63
63
  // test.example.com 3600 IN NSEC NextDomain TypeBitMaps
64
- const [owner, ttl, c, type, next] = opts.bindline.split(/\s+/)
64
+ const [owner, ttl, c, type, next] = bindline.split(/\s+/)
65
65
  return new NSEC({
66
66
  owner,
67
67
  ttl: parseInt(ttl, 10),
68
68
  class: c,
69
69
  type: type,
70
70
  'next domain': next,
71
- 'type bit maps': opts.bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
71
+ 'type bit maps': bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
72
72
  })
73
73
  }
74
74
 
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,
@@ -97,8 +97,8 @@ export default class NSEC3 extends RR {
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')
@@ -112,7 +112,7 @@ export default class NSEC3 extends RR {
112
112
  // Salt (variable length based on Salt Length)
113
113
  // Next Hashed Owner Name (variable length)
114
114
  // Type Bit Maps (variable length)
115
- const { salt, nextHashedOwnerName, typeBitMaps } = TINYDNS.parseNSEC3Buffer(bytes)
115
+ const { salt, nextHashedOwnerName, typeBitMaps } = parseNSEC3Buffer(bytes)
116
116
 
117
117
  return new NSEC3({
118
118
  owner: this.fullyQualify(fqdn),
@@ -125,7 +125,7 @@ export default class NSEC3 extends RR {
125
125
  'next hashed owner name': nextHashedOwnerName,
126
126
  'type bit maps': typeBitMaps,
127
127
  timestamp: ts,
128
- location: loc !== '' && loc !== '\n' ? loc : '',
128
+ location: loc?.trim() ?? '',
129
129
  })
130
130
  }
131
131
 
@@ -155,3 +155,48 @@ export default class NSEC3 extends RR {
155
155
  )
156
156
  }
157
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
+ }