@nictool/dns-resource-record 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codeclimate.yml +25 -0
- package/CHANGELOG.md +253 -0
- package/DEVELOP.md +23 -0
- package/LICENSE +29 -0
- package/README.md +267 -0
- package/index.js +67 -0
- package/lib/tinydns.js +186 -0
- package/package.json +40 -0
- package/rr/a.js +65 -0
- package/rr/aaaa.js +137 -0
- package/rr/caa.js +129 -0
- package/rr/cert.js +75 -0
- package/rr/cname.js +76 -0
- package/rr/dname.js +74 -0
- package/rr/dnskey.js +117 -0
- package/rr/ds.js +104 -0
- package/rr/hinfo.js +84 -0
- package/rr/ipseckey.js +159 -0
- package/rr/key.js +73 -0
- package/rr/loc.js +216 -0
- package/rr/mx.js +86 -0
- package/rr/naptr.js +146 -0
- package/rr/ns.js +74 -0
- package/rr/nsec.js +61 -0
- package/rr/nsec3.js +99 -0
- package/rr/nsec3param.js +84 -0
- package/rr/openpgpkey.js +44 -0
- package/rr/ptr.js +65 -0
- package/rr/rrsig.js +101 -0
- package/rr/sig.js +100 -0
- package/rr/smimea.js +73 -0
- package/rr/soa.js +140 -0
- package/rr/spf.js +54 -0
- package/rr/srv.js +122 -0
- package/rr/sshfp.js +86 -0
- package/rr/tlsa.js +106 -0
- package/rr/txt.js +122 -0
- package/rr/uri.js +95 -0
- package/rr.js +291 -0
- package/test/a.js +76 -0
- package/test/aaaa.js +79 -0
- package/test/base.js +138 -0
- package/test/caa.js +104 -0
- package/test/cert.js +48 -0
- package/test/cname.js +41 -0
- package/test/dname.js +53 -0
- package/test/dnskey.js +44 -0
- package/test/ds.js +44 -0
- package/test/fake.js +26 -0
- package/test/hinfo.js +75 -0
- package/test/ipseckey.js +115 -0
- package/test/key.js +37 -0
- package/test/loc.js +71 -0
- package/test/mx.js +75 -0
- package/test/naptr.js +40 -0
- package/test/ns.js +53 -0
- package/test/nsec.js +38 -0
- package/test/nsec3.js +26 -0
- package/test/nsec3param.js +26 -0
- package/test/openpgpkey.js +35 -0
- package/test/ptr.js +54 -0
- package/test/rr.js +196 -0
- package/test/smimea.js +45 -0
- package/test/soa.js +77 -0
- package/test/spf.js +48 -0
- package/test/srv.js +81 -0
- package/test/sshfp.js +62 -0
- package/test/tinydns.js +140 -0
- package/test/tlsa.js +54 -0
- package/test/txt.js +70 -0
- package/test/uri.js +61 -0
package/rr/loc.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
|
|
2
|
+
import RR from '../rr.js'
|
|
3
|
+
import * as TINYDNS from '../lib/tinydns.js'
|
|
4
|
+
|
|
5
|
+
const REF = { // RFC 1876
|
|
6
|
+
LATLON : 2**31, // LAT equator, LON prime meridian
|
|
7
|
+
ALTITUDE: 100000 * 100, // reference spheroid used by GPS, in cm
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const CONV = {
|
|
11
|
+
sec: 1000,
|
|
12
|
+
min: 60 * 1000,
|
|
13
|
+
deg: 60 * 60 * 1000,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default class LOC extends RR {
|
|
17
|
+
constructor (opts) {
|
|
18
|
+
super(opts)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/****** Resource record specific setters *******/
|
|
22
|
+
setAddress (val) {
|
|
23
|
+
if (!val) throw new Error('LOC: address is required')
|
|
24
|
+
|
|
25
|
+
/*
|
|
26
|
+
... LOC ( d1 [m1 [s1]] {"N"|"S"} d2 [m2 [s2]]
|
|
27
|
+
{"E"|"W"} alt["m"] [siz["m"] [hp["m"]
|
|
28
|
+
[vp["m"]]]] )
|
|
29
|
+
*/
|
|
30
|
+
this.parseLoc(val)
|
|
31
|
+
|
|
32
|
+
this.set('address', val)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getDescription () {
|
|
36
|
+
return 'Location'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getRdataFields (arg) {
|
|
40
|
+
return [ 'address' ]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getRFCs () {
|
|
44
|
+
return [ 1876 ]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getTypeId () {
|
|
48
|
+
return 29
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
parseLoc (string) {
|
|
52
|
+
|
|
53
|
+
// d1 [m1 [s1]]
|
|
54
|
+
const dms = '(\\d+)\\s+(?:(\\d+)\\s+)?(?:([\\d.]+)\\s+)?'
|
|
55
|
+
|
|
56
|
+
// alt["m"] [siz["m"] [hp["m"] [vp["m"]]]]
|
|
57
|
+
const alt = '(-?[\\d.]+)m?(?:\\s+([\\d.]+)m?)?(?:\\s+([\\d.]+)m?)?(?:\\s+([\\d.]+)m?)?'
|
|
58
|
+
|
|
59
|
+
// put them all together
|
|
60
|
+
const locRe = new RegExp(`^${dms}(N|S)\\s+${dms}(E|W)\\s+${alt}`, 'i')
|
|
61
|
+
const r = string.match(locRe)
|
|
62
|
+
if (!r) throw new Error('LOC address: invalid format, see RFC 1876')
|
|
63
|
+
|
|
64
|
+
const loc = {
|
|
65
|
+
latitude: {
|
|
66
|
+
degrees : r[1],
|
|
67
|
+
minutes : r[2],
|
|
68
|
+
seconds : r[3],
|
|
69
|
+
hemisphere: r[4].toUpperCase(),
|
|
70
|
+
},
|
|
71
|
+
longitude: {
|
|
72
|
+
degrees : r[5],
|
|
73
|
+
minutes : r[6],
|
|
74
|
+
seconds : r[7],
|
|
75
|
+
hemisphere: r[8].toUpperCase(),
|
|
76
|
+
},
|
|
77
|
+
altitude : r[9] * 100, // m -> cm
|
|
78
|
+
size : r[10] * 100,
|
|
79
|
+
precision: {
|
|
80
|
+
horizontal: r[11] * 100,
|
|
81
|
+
vertical : r[12] * 100,
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return loc
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/****** IMPORTERS *******/
|
|
89
|
+
fromTinydns (opts) {
|
|
90
|
+
// LOC via generic, :fqdn:n:rdata:ttl:timestamp:lo
|
|
91
|
+
const [ fqdn, n, rdata, ttl, ts, loc ] = opts.tinyline.substring(1).split(':')
|
|
92
|
+
if (n != 29) throw new Error('LOC fromTinydns, invalid n')
|
|
93
|
+
|
|
94
|
+
// divide by 100 is to convert cm to meters
|
|
95
|
+
const l = {
|
|
96
|
+
version : TINYDNS.octalToUInt8(rdata.substring(0, 4)),
|
|
97
|
+
size : this.fromExponent(TINYDNS.octalToUInt8(rdata.substring(4, 8))),
|
|
98
|
+
precision: {
|
|
99
|
+
horizontal: this.fromExponent(TINYDNS.octalToUInt8(rdata.substring( 8, 12))),
|
|
100
|
+
vertical : this.fromExponent(TINYDNS.octalToUInt8(rdata.substring(12, 16))),
|
|
101
|
+
},
|
|
102
|
+
latitude : this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.substring(16, 32)), 'lat'),
|
|
103
|
+
longitude: this.arcSecToDMS(TINYDNS.octalToUInt32(rdata.substring(32, 48)), 'lon'),
|
|
104
|
+
altitude : TINYDNS.octalToUInt32(rdata.substring(48, 64)) - REF.ALTITUDE,
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return new LOC({
|
|
108
|
+
type : 'LOC',
|
|
109
|
+
owner : this.fullyQualify(fqdn),
|
|
110
|
+
address : this.toHuman(l),
|
|
111
|
+
ttl : parseInt(ttl, 10),
|
|
112
|
+
timestamp: ts,
|
|
113
|
+
location : loc !== '' && loc !== '\n' ? loc : '',
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fromBind (opts) {
|
|
118
|
+
const [ owner, ttl, c, type ] = opts.bindline.split(/\s+/)
|
|
119
|
+
|
|
120
|
+
return new LOC({
|
|
121
|
+
owner,
|
|
122
|
+
ttl : parseInt(ttl, 10),
|
|
123
|
+
class : c,
|
|
124
|
+
type : type,
|
|
125
|
+
address: opts.bindline.split(/\s+/).slice(4).join(' ').trim(),
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
dmsToArcSec (obj) {
|
|
130
|
+
let retval = (obj.degrees * CONV.deg) + ((obj.minutes || 0) * CONV.min) + ((obj.seconds || 0) * CONV.sec)
|
|
131
|
+
switch (obj.hemisphere.toUpperCase()) {
|
|
132
|
+
case 'W':
|
|
133
|
+
case 'S':
|
|
134
|
+
retval = -retval
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
retval += REF.LATLON
|
|
138
|
+
return retval
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
arcSecToDMS (rawmsec, latlon) {
|
|
142
|
+
let msec = Math.abs(rawmsec - REF.LATLON)
|
|
143
|
+
// console.log(`rawmsec: ${rawmsec}, abs msec: ${msec}`)
|
|
144
|
+
|
|
145
|
+
const deg = Math.floor(msec / CONV.deg)
|
|
146
|
+
msec -= deg * CONV.deg
|
|
147
|
+
|
|
148
|
+
const min = Math.floor(msec / CONV.min)
|
|
149
|
+
msec -= min * CONV.min
|
|
150
|
+
|
|
151
|
+
const sec = Math.floor(msec / CONV.sec)
|
|
152
|
+
msec -= sec * CONV.sec
|
|
153
|
+
|
|
154
|
+
let hem
|
|
155
|
+
switch (latlon) {
|
|
156
|
+
case 'lat':
|
|
157
|
+
hem = rawmsec >= REF.LATLON ? 'N' : 'S'
|
|
158
|
+
break
|
|
159
|
+
case 'lon':
|
|
160
|
+
hem = rawmsec >= REF.LATLON ? 'E' : 'W'
|
|
161
|
+
break
|
|
162
|
+
default:
|
|
163
|
+
throw new Error('unknown or missing hemisphere')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return `${deg} ${min} ${sec}${msec ? '.'+msec : ''} ${hem}`
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
fromExponent (prec) {
|
|
170
|
+
const mantissa = ((prec >> 4) & 0x0f) % 10
|
|
171
|
+
const exponent = ((prec >> 0) & 0x0f) % 10
|
|
172
|
+
return mantissa * Math.pow(10, exponent)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
toExponent (val) {
|
|
176
|
+
/*
|
|
177
|
+
RFC 1876, ... expressed as a pair of four-bit unsigned
|
|
178
|
+
integers, each ranging from zero to nine, with the most
|
|
179
|
+
significant four bits representing the base and the second
|
|
180
|
+
number representing the power of ten by which to multiply
|
|
181
|
+
the base.
|
|
182
|
+
*/
|
|
183
|
+
let exponent = 0
|
|
184
|
+
while (val >= 10) {
|
|
185
|
+
val /= 10
|
|
186
|
+
++exponent
|
|
187
|
+
}
|
|
188
|
+
return (parseInt(val) << 4) | (exponent & 0x0f)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
toHuman (obj) {
|
|
192
|
+
let r = `${obj.latitude} ${obj.longitude} ${(obj.altitude/100)}m`
|
|
193
|
+
if (obj.size) r += ` ${obj.size/100}m`
|
|
194
|
+
if (obj.precision.horizontal) r += ` ${obj.precision.horizontal / 100}m`
|
|
195
|
+
if (obj.precision.vertical ) r += ` ${obj.precision.vertical / 100}m`
|
|
196
|
+
return r
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/****** EXPORTERS *******/
|
|
200
|
+
|
|
201
|
+
toTinydns () {
|
|
202
|
+
const loc = this.parseLoc(this.get('address'))
|
|
203
|
+
|
|
204
|
+
// LOC format declares in meters, tinydns uses cm (hence * 100)
|
|
205
|
+
let rdata = ''
|
|
206
|
+
rdata += TINYDNS.UInt8toOctal(0) // version
|
|
207
|
+
rdata += TINYDNS.UInt8toOctal(this.toExponent(loc.size))
|
|
208
|
+
rdata += TINYDNS.UInt8toOctal(this.toExponent(loc.precision.horizontal))
|
|
209
|
+
rdata += TINYDNS.UInt8toOctal(this.toExponent(loc.precision.vertical))
|
|
210
|
+
rdata += TINYDNS.UInt32toOctal(this.dmsToArcSec(loc.latitude))
|
|
211
|
+
rdata += TINYDNS.UInt32toOctal(this.dmsToArcSec(loc.longitude))
|
|
212
|
+
rdata += TINYDNS.UInt32toOctal(loc.altitude + REF.ALTITUDE)
|
|
213
|
+
|
|
214
|
+
return this.getTinydnsGeneric(rdata)
|
|
215
|
+
}
|
|
216
|
+
}
|
package/rr/mx.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
|
|
2
|
+
import net from 'net'
|
|
3
|
+
|
|
4
|
+
import RR from '../rr.js'
|
|
5
|
+
|
|
6
|
+
export default class MX extends RR {
|
|
7
|
+
constructor (opts) {
|
|
8
|
+
super(opts)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/****** Resource record specific setters *******/
|
|
12
|
+
setPreference (val) {
|
|
13
|
+
if (val === undefined) val = this?.default?.preference
|
|
14
|
+
this.is16bitInt('MX', 'preference', val)
|
|
15
|
+
this.set('preference', val)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setExchange (val) {
|
|
19
|
+
if (!val) throw new Error('MX: exchange is required')
|
|
20
|
+
|
|
21
|
+
if (net.isIPv4(val) || net.isIPv6(val))
|
|
22
|
+
throw new Error(`MX: exchange must be a FQDN, ${this.citeRFC()}`)
|
|
23
|
+
|
|
24
|
+
this.isFullyQualified('MX', 'exchange', val)
|
|
25
|
+
this.isValidHostname('MX', 'exchange', val)
|
|
26
|
+
|
|
27
|
+
// RFC 4034: letters in the DNS names ... are lower cased
|
|
28
|
+
this.set('exchange', val.toLowerCase())
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getDescription () {
|
|
32
|
+
return 'Mail Exchanger'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getRdataFields (arg) {
|
|
36
|
+
return [ 'preference', 'exchange' ]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getRFCs () {
|
|
40
|
+
return [ 1035, 2181, 7505 ]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getTypeId () {
|
|
44
|
+
return 15
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/****** IMPORTERS *******/
|
|
48
|
+
fromTinydns (opts) {
|
|
49
|
+
// @fqdn:ip:x:dist:ttl:timestamp:lo
|
|
50
|
+
// eslint-disable-next-line no-unused-vars
|
|
51
|
+
const [ owner, ip, x, preference, ttl, ts, loc ] = opts.tinyline.substring(1).split(':')
|
|
52
|
+
|
|
53
|
+
return new MX({
|
|
54
|
+
type : 'MX',
|
|
55
|
+
owner : this.fullyQualify(owner),
|
|
56
|
+
exchange : this.fullyQualify(/\./.test(x) ? x : `${x}.mx.${owner}`),
|
|
57
|
+
preference: parseInt(preference, 10) || 0,
|
|
58
|
+
ttl : parseInt(ttl, 10),
|
|
59
|
+
timestamp : ts,
|
|
60
|
+
location : loc !== '' && loc !== '\n' ? loc : '',
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fromBind (opts) {
|
|
65
|
+
// test.example.com 3600 IN MX preference exchange
|
|
66
|
+
const [ owner, ttl, c, type, preference, exchange ] = opts.bindline.split(/\s+/)
|
|
67
|
+
|
|
68
|
+
return new MX({
|
|
69
|
+
owner,
|
|
70
|
+
ttl : parseInt(ttl, 10),
|
|
71
|
+
class : c,
|
|
72
|
+
type,
|
|
73
|
+
preference: parseInt(preference),
|
|
74
|
+
exchange,
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/****** EXPORTERS *******/
|
|
79
|
+
toBind (zone_opts) {
|
|
80
|
+
return `${this.getPrefix(zone_opts)}\t${this.get('preference')}\t${this.getFQDN('exchange', zone_opts)}\n`
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
toTinydns () {
|
|
84
|
+
return `@${this.getTinyFQDN('owner')}::${this.getTinyFQDN('exchange')}:${this.get('preference')}:${this.getTinydnsPostamble()}\n`
|
|
85
|
+
}
|
|
86
|
+
}
|
package/rr/naptr.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
|
|
2
|
+
import RR from '../rr.js'
|
|
3
|
+
import * as TINYDNS from '../lib/tinydns.js'
|
|
4
|
+
|
|
5
|
+
const rdataRe = /[\r\n\t:\\/]/
|
|
6
|
+
|
|
7
|
+
export default class NAPTR extends RR {
|
|
8
|
+
constructor (opts) {
|
|
9
|
+
super(opts)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
getDescription () {
|
|
13
|
+
return 'Naming Authority Pointer'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
getQuotedFields () {
|
|
17
|
+
return [ 'flags', 'service', 'regexp' ]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getRdataFields (arg) {
|
|
21
|
+
return [ 'order', 'preference', 'flags', 'service', 'regexp', 'replacement' ]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getRFCs () {
|
|
25
|
+
return [ 2915, 3403 ]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getTypeId () {
|
|
29
|
+
return 35
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/****** Resource record specific setters *******/
|
|
33
|
+
setOrder (val) {
|
|
34
|
+
this.is16bitInt('NAPTR', 'order', val)
|
|
35
|
+
this.set('order', val)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setPreference (val) {
|
|
39
|
+
this.is16bitInt('NAPTR', 'preference', val)
|
|
40
|
+
this.set('preference', val)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setFlags (val) {
|
|
44
|
+
if (![ '', 'S', 'A', 'U', 'P' ].includes(val.toUpperCase()))
|
|
45
|
+
throw new Error (`NAPTR flags are invalid, ${this.citeRFC()}`)
|
|
46
|
+
|
|
47
|
+
this.set('flags', val.toUpperCase())
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setService (val) {
|
|
51
|
+
this.set('service', val)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
setRegexp (val) {
|
|
55
|
+
this.set('regexp', val)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setReplacement (val) {
|
|
59
|
+
this.set('replacement', val)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/****** IMPORTERS *******/
|
|
63
|
+
fromTinydns (opts) {
|
|
64
|
+
// NAPTR via generic, :fqdn:n:rdata:ttl:timestamp:lo
|
|
65
|
+
const [ fqdn, n, rdata, ttl, ts, loc ] = opts.tinyline.substring(1).split(':')
|
|
66
|
+
if (n != 35) throw new Error('NAPTR fromTinydns, invalid n')
|
|
67
|
+
|
|
68
|
+
const binRdata = Buffer.from(TINYDNS.octalToChar(rdata), 'binary')
|
|
69
|
+
|
|
70
|
+
const rec = {
|
|
71
|
+
type : 'NAPTR',
|
|
72
|
+
owner : this.fullyQualify(fqdn),
|
|
73
|
+
ttl : parseInt(ttl, 10),
|
|
74
|
+
timestamp : ts,
|
|
75
|
+
location : loc !== '' && loc !== '\n' ? loc : '',
|
|
76
|
+
order : binRdata.readUInt16BE(0,2),
|
|
77
|
+
preference: binRdata.readUInt16BE(2,4),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let idx = 4
|
|
81
|
+
const flagsLength = binRdata.readUInt8(idx); idx++
|
|
82
|
+
rec.flags = binRdata.slice(idx, flagsLength).toString()
|
|
83
|
+
idx += flagsLength
|
|
84
|
+
|
|
85
|
+
const serviceLen = binRdata.readUInt8(idx); idx++
|
|
86
|
+
rec.service = binRdata.slice(idx, idx+serviceLen).toString()
|
|
87
|
+
idx += serviceLen
|
|
88
|
+
|
|
89
|
+
const regexpLen = binRdata.readUInt8(idx); idx++
|
|
90
|
+
rec.regexp = binRdata.slice(idx, idx+regexpLen).toString()
|
|
91
|
+
idx += regexpLen
|
|
92
|
+
|
|
93
|
+
const replaceLen = binRdata.readUInt8(idx); idx++
|
|
94
|
+
rec.replacement = binRdata.slice(idx, idx+replaceLen).toString()
|
|
95
|
+
|
|
96
|
+
return new NAPTR(rec)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fromBind (opts) {
|
|
100
|
+
const str = opts.bindline
|
|
101
|
+
// test.example.com 3600 IN NAPTR order, preference, "flags", "service", "regexp", replacement
|
|
102
|
+
const [ owner, ttl, c, type, order, preference ] = str.split(/\s+/)
|
|
103
|
+
const [ flags, service, regexp ] = str.match(/(?:").*?(?:"\s)/g)
|
|
104
|
+
const replacement = str.trim().split(/\s+/).pop()
|
|
105
|
+
|
|
106
|
+
const bits = {
|
|
107
|
+
owner : owner,
|
|
108
|
+
ttl : parseInt(ttl, 10),
|
|
109
|
+
class : c,
|
|
110
|
+
type : type,
|
|
111
|
+
order : parseInt(order, 10),
|
|
112
|
+
preference : parseInt(preference, 10),
|
|
113
|
+
flags : flags.trim().replace(/^['"]|['"]$/g, ''),
|
|
114
|
+
service : service.trim().replace(/^['"]|['"]$/g, ''),
|
|
115
|
+
regexp : regexp.trim().replace(/^['"]|['"]/g, ''),
|
|
116
|
+
replacement: replacement,
|
|
117
|
+
}
|
|
118
|
+
return new NAPTR(bits)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/****** EXPORTERS *******/
|
|
122
|
+
toTinydns () {
|
|
123
|
+
|
|
124
|
+
let rdata =
|
|
125
|
+
TINYDNS.UInt16toOctal(this.get('order')) +
|
|
126
|
+
TINYDNS.UInt16toOctal(this.get('preference')) +
|
|
127
|
+
|
|
128
|
+
TINYDNS.UInt8toOctal(this.get('flags').length) +
|
|
129
|
+
this.get('flags') +
|
|
130
|
+
|
|
131
|
+
TINYDNS.UInt8toOctal(this.get('service').length) +
|
|
132
|
+
TINYDNS.escapeOctal(rdataRe, this.get('service')) +
|
|
133
|
+
|
|
134
|
+
TINYDNS.UInt8toOctal(this.get('regexp').length) +
|
|
135
|
+
TINYDNS.escapeOctal(rdataRe, this.get('regexp'))
|
|
136
|
+
|
|
137
|
+
const replacement = this.get('replacement')
|
|
138
|
+
if (replacement !== '') {
|
|
139
|
+
rdata += TINYDNS.UInt8toOctal(replacement.length)
|
|
140
|
+
rdata += TINYDNS.escapeOctal(rdataRe, replacement)
|
|
141
|
+
}
|
|
142
|
+
rdata += '\\000'
|
|
143
|
+
|
|
144
|
+
return this.getTinydnsGeneric(rdata)
|
|
145
|
+
}
|
|
146
|
+
}
|
package/rr/ns.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
|
|
2
|
+
import RR from '../rr.js'
|
|
3
|
+
|
|
4
|
+
export default class NS extends RR {
|
|
5
|
+
constructor (opts) {
|
|
6
|
+
super(opts)
|
|
7
|
+
if (opts === null) return
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/****** Resource record specific setters *******/
|
|
11
|
+
setDname (val) {
|
|
12
|
+
if (!val) throw new Error(`NS: dname is required, ${this.citeRFC()}`)
|
|
13
|
+
|
|
14
|
+
this.isFullyQualified('NS', 'dname', val)
|
|
15
|
+
this.isValidHostname('NS', 'dname', val)
|
|
16
|
+
|
|
17
|
+
// RFC 4034: letters in the DNS names are lower cased
|
|
18
|
+
this.set('dname', val.toLowerCase())
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getDescription () {
|
|
22
|
+
return 'Name Server'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getRdataFields (arg) {
|
|
26
|
+
return [ 'dname' ]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getRFCs () {
|
|
30
|
+
return [ 1035 ]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getTypeId () {
|
|
34
|
+
return 2
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/****** IMPORTERS *******/
|
|
38
|
+
fromTinydns (opts) {
|
|
39
|
+
// &fqdn:ip:x:ttl:timestamp:lo
|
|
40
|
+
// eslint-disable-next-line no-unused-vars
|
|
41
|
+
const [ fqdn, ip, dname, ttl, ts, loc ] = opts.tinyline.substring(1).split(':')
|
|
42
|
+
|
|
43
|
+
return new NS({
|
|
44
|
+
type : 'NS',
|
|
45
|
+
owner : this.fullyQualify(fqdn),
|
|
46
|
+
dname : this.fullyQualify(/\./.test(dname) ? dname : `${dname}.ns.${fqdn}`),
|
|
47
|
+
ttl : parseInt(ttl, 10),
|
|
48
|
+
timestamp: ts,
|
|
49
|
+
location : loc !== '' && loc !== '\n' ? loc : '',
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fromBind (opts) {
|
|
54
|
+
// test.example.com 3600 IN NS dname
|
|
55
|
+
const [ owner, ttl, c, type, dname ] = opts.bindline.split(/\s+/)
|
|
56
|
+
|
|
57
|
+
return new NS({
|
|
58
|
+
owner,
|
|
59
|
+
ttl : parseInt(ttl, 10),
|
|
60
|
+
class: c,
|
|
61
|
+
type : type,
|
|
62
|
+
dname: dname,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/****** EXPORTERS *******/
|
|
67
|
+
toBind (zone_opts) {
|
|
68
|
+
return `${this.getPrefix(zone_opts)}\t${this.getFQDN('dname', zone_opts)}\n`
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
toTinydns () {
|
|
72
|
+
return `&${this.getTinyFQDN('owner')}::${this.getTinyFQDN('dname')}:${this.getTinydnsPostamble()}\n`
|
|
73
|
+
}
|
|
74
|
+
}
|
package/rr/nsec.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
import RR from '../rr.js'
|
|
3
|
+
|
|
4
|
+
export default class NSEC extends RR {
|
|
5
|
+
constructor (opts) {
|
|
6
|
+
super(opts)
|
|
7
|
+
if (opts === null) return
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/****** Resource record specific setters *******/
|
|
11
|
+
setNextDomain (val) {
|
|
12
|
+
if (!val) throw new Error(`NSEC: 'next domain' is required:, ${this.citeRFC()}`)
|
|
13
|
+
|
|
14
|
+
this.isFullyQualified('NSEC', 'next domain', val)
|
|
15
|
+
this.isValidHostname('NSEC', 'next domain', val)
|
|
16
|
+
|
|
17
|
+
// RFC 4034: letters in the DNS names are lower cased
|
|
18
|
+
this.set('next domain', val.toLowerCase())
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setTypeBitMaps (val) {
|
|
22
|
+
if (!val) throw new Error(`NSEC: 'type bit maps' is required, ${this.citeRFC()}`)
|
|
23
|
+
|
|
24
|
+
this.set('type bit maps', val)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getDescription () {
|
|
28
|
+
return 'Next Secure'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getRdataFields (arg) {
|
|
32
|
+
return [ 'next domain', 'type bit maps' ]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getRFCs () {
|
|
36
|
+
return [ 4034 ]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getTypeId () {
|
|
40
|
+
return 47
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/****** IMPORTERS *******/
|
|
44
|
+
|
|
45
|
+
fromBind (opts) {
|
|
46
|
+
// test.example.com 3600 IN NSEC NextDomain TypeBitMaps
|
|
47
|
+
const [ owner, ttl, c, type, next ] = opts.bindline.split(/\s+/)
|
|
48
|
+
return new NSEC({
|
|
49
|
+
owner,
|
|
50
|
+
ttl : parseInt(ttl, 10),
|
|
51
|
+
class : c,
|
|
52
|
+
type : type,
|
|
53
|
+
'next domain' : next,
|
|
54
|
+
'type bit maps': opts.bindline.split(/\s+/).slice(5).filter(removeParens).join(' ').trim(),
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/****** EXPORTERS *******/
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const removeParens = a => ![ '(',')' ].includes(a)
|
package/rr/nsec3.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
|
|
2
|
+
import RR from '../rr.js'
|
|
3
|
+
|
|
4
|
+
export default class NSEC3 extends RR {
|
|
5
|
+
constructor (opts) {
|
|
6
|
+
super(opts)
|
|
7
|
+
if (opts === null) return
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/****** Resource record specific setters *******/
|
|
11
|
+
setHashAlgoritm (val) {
|
|
12
|
+
// Hash Algorithm is a single octet.
|
|
13
|
+
// The Hash Algorithm field is represented as an unsigned decimal integer.
|
|
14
|
+
if (!val) throw new Error(`NSEC3: 'hash algorithm' is required, ${this.citeRFC()}`)
|
|
15
|
+
|
|
16
|
+
this.is8bitInt('NSEC3', 'hash algorithm', val)
|
|
17
|
+
|
|
18
|
+
this.set('hash algorithm', val)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setFlags (val) {
|
|
22
|
+
// The Flags field is represented as an unsigned decimal integer.
|
|
23
|
+
if (!val) throw new Error(`NSEC3: 'flags' is required, ${this.citeRFC()}`)
|
|
24
|
+
|
|
25
|
+
this.is8bitInt('NSEC3', 'flags', val)
|
|
26
|
+
|
|
27
|
+
this.set('flags', val)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setIterations (val) {
|
|
31
|
+
// The Iterations field is represented as an unsigned decimal integer. 0-65535
|
|
32
|
+
if (!val) throw new Error(`NSEC3: 'iterations' is required, ${this.citeRFC()}`)
|
|
33
|
+
|
|
34
|
+
this.is16bitInt('NSEC3', 'flags', val)
|
|
35
|
+
|
|
36
|
+
this.set('iterations', val)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setSalt (val) {
|
|
40
|
+
// The Salt field is represented as a sequence of case-insensitive
|
|
41
|
+
// hexadecimal digits. Whitespace is not allowed within the
|
|
42
|
+
// sequence. The Salt field is represented as "-" (without the
|
|
43
|
+
// quotes) when the Salt Length field has a value of 0
|
|
44
|
+
this.set('salt', val)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setNextHashedOwnerName (val) {
|
|
48
|
+
// The Next Hashed Owner Name field is represented as an unpadded
|
|
49
|
+
// sequence of case-insensitive base32 digits, without whitespace
|
|
50
|
+
if (!val) throw new Error(`NSEC3: 'next hashed owner name' is required, ${this.citeRFC()}`)
|
|
51
|
+
|
|
52
|
+
this.set('next hashed owner name', val)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
setTypeBitMaps (val) {
|
|
56
|
+
// The Type Bit Maps field is represented as a sequence of RR type mnemonics.
|
|
57
|
+
if (!val) throw new Error(`NSEC3: 'type bit maps' is required, ${this.citeRFC()}`)
|
|
58
|
+
|
|
59
|
+
this.set('type bit maps', val)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getDescription () {
|
|
63
|
+
return 'Next Secure'
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getRdataFields (arg) {
|
|
67
|
+
return [ 'hash algorithm', 'flags', 'iterations', 'salt', 'next hashed owner name', 'type bit maps' ]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getRFCs () {
|
|
71
|
+
return [ 5155, 9077 ]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getTypeId () {
|
|
75
|
+
return 50
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/****** IMPORTERS *******/
|
|
79
|
+
|
|
80
|
+
fromBind (str) {
|
|
81
|
+
// test.example.com 3600 IN NSEC3
|
|
82
|
+
const [ owner, ttl, c, type ] = str.split(/\s+/)
|
|
83
|
+
return new NSEC3({
|
|
84
|
+
owner,
|
|
85
|
+
ttl : parseInt(ttl, 10),
|
|
86
|
+
class : c,
|
|
87
|
+
type : type,
|
|
88
|
+
'hash algorithm' : '',
|
|
89
|
+
'flags' : '',
|
|
90
|
+
'iterations' : '',
|
|
91
|
+
'salt' : '',
|
|
92
|
+
'next hashed owner name': '',
|
|
93
|
+
'type bit maps' : '',
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/****** EXPORTERS *******/
|
|
98
|
+
|
|
99
|
+
}
|