@waku/core 0.0.1 → 0.0.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/CHANGELOG.md +9 -0
- package/bundle/{index-691c0be6.js → index-a67d7136.js} +1 -1
- package/bundle/{index-0a4bdddc.js → index-f7e049ad.js} +1 -1
- package/bundle/index.js +24853 -3555
- package/bundle/lib/peer_discovery_static_list.js +18 -4
- package/bundle/lib/predefined_bootstrap_nodes.js +1 -1
- package/bundle/lib/wait_for_remote_peer.js +2 -3
- package/bundle/lib/waku_message/topic_only_message.js +2 -3
- package/bundle/lib/waku_message/version_0.js +317 -4
- package/bundle/{message-e2db79d7.js → message-049c8b67.js} +861 -2
- package/bundle/{topic_only_message-34f36fa6.js → topic_only_message-5ad3a869.js} +1 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/waku_filter/index.d.ts +2 -2
- package/dist/lib/waku_filter/index.js.map +1 -1
- package/dist/lib/waku_message/version_0.d.ts +3 -2
- package/dist/lib/waku_message/version_0.js +2 -1
- package/dist/lib/waku_message/version_0.js.map +1 -1
- package/dist/lib/waku_relay/index.d.ts +4 -3
- package/dist/lib/waku_relay/index.js.map +1 -1
- package/dist/lib/waku_store/index.d.ts +4 -4
- package/package.json +4 -123
- package/src/index.ts +0 -10
- package/src/lib/waku_filter/index.ts +4 -3
- package/src/lib/waku_message/version_0.ts +5 -3
- package/src/lib/waku_relay/index.ts +4 -3
- package/src/lib/waku_store/index.ts +5 -5
- package/bundle/crypto-8551d579.js +0 -2585
- package/bundle/crypto-b00764b7.js +0 -1772
- package/bundle/enr-564d4a51.js +0 -20785
- package/bundle/enr-9fc5eed8.js +0 -20786
- package/bundle/enr-f6e82a53.js +0 -20785
- package/bundle/events-fcbda4dc.js +0 -76
- package/bundle/index-02d21809.js +0 -20
- package/bundle/index-2ae915be.js +0 -1854
- package/bundle/index-a013a259.js +0 -20
- package/bundle/index-ba42b4fc.js +0 -862
- package/bundle/lib/enr.js +0 -8
- package/bundle/lib/peer_discovery_dns.js +0 -5018
- package/bundle/lib/utils.js +0 -1
- package/bundle/lib/waku_message/version_1.js +0 -463
- package/bundle/multiaddr_to_peer_info-c406b1e1.js +0 -19
- package/bundle/multiaddr_to_peer_info-fd1de516.js +0 -19
- package/bundle/utils-9a3221f2.js +0 -815
- package/bundle/version_0-e6fe440c.js +0 -317
- package/dist/lib/crypto.d.ts +0 -34
- package/dist/lib/crypto.js +0 -79
- package/dist/lib/crypto.js.map +0 -1
- package/dist/lib/enr/constants.d.ts +0 -4
- package/dist/lib/enr/constants.js +0 -8
- package/dist/lib/enr/constants.js.map +0 -1
- package/dist/lib/enr/enr.d.ts +0 -90
- package/dist/lib/enr/enr.js +0 -432
- package/dist/lib/enr/enr.js.map +0 -1
- package/dist/lib/enr/index.d.ts +0 -5
- package/dist/lib/enr/index.js +0 -6
- package/dist/lib/enr/index.js.map +0 -1
- package/dist/lib/enr/keypair/index.d.ts +0 -8
- package/dist/lib/enr/keypair/index.js +0 -53
- package/dist/lib/enr/keypair/index.js.map +0 -1
- package/dist/lib/enr/keypair/secp256k1.d.ts +0 -13
- package/dist/lib/enr/keypair/secp256k1.js +0 -57
- package/dist/lib/enr/keypair/secp256k1.js.map +0 -1
- package/dist/lib/enr/keypair/types.d.ts +0 -13
- package/dist/lib/enr/keypair/types.js +0 -7
- package/dist/lib/enr/keypair/types.js.map +0 -1
- package/dist/lib/enr/multiaddr_from_fields.d.ts +0 -2
- package/dist/lib/enr/multiaddr_from_fields.js +0 -8
- package/dist/lib/enr/multiaddr_from_fields.js.map +0 -1
- package/dist/lib/enr/multiaddrs_codec.d.ts +0 -3
- package/dist/lib/enr/multiaddrs_codec.js +0 -32
- package/dist/lib/enr/multiaddrs_codec.js.map +0 -1
- package/dist/lib/enr/types.d.ts +0 -8
- package/dist/lib/enr/types.js +0 -3
- package/dist/lib/enr/types.js.map +0 -1
- package/dist/lib/enr/v4.d.ts +0 -3
- package/dist/lib/enr/v4.js +0 -14
- package/dist/lib/enr/v4.js.map +0 -1
- package/dist/lib/enr/waku2_codec.d.ts +0 -8
- package/dist/lib/enr/waku2_codec.js +0 -36
- package/dist/lib/enr/waku2_codec.js.map +0 -1
- package/dist/lib/peer_discovery_dns/dns.d.ts +0 -48
- package/dist/lib/peer_discovery_dns/dns.js +0 -158
- package/dist/lib/peer_discovery_dns/dns.js.map +0 -1
- package/dist/lib/peer_discovery_dns/dns_over_https.d.ts +0 -32
- package/dist/lib/peer_discovery_dns/dns_over_https.js +0 -87
- package/dist/lib/peer_discovery_dns/dns_over_https.js.map +0 -1
- package/dist/lib/peer_discovery_dns/enrtree.d.ts +0 -33
- package/dist/lib/peer_discovery_dns/enrtree.js +0 -76
- package/dist/lib/peer_discovery_dns/enrtree.js.map +0 -1
- package/dist/lib/peer_discovery_dns/fetch_nodes.d.ts +0 -14
- package/dist/lib/peer_discovery_dns/fetch_nodes.js +0 -133
- package/dist/lib/peer_discovery_dns/fetch_nodes.js.map +0 -1
- package/dist/lib/peer_discovery_dns/index.d.ts +0 -30
- package/dist/lib/peer_discovery_dns/index.js +0 -54
- package/dist/lib/peer_discovery_dns/index.js.map +0 -1
- package/dist/lib/utils.d.ts +0 -22
- package/dist/lib/utils.js +0 -40
- package/dist/lib/utils.js.map +0 -1
- package/dist/lib/waku_message/constants.d.ts +0 -12
- package/dist/lib/waku_message/constants.js +0 -10
- package/dist/lib/waku_message/constants.js.map +0 -1
- package/dist/lib/waku_message/ecies.d.ts +0 -17
- package/dist/lib/waku_message/ecies.js +0 -126
- package/dist/lib/waku_message/ecies.js.map +0 -1
- package/dist/lib/waku_message/symmetric.d.ts +0 -3
- package/dist/lib/waku_message/symmetric.js +0 -18
- package/dist/lib/waku_message/symmetric.js.map +0 -1
- package/dist/lib/waku_message/version_1.d.ts +0 -93
- package/dist/lib/waku_message/version_1.js +0 -325
- package/dist/lib/waku_message/version_1.js.map +0 -1
- package/src/lib/crypto.ts +0 -100
- package/src/lib/enr/constants.ts +0 -10
- package/src/lib/enr/enr.ts +0 -516
- package/src/lib/enr/index.ts +0 -5
- package/src/lib/enr/keypair/index.ts +0 -76
- package/src/lib/enr/keypair/secp256k1.ts +0 -69
- package/src/lib/enr/keypair/types.ts +0 -14
- package/src/lib/enr/multiaddr_from_fields.ts +0 -18
- package/src/lib/enr/multiaddrs_codec.ts +0 -50
- package/src/lib/enr/types.ts +0 -11
- package/src/lib/enr/v4.ts +0 -22
- package/src/lib/enr/waku2_codec.ts +0 -39
- package/src/lib/peer_discovery_dns/dns.ts +0 -223
- package/src/lib/peer_discovery_dns/dns_over_https.ts +0 -98
- package/src/lib/peer_discovery_dns/enrtree.ts +0 -123
- package/src/lib/peer_discovery_dns/fetch_nodes.ts +0 -180
- package/src/lib/peer_discovery_dns/index.ts +0 -84
- package/src/lib/utils.ts +0 -50
- package/src/lib/waku_message/constants.ts +0 -10
- package/src/lib/waku_message/ecies.ts +0 -194
- package/src/lib/waku_message/symmetric.ts +0 -33
- package/src/lib/waku_message/version_1.ts +0 -457
@@ -1,5018 +0,0 @@
|
|
1
|
-
import { m as multiaddrsToPeerInfo, s as symbol } from '../multiaddr_to_peer_info-fd1de516.js';
|
2
|
-
import { E as EventEmitter, C as CustomEvent } from '../events-fcbda4dc.js';
|
3
|
-
import { d as debug } from '../browser-1e1a2f27.js';
|
4
|
-
import { d as ENR } from '../enr-9fc5eed8.js';
|
5
|
-
import { e as verifySignature, k as keccak256 } from '../crypto-b00764b7.js';
|
6
|
-
import '../index-2ae915be.js';
|
7
|
-
import { g as bytesToUtf8, e as utf8ToBytes, f as fromString } from '../utils-9a3221f2.js';
|
8
|
-
import { g as commonjsGlobal } from '../index-ba42b4fc.js';
|
9
|
-
import '../index-0a4bdddc.js';
|
10
|
-
import '../index-64ce43f0.js';
|
11
|
-
|
12
|
-
const v4Regex$1 = /^(\d{1,3}\.){3,3}\d{1,3}$/;
|
13
|
-
const v4Size = 4;
|
14
|
-
const v6Regex$1 = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i;
|
15
|
-
const v6Size = 16;
|
16
|
-
|
17
|
-
const v4 = {
|
18
|
-
name: 'v4',
|
19
|
-
size: v4Size,
|
20
|
-
isFormat: ip => v4Regex$1.test(ip),
|
21
|
-
encode (ip, buff, offset) {
|
22
|
-
offset = ~~offset;
|
23
|
-
buff = buff || new Uint8Array(offset + v4Size);
|
24
|
-
const max = ip.length;
|
25
|
-
let n = 0;
|
26
|
-
for (let i = 0; i < max;) {
|
27
|
-
const c = ip.charCodeAt(i++);
|
28
|
-
if (c === 46) { // "."
|
29
|
-
buff[offset++] = n;
|
30
|
-
n = 0;
|
31
|
-
} else {
|
32
|
-
n = n * 10 + (c - 48);
|
33
|
-
}
|
34
|
-
}
|
35
|
-
buff[offset] = n;
|
36
|
-
return buff
|
37
|
-
},
|
38
|
-
decode (buff, offset) {
|
39
|
-
offset = ~~offset;
|
40
|
-
return `${buff[offset++]}.${buff[offset++]}.${buff[offset++]}.${buff[offset]}`
|
41
|
-
}
|
42
|
-
};
|
43
|
-
|
44
|
-
const v6 = {
|
45
|
-
name: 'v6',
|
46
|
-
size: v6Size,
|
47
|
-
isFormat: ip => ip.length > 0 && v6Regex$1.test(ip),
|
48
|
-
encode (ip, buff, offset) {
|
49
|
-
offset = ~~offset;
|
50
|
-
let end = offset + v6Size;
|
51
|
-
let fill = -1;
|
52
|
-
let hexN = 0;
|
53
|
-
let decN = 0;
|
54
|
-
let prevColon = true;
|
55
|
-
let useDec = false;
|
56
|
-
buff = buff || new Uint8Array(offset + v6Size);
|
57
|
-
// Note: This algorithm needs to check if the offset
|
58
|
-
// could exceed the buffer boundaries as it supports
|
59
|
-
// non-standard compliant encodings that may go beyond
|
60
|
-
// the boundary limits. if (offset < end) checks should
|
61
|
-
// not be necessary...
|
62
|
-
for (let i = 0; i < ip.length; i++) {
|
63
|
-
let c = ip.charCodeAt(i);
|
64
|
-
if (c === 58) { // :
|
65
|
-
if (prevColon) {
|
66
|
-
if (fill !== -1) {
|
67
|
-
// Not Standard! (standard doesn't allow multiple ::)
|
68
|
-
// We need to treat
|
69
|
-
if (offset < end) buff[offset] = 0;
|
70
|
-
if (offset < end - 1) buff[offset + 1] = 0;
|
71
|
-
offset += 2;
|
72
|
-
} else if (offset < end) {
|
73
|
-
// :: in the middle
|
74
|
-
fill = offset;
|
75
|
-
}
|
76
|
-
} else {
|
77
|
-
// : ends the previous number
|
78
|
-
if (useDec === true) {
|
79
|
-
// Non-standard! (ipv4 should be at end only)
|
80
|
-
// A ipv4 address should not be found anywhere else but at
|
81
|
-
// the end. This codec also support putting characters
|
82
|
-
// after the ipv4 address..
|
83
|
-
if (offset < end) buff[offset] = decN;
|
84
|
-
offset++;
|
85
|
-
} else {
|
86
|
-
if (offset < end) buff[offset] = hexN >> 8;
|
87
|
-
if (offset < end - 1) buff[offset + 1] = hexN & 0xff;
|
88
|
-
offset += 2;
|
89
|
-
}
|
90
|
-
hexN = 0;
|
91
|
-
decN = 0;
|
92
|
-
}
|
93
|
-
prevColon = true;
|
94
|
-
useDec = false;
|
95
|
-
} else if (c === 46) { // . indicates IPV4 notation
|
96
|
-
if (offset < end) buff[offset] = decN;
|
97
|
-
offset++;
|
98
|
-
decN = 0;
|
99
|
-
hexN = 0;
|
100
|
-
prevColon = false;
|
101
|
-
useDec = true;
|
102
|
-
} else {
|
103
|
-
prevColon = false;
|
104
|
-
if (c >= 97) {
|
105
|
-
c -= 87; // a-f ... 97~102 -87 => 10~15
|
106
|
-
} else if (c >= 65) {
|
107
|
-
c -= 55; // A-F ... 65~70 -55 => 10~15
|
108
|
-
} else {
|
109
|
-
c -= 48; // 0-9 ... starting from charCode 48
|
110
|
-
decN = decN * 10 + c;
|
111
|
-
}
|
112
|
-
// We don't know yet if its a dec or hex number
|
113
|
-
hexN = (hexN << 4) + c;
|
114
|
-
}
|
115
|
-
}
|
116
|
-
if (prevColon === false) {
|
117
|
-
// Commiting last number
|
118
|
-
if (useDec === true) {
|
119
|
-
if (offset < end) buff[offset] = decN;
|
120
|
-
offset++;
|
121
|
-
} else {
|
122
|
-
if (offset < end) buff[offset] = hexN >> 8;
|
123
|
-
if (offset < end - 1) buff[offset + 1] = hexN & 0xff;
|
124
|
-
offset += 2;
|
125
|
-
}
|
126
|
-
} else if (fill === 0) {
|
127
|
-
// Not Standard! (standard doesn't allow multiple ::)
|
128
|
-
// This means that a : was found at the start AND end which means the
|
129
|
-
// end needs to be treated as 0 entry...
|
130
|
-
if (offset < end) buff[offset] = 0;
|
131
|
-
if (offset < end - 1) buff[offset + 1] = 0;
|
132
|
-
offset += 2;
|
133
|
-
} else if (fill !== -1) {
|
134
|
-
// Non-standard! (standard doens't allow multiple ::)
|
135
|
-
// Here we find that there has been a :: somewhere in the middle
|
136
|
-
// and the end. To treat the end with priority we need to move all
|
137
|
-
// written data two bytes to the right.
|
138
|
-
offset += 2;
|
139
|
-
for (let i = Math.min(offset - 1, end - 1); i >= fill + 2; i--) {
|
140
|
-
buff[i] = buff[i - 2];
|
141
|
-
}
|
142
|
-
buff[fill] = 0;
|
143
|
-
buff[fill + 1] = 0;
|
144
|
-
fill = offset;
|
145
|
-
}
|
146
|
-
if (fill !== offset && fill !== -1) {
|
147
|
-
// Move the written numbers to the end while filling the everything
|
148
|
-
// "fill" to the bytes with zeros.
|
149
|
-
if (offset > end - 2) {
|
150
|
-
// Non Standard support, when the cursor exceeds bounds.
|
151
|
-
offset = end - 2;
|
152
|
-
}
|
153
|
-
while (end > fill) {
|
154
|
-
buff[--end] = offset < end && offset > fill ? buff[--offset] : 0;
|
155
|
-
}
|
156
|
-
} else {
|
157
|
-
// Fill the rest with zeros
|
158
|
-
while (offset < end) {
|
159
|
-
buff[offset++] = 0;
|
160
|
-
}
|
161
|
-
}
|
162
|
-
return buff
|
163
|
-
},
|
164
|
-
decode (buff, offset) {
|
165
|
-
offset = ~~offset;
|
166
|
-
let result = '';
|
167
|
-
for (let i = 0; i < v6Size; i += 2) {
|
168
|
-
if (i !== 0) {
|
169
|
-
result += ':';
|
170
|
-
}
|
171
|
-
result += (buff[offset + i] << 8 | buff[offset + i + 1]).toString(16);
|
172
|
-
}
|
173
|
-
return result
|
174
|
-
.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3')
|
175
|
-
.replace(/:{3,4}/, '::')
|
176
|
-
}
|
177
|
-
};
|
178
|
-
function sizeOf (ip) {
|
179
|
-
if (v4.isFormat(ip)) return v4.size
|
180
|
-
if (v6.isFormat(ip)) return v6.size
|
181
|
-
throw Error(`Invalid ip address: ${ip}`)
|
182
|
-
}
|
183
|
-
|
184
|
-
function familyOf (string) {
|
185
|
-
return sizeOf(string) === v4.size ? 1 : 2
|
186
|
-
}
|
187
|
-
|
188
|
-
function encode$2 (ip, buff, offset) {
|
189
|
-
offset = ~~offset;
|
190
|
-
const size = sizeOf(ip);
|
191
|
-
if (typeof buff === 'function') {
|
192
|
-
buff = buff(offset + size);
|
193
|
-
}
|
194
|
-
if (size === v4.size) {
|
195
|
-
return v4.encode(ip, buff, offset)
|
196
|
-
}
|
197
|
-
return v6.encode(ip, buff, offset)
|
198
|
-
}
|
199
|
-
|
200
|
-
function decode$2 (buff, offset, length) {
|
201
|
-
offset = ~~offset;
|
202
|
-
length = length || (buff.length - offset);
|
203
|
-
if (length === v4.size) {
|
204
|
-
return v4.decode(buff, offset, length)
|
205
|
-
}
|
206
|
-
if (length === v6.size) {
|
207
|
-
return v6.decode(buff, offset, length)
|
208
|
-
}
|
209
|
-
throw Error(`Invalid buffer size needs to be ${v4.size} for v4 or ${v6.size} for v6.`)
|
210
|
-
}
|
211
|
-
|
212
|
-
function toString$4 (type) {
|
213
|
-
switch (type) {
|
214
|
-
case 1: return 'A'
|
215
|
-
case 10: return 'NULL'
|
216
|
-
case 28: return 'AAAA'
|
217
|
-
case 18: return 'AFSDB'
|
218
|
-
case 42: return 'APL'
|
219
|
-
case 257: return 'CAA'
|
220
|
-
case 60: return 'CDNSKEY'
|
221
|
-
case 59: return 'CDS'
|
222
|
-
case 37: return 'CERT'
|
223
|
-
case 5: return 'CNAME'
|
224
|
-
case 49: return 'DHCID'
|
225
|
-
case 32769: return 'DLV'
|
226
|
-
case 39: return 'DNAME'
|
227
|
-
case 48: return 'DNSKEY'
|
228
|
-
case 43: return 'DS'
|
229
|
-
case 55: return 'HIP'
|
230
|
-
case 13: return 'HINFO'
|
231
|
-
case 45: return 'IPSECKEY'
|
232
|
-
case 25: return 'KEY'
|
233
|
-
case 36: return 'KX'
|
234
|
-
case 29: return 'LOC'
|
235
|
-
case 15: return 'MX'
|
236
|
-
case 35: return 'NAPTR'
|
237
|
-
case 2: return 'NS'
|
238
|
-
case 47: return 'NSEC'
|
239
|
-
case 50: return 'NSEC3'
|
240
|
-
case 51: return 'NSEC3PARAM'
|
241
|
-
case 12: return 'PTR'
|
242
|
-
case 46: return 'RRSIG'
|
243
|
-
case 17: return 'RP'
|
244
|
-
case 24: return 'SIG'
|
245
|
-
case 6: return 'SOA'
|
246
|
-
case 99: return 'SPF'
|
247
|
-
case 33: return 'SRV'
|
248
|
-
case 44: return 'SSHFP'
|
249
|
-
case 32768: return 'TA'
|
250
|
-
case 249: return 'TKEY'
|
251
|
-
case 52: return 'TLSA'
|
252
|
-
case 250: return 'TSIG'
|
253
|
-
case 16: return 'TXT'
|
254
|
-
case 252: return 'AXFR'
|
255
|
-
case 251: return 'IXFR'
|
256
|
-
case 41: return 'OPT'
|
257
|
-
case 255: return 'ANY'
|
258
|
-
}
|
259
|
-
return 'UNKNOWN_' + type
|
260
|
-
}
|
261
|
-
|
262
|
-
function toType (name) {
|
263
|
-
switch (name.toUpperCase()) {
|
264
|
-
case 'A': return 1
|
265
|
-
case 'NULL': return 10
|
266
|
-
case 'AAAA': return 28
|
267
|
-
case 'AFSDB': return 18
|
268
|
-
case 'APL': return 42
|
269
|
-
case 'CAA': return 257
|
270
|
-
case 'CDNSKEY': return 60
|
271
|
-
case 'CDS': return 59
|
272
|
-
case 'CERT': return 37
|
273
|
-
case 'CNAME': return 5
|
274
|
-
case 'DHCID': return 49
|
275
|
-
case 'DLV': return 32769
|
276
|
-
case 'DNAME': return 39
|
277
|
-
case 'DNSKEY': return 48
|
278
|
-
case 'DS': return 43
|
279
|
-
case 'HIP': return 55
|
280
|
-
case 'HINFO': return 13
|
281
|
-
case 'IPSECKEY': return 45
|
282
|
-
case 'KEY': return 25
|
283
|
-
case 'KX': return 36
|
284
|
-
case 'LOC': return 29
|
285
|
-
case 'MX': return 15
|
286
|
-
case 'NAPTR': return 35
|
287
|
-
case 'NS': return 2
|
288
|
-
case 'NSEC': return 47
|
289
|
-
case 'NSEC3': return 50
|
290
|
-
case 'NSEC3PARAM': return 51
|
291
|
-
case 'PTR': return 12
|
292
|
-
case 'RRSIG': return 46
|
293
|
-
case 'RP': return 17
|
294
|
-
case 'SIG': return 24
|
295
|
-
case 'SOA': return 6
|
296
|
-
case 'SPF': return 99
|
297
|
-
case 'SRV': return 33
|
298
|
-
case 'SSHFP': return 44
|
299
|
-
case 'TA': return 32768
|
300
|
-
case 'TKEY': return 249
|
301
|
-
case 'TLSA': return 52
|
302
|
-
case 'TSIG': return 250
|
303
|
-
case 'TXT': return 16
|
304
|
-
case 'AXFR': return 252
|
305
|
-
case 'IXFR': return 251
|
306
|
-
case 'OPT': return 41
|
307
|
-
case 'ANY': return 255
|
308
|
-
case '*': return 255
|
309
|
-
}
|
310
|
-
if (name.toUpperCase().startsWith('UNKNOWN_')) return parseInt(name.slice(8))
|
311
|
-
return 0
|
312
|
-
}
|
313
|
-
|
314
|
-
/*
|
315
|
-
* Traditional DNS header RCODEs (4-bits) defined by IANA in
|
316
|
-
* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
|
317
|
-
*/
|
318
|
-
|
319
|
-
function toString$3 (rcode) {
|
320
|
-
switch (rcode) {
|
321
|
-
case 0: return 'NOERROR'
|
322
|
-
case 1: return 'FORMERR'
|
323
|
-
case 2: return 'SERVFAIL'
|
324
|
-
case 3: return 'NXDOMAIN'
|
325
|
-
case 4: return 'NOTIMP'
|
326
|
-
case 5: return 'REFUSED'
|
327
|
-
case 6: return 'YXDOMAIN'
|
328
|
-
case 7: return 'YXRRSET'
|
329
|
-
case 8: return 'NXRRSET'
|
330
|
-
case 9: return 'NOTAUTH'
|
331
|
-
case 10: return 'NOTZONE'
|
332
|
-
case 11: return 'RCODE_11'
|
333
|
-
case 12: return 'RCODE_12'
|
334
|
-
case 13: return 'RCODE_13'
|
335
|
-
case 14: return 'RCODE_14'
|
336
|
-
case 15: return 'RCODE_15'
|
337
|
-
}
|
338
|
-
return 'RCODE_' + rcode
|
339
|
-
}
|
340
|
-
|
341
|
-
/*
|
342
|
-
* Traditional DNS header OPCODEs (4-bits) defined by IANA in
|
343
|
-
* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-5
|
344
|
-
*/
|
345
|
-
|
346
|
-
function toString$2 (opcode) {
|
347
|
-
switch (opcode) {
|
348
|
-
case 0: return 'QUERY'
|
349
|
-
case 1: return 'IQUERY'
|
350
|
-
case 2: return 'STATUS'
|
351
|
-
case 3: return 'OPCODE_3'
|
352
|
-
case 4: return 'NOTIFY'
|
353
|
-
case 5: return 'UPDATE'
|
354
|
-
case 6: return 'OPCODE_6'
|
355
|
-
case 7: return 'OPCODE_7'
|
356
|
-
case 8: return 'OPCODE_8'
|
357
|
-
case 9: return 'OPCODE_9'
|
358
|
-
case 10: return 'OPCODE_10'
|
359
|
-
case 11: return 'OPCODE_11'
|
360
|
-
case 12: return 'OPCODE_12'
|
361
|
-
case 13: return 'OPCODE_13'
|
362
|
-
case 14: return 'OPCODE_14'
|
363
|
-
case 15: return 'OPCODE_15'
|
364
|
-
}
|
365
|
-
return 'OPCODE_' + opcode
|
366
|
-
}
|
367
|
-
|
368
|
-
function toString$1 (klass) {
|
369
|
-
switch (klass) {
|
370
|
-
case 1: return 'IN'
|
371
|
-
case 2: return 'CS'
|
372
|
-
case 3: return 'CH'
|
373
|
-
case 4: return 'HS'
|
374
|
-
case 255: return 'ANY'
|
375
|
-
}
|
376
|
-
return 'UNKNOWN_' + klass
|
377
|
-
}
|
378
|
-
|
379
|
-
function toClass (name) {
|
380
|
-
switch (name.toUpperCase()) {
|
381
|
-
case 'IN': return 1
|
382
|
-
case 'CS': return 2
|
383
|
-
case 'CH': return 3
|
384
|
-
case 'HS': return 4
|
385
|
-
case 'ANY': return 255
|
386
|
-
}
|
387
|
-
return 0
|
388
|
-
}
|
389
|
-
|
390
|
-
function toString (type) {
|
391
|
-
switch (type) {
|
392
|
-
// list at
|
393
|
-
// https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-11
|
394
|
-
case 1: return 'LLQ'
|
395
|
-
case 2: return 'UL'
|
396
|
-
case 3: return 'NSID'
|
397
|
-
case 5: return 'DAU'
|
398
|
-
case 6: return 'DHU'
|
399
|
-
case 7: return 'N3U'
|
400
|
-
case 8: return 'CLIENT_SUBNET'
|
401
|
-
case 9: return 'EXPIRE'
|
402
|
-
case 10: return 'COOKIE'
|
403
|
-
case 11: return 'TCP_KEEPALIVE'
|
404
|
-
case 12: return 'PADDING'
|
405
|
-
case 13: return 'CHAIN'
|
406
|
-
case 14: return 'KEY_TAG'
|
407
|
-
case 26946: return 'DEVICEID'
|
408
|
-
}
|
409
|
-
if (type < 0) {
|
410
|
-
return null
|
411
|
-
}
|
412
|
-
return `OPTION_${type}`
|
413
|
-
}
|
414
|
-
|
415
|
-
function toCode (name) {
|
416
|
-
if (typeof name === 'number') {
|
417
|
-
return name
|
418
|
-
}
|
419
|
-
if (!name) {
|
420
|
-
return -1
|
421
|
-
}
|
422
|
-
switch (name.toUpperCase()) {
|
423
|
-
case 'OPTION_0': return 0
|
424
|
-
case 'LLQ': return 1
|
425
|
-
case 'UL': return 2
|
426
|
-
case 'NSID': return 3
|
427
|
-
case 'OPTION_4': return 4
|
428
|
-
case 'DAU': return 5
|
429
|
-
case 'DHU': return 6
|
430
|
-
case 'N3U': return 7
|
431
|
-
case 'CLIENT_SUBNET': return 8
|
432
|
-
case 'EXPIRE': return 9
|
433
|
-
case 'COOKIE': return 10
|
434
|
-
case 'TCP_KEEPALIVE': return 11
|
435
|
-
case 'PADDING': return 12
|
436
|
-
case 'CHAIN': return 13
|
437
|
-
case 'KEY_TAG': return 14
|
438
|
-
case 'DEVICEID': return 26946
|
439
|
-
case 'OPTION_65535': return 65535
|
440
|
-
}
|
441
|
-
const m = name.match(/_(\d+)$/);
|
442
|
-
if (m) {
|
443
|
-
return parseInt(m[1], 10)
|
444
|
-
}
|
445
|
-
return -1
|
446
|
-
}
|
447
|
-
|
448
|
-
const SURROGATE_A = 0b1101100000000000;
|
449
|
-
const SURROGATE_B = 0b1101110000000000;
|
450
|
-
|
451
|
-
function encodingLength$1 (str) {
|
452
|
-
let len = 0;
|
453
|
-
const strLen = str.length;
|
454
|
-
for (let i = 0; i < strLen; i += 1) {
|
455
|
-
const code = str.charCodeAt(i);
|
456
|
-
if (code <= 0x7F) {
|
457
|
-
len += 1;
|
458
|
-
} else if (code <= 0x07FF) {
|
459
|
-
len += 2;
|
460
|
-
} else if ((code & 0xF800) !== SURROGATE_A) {
|
461
|
-
len += 3;
|
462
|
-
} else {
|
463
|
-
const next = i + 1;
|
464
|
-
if (next === strLen || code >= SURROGATE_B) {
|
465
|
-
len += 3;
|
466
|
-
} else {
|
467
|
-
const nextCode = str.charCodeAt(next);
|
468
|
-
if ((nextCode & 0xFC00) !== SURROGATE_B) {
|
469
|
-
len += 3;
|
470
|
-
} else {
|
471
|
-
i = next;
|
472
|
-
len += 4;
|
473
|
-
}
|
474
|
-
}
|
475
|
-
}
|
476
|
-
}
|
477
|
-
return len
|
478
|
-
}
|
479
|
-
|
480
|
-
function encode$1 (str, buf, offset) {
|
481
|
-
const strLen = str.length;
|
482
|
-
if (offset === undefined || offset === null) {
|
483
|
-
offset = 0;
|
484
|
-
}
|
485
|
-
if (buf === undefined) {
|
486
|
-
buf = new Uint8Array(encodingLength$1(str) + offset);
|
487
|
-
}
|
488
|
-
let off = offset;
|
489
|
-
for (let i = 0; i < strLen; i += 1) {
|
490
|
-
let code = str.charCodeAt(i);
|
491
|
-
if (code <= 0x7F) {
|
492
|
-
buf[off++] = code;
|
493
|
-
} else if (code <= 0x07FF) {
|
494
|
-
buf[off++] = 0b11000000 | ((code & 0b11111000000) >> 6);
|
495
|
-
buf[off++] = 0b10000000 | (code & 0b00000111111);
|
496
|
-
} else if ((code & 0xF800) !== SURROGATE_A) {
|
497
|
-
buf[off++] = 0b11100000 | ((code & 0b1111000000000000) >> 12);
|
498
|
-
buf[off++] = 0b10000000 | ((code & 0b0000111111000000) >> 6);
|
499
|
-
buf[off++] = 0b10000000 | (code & 0b0000000000111111);
|
500
|
-
} else {
|
501
|
-
const next = i + 1;
|
502
|
-
if (next === strLen || code >= SURROGATE_B) {
|
503
|
-
// Incorrectly started surrogate pair
|
504
|
-
buf[off++] = 0xef;
|
505
|
-
buf[off++] = 0xbf;
|
506
|
-
buf[off++] = 0xbd;
|
507
|
-
} else {
|
508
|
-
const nextCode = str.charCodeAt(next);
|
509
|
-
if ((nextCode & 0xFC00) !== SURROGATE_B) {
|
510
|
-
// Incorrect surrogate pair
|
511
|
-
buf[off++] = 0xef;
|
512
|
-
buf[off++] = 0xbf;
|
513
|
-
buf[off++] = 0xbd;
|
514
|
-
} else {
|
515
|
-
i = next;
|
516
|
-
code = 0b000010000000000000000 |
|
517
|
-
((code & 0b1111111111) << 10) |
|
518
|
-
(nextCode & 0b1111111111);
|
519
|
-
buf[off++] = 0b11110000 | ((code & 0b111000000000000000000) >> 18);
|
520
|
-
buf[off++] = 0b10000000 | ((code & 0b000111111000000000000) >> 12);
|
521
|
-
buf[off++] = 0b10000000 | ((code & 0b000000000111111000000) >> 6);
|
522
|
-
buf[off++] = 0b10000000 | (code & 0b000000000000000111111);
|
523
|
-
}
|
524
|
-
}
|
525
|
-
}
|
526
|
-
}
|
527
|
-
encode$1.bytes = off - offset;
|
528
|
-
return buf
|
529
|
-
}
|
530
|
-
encode$1.bytes = 0;
|
531
|
-
|
532
|
-
function decode$1 (buf, start, end) {
|
533
|
-
let result = '';
|
534
|
-
if (start === undefined || start === null) {
|
535
|
-
start = 0;
|
536
|
-
}
|
537
|
-
if (end === undefined || end === null) {
|
538
|
-
end = buf.length;
|
539
|
-
}
|
540
|
-
for (let offset = start; offset < end;) {
|
541
|
-
const code = buf[offset++];
|
542
|
-
let num;
|
543
|
-
if (code <= 128) {
|
544
|
-
num = code;
|
545
|
-
} else if (code > 191 && code < 224) {
|
546
|
-
num = ((code & 0b11111) << 6) | (buf[offset++] & 0b111111);
|
547
|
-
} else if (code > 239 && code < 365) {
|
548
|
-
num = (
|
549
|
-
((code & 0b111) << 18) |
|
550
|
-
((buf[offset++] & 0b111111) << 12) |
|
551
|
-
((buf[offset++] & 0b111111) << 6) |
|
552
|
-
(buf[offset++] & 0b111111)
|
553
|
-
) - 0x10000;
|
554
|
-
const numA = SURROGATE_A | ((num >> 10) & 0b1111111111);
|
555
|
-
result += String.fromCharCode(numA);
|
556
|
-
num = SURROGATE_B | (num & 0b1111111111);
|
557
|
-
} else {
|
558
|
-
num = ((code & 0b1111) << 12) |
|
559
|
-
((buf[offset++] & 0b111111) << 6) |
|
560
|
-
(buf[offset++] & 0b111111);
|
561
|
-
}
|
562
|
-
result += String.fromCharCode(num);
|
563
|
-
}
|
564
|
-
decode$1.bytes = end - start;
|
565
|
-
return result
|
566
|
-
}
|
567
|
-
decode$1.bytes = 0;
|
568
|
-
|
569
|
-
const isU8Arr = input => input instanceof Uint8Array;
|
570
|
-
|
571
|
-
function bytelength (input) {
|
572
|
-
return typeof input === 'string' ? encodingLength$1(input) : input.byteLength
|
573
|
-
}
|
574
|
-
|
575
|
-
function from (input) {
|
576
|
-
if (input instanceof Uint8Array) {
|
577
|
-
return input
|
578
|
-
}
|
579
|
-
if (Array.isArray(input)) {
|
580
|
-
return new Uint8Array(input)
|
581
|
-
}
|
582
|
-
return encode$1(input)
|
583
|
-
}
|
584
|
-
|
585
|
-
function write (arr, str, start) {
|
586
|
-
if (typeof str !== 'string') {
|
587
|
-
throw new Error('unknown input type')
|
588
|
-
}
|
589
|
-
encode$1(str, arr, start);
|
590
|
-
return encode$1.bytes
|
591
|
-
}
|
592
|
-
|
593
|
-
const P_24 = Math.pow(2, 24);
|
594
|
-
const P_16 = Math.pow(2, 16);
|
595
|
-
const P_8 = Math.pow(2, 8);
|
596
|
-
const readUInt32BE = (buf, offset) => buf[offset] * P_24 +
|
597
|
-
buf[offset + 1] * P_16 +
|
598
|
-
buf[offset + 2] * P_8 +
|
599
|
-
buf[offset + 3];
|
600
|
-
|
601
|
-
const readUInt16BE = (buf, offset) => (buf[offset] << 8) | buf[offset + 1];
|
602
|
-
const writeUInt32BE = (buf, value, offset) => {
|
603
|
-
value = +value;
|
604
|
-
buf[offset + 3] = value;
|
605
|
-
value = value >>> 8;
|
606
|
-
buf[offset + 2] = value;
|
607
|
-
value = value >>> 8;
|
608
|
-
buf[offset + 1] = value;
|
609
|
-
value = value >>> 8;
|
610
|
-
buf[offset] = value;
|
611
|
-
return offset + 4
|
612
|
-
};
|
613
|
-
const writeUInt16BE = (buf, value, offset) => {
|
614
|
-
buf[offset] = value >> 8;
|
615
|
-
buf[offset + 1] = value & 0xFF;
|
616
|
-
return offset + 2
|
617
|
-
};
|
618
|
-
|
619
|
-
function copy (source, target, targetStart, sourceStart, sourceEnd) {
|
620
|
-
if (targetStart < 0) {
|
621
|
-
sourceStart -= targetStart;
|
622
|
-
targetStart = 0;
|
623
|
-
}
|
624
|
-
|
625
|
-
if (sourceStart < 0) {
|
626
|
-
sourceStart = 0;
|
627
|
-
}
|
628
|
-
|
629
|
-
if (sourceEnd < 0) {
|
630
|
-
return new Uint8Array(0)
|
631
|
-
}
|
632
|
-
|
633
|
-
if (targetStart >= target.length || sourceStart >= sourceEnd) {
|
634
|
-
return 0
|
635
|
-
}
|
636
|
-
|
637
|
-
return _copyActual(source, target, targetStart, sourceStart, sourceEnd)
|
638
|
-
}
|
639
|
-
|
640
|
-
function _copyActual (source, target, targetStart, sourceStart, sourceEnd) {
|
641
|
-
if (sourceEnd - sourceStart > target.length - targetStart) {
|
642
|
-
sourceEnd = sourceStart + target.length - targetStart;
|
643
|
-
}
|
644
|
-
|
645
|
-
let nb = sourceEnd - sourceStart;
|
646
|
-
const sourceLen = source.length - sourceStart;
|
647
|
-
if (nb > sourceLen) {
|
648
|
-
nb = sourceLen;
|
649
|
-
}
|
650
|
-
|
651
|
-
if (sourceStart !== 0 || sourceEnd < source.length) {
|
652
|
-
source = new Uint8Array(source.buffer, source.byteOffset + sourceStart, nb);
|
653
|
-
}
|
654
|
-
|
655
|
-
target.set(source, targetStart);
|
656
|
-
|
657
|
-
return nb
|
658
|
-
}
|
659
|
-
|
660
|
-
const QUERY_FLAG = 0;
|
661
|
-
const RESPONSE_FLAG = 1 << 15;
|
662
|
-
const FLUSH_MASK = 1 << 15;
|
663
|
-
const NOT_FLUSH_MASK = ~FLUSH_MASK;
|
664
|
-
const QU_MASK = 1 << 15;
|
665
|
-
const NOT_QU_MASK = ~QU_MASK;
|
666
|
-
|
667
|
-
function codec ({ bytes = 0, encode, decode, encodingLength }) {
|
668
|
-
encode.bytes = bytes;
|
669
|
-
decode.bytes = bytes;
|
670
|
-
return {
|
671
|
-
encode,
|
672
|
-
decode,
|
673
|
-
encodingLength: encodingLength || (() => bytes)
|
674
|
-
}
|
675
|
-
}
|
676
|
-
|
677
|
-
const name = codec({
|
678
|
-
encode (str, buf, offset) {
|
679
|
-
if (!buf) buf = new Uint8Array(name.encodingLength(str));
|
680
|
-
if (!offset) offset = 0;
|
681
|
-
const oldOffset = offset;
|
682
|
-
|
683
|
-
// strip leading and trailing .
|
684
|
-
const n = str.replace(/^\.|\.$/gm, '');
|
685
|
-
if (n.length) {
|
686
|
-
const list = n.split('.');
|
687
|
-
|
688
|
-
for (let i = 0; i < list.length; i++) {
|
689
|
-
const len = write(buf, list[i], offset + 1);
|
690
|
-
buf[offset] = len;
|
691
|
-
offset += len + 1;
|
692
|
-
}
|
693
|
-
}
|
694
|
-
|
695
|
-
buf[offset++] = 0;
|
696
|
-
|
697
|
-
name.encode.bytes = offset - oldOffset;
|
698
|
-
return buf
|
699
|
-
},
|
700
|
-
decode (buf, offset) {
|
701
|
-
if (!offset) offset = 0;
|
702
|
-
|
703
|
-
const list = [];
|
704
|
-
let oldOffset = offset;
|
705
|
-
let totalLength = 0;
|
706
|
-
let consumedBytes = 0;
|
707
|
-
let jumped = false;
|
708
|
-
|
709
|
-
while (true) {
|
710
|
-
if (offset >= buf.length) {
|
711
|
-
throw new Error('Cannot decode name (buffer overflow)')
|
712
|
-
}
|
713
|
-
const len = buf[offset++];
|
714
|
-
consumedBytes += jumped ? 0 : 1;
|
715
|
-
|
716
|
-
if (len === 0) {
|
717
|
-
break
|
718
|
-
} else if ((len & 0xc0) === 0) {
|
719
|
-
if (offset + len > buf.length) {
|
720
|
-
throw new Error('Cannot decode name (buffer overflow)')
|
721
|
-
}
|
722
|
-
totalLength += len + 1;
|
723
|
-
if (totalLength > 254) {
|
724
|
-
throw new Error('Cannot decode name (name too long)')
|
725
|
-
}
|
726
|
-
list.push(decode$1(buf, offset, offset + len));
|
727
|
-
offset += len;
|
728
|
-
consumedBytes += jumped ? 0 : len;
|
729
|
-
} else if ((len & 0xc0) === 0xc0) {
|
730
|
-
if (offset + 1 > buf.length) {
|
731
|
-
throw new Error('Cannot decode name (buffer overflow)')
|
732
|
-
}
|
733
|
-
const jumpOffset = readUInt16BE(buf, offset - 1) - 0xc000;
|
734
|
-
if (jumpOffset >= oldOffset) {
|
735
|
-
// Allow only pointers to prior data. RFC 1035, section 4.1.4 states:
|
736
|
-
// "[...] an entire domain name or a list of labels at the end of a domain name
|
737
|
-
// is replaced with a pointer to a prior occurance (sic) of the same name."
|
738
|
-
throw new Error('Cannot decode name (bad pointer)')
|
739
|
-
}
|
740
|
-
offset = jumpOffset;
|
741
|
-
oldOffset = jumpOffset;
|
742
|
-
consumedBytes += jumped ? 0 : 1;
|
743
|
-
jumped = true;
|
744
|
-
} else {
|
745
|
-
throw new Error('Cannot decode name (bad label)')
|
746
|
-
}
|
747
|
-
}
|
748
|
-
|
749
|
-
name.decode.bytes = consumedBytes;
|
750
|
-
return list.length === 0 ? '.' : list.join('.')
|
751
|
-
},
|
752
|
-
encodingLength (n) {
|
753
|
-
if (n === '.' || n === '..') return 1
|
754
|
-
return bytelength(n.replace(/^\.|\.$/gm, '')) + 2
|
755
|
-
}
|
756
|
-
});
|
757
|
-
|
758
|
-
const string = codec({
|
759
|
-
encode (s, buf, offset) {
|
760
|
-
if (!buf) buf = new Uint8Array(string.encodingLength(s));
|
761
|
-
if (!offset) offset = 0;
|
762
|
-
|
763
|
-
const len = write(buf, s, offset + 1);
|
764
|
-
buf[offset] = len;
|
765
|
-
string.encode.bytes = len + 1;
|
766
|
-
return buf
|
767
|
-
},
|
768
|
-
decode (buf, offset) {
|
769
|
-
if (!offset) offset = 0;
|
770
|
-
|
771
|
-
const len = buf[offset];
|
772
|
-
const s = decode$1(buf, offset + 1, offset + 1 + len);
|
773
|
-
string.decode.bytes = len + 1;
|
774
|
-
return s
|
775
|
-
},
|
776
|
-
encodingLength (s) {
|
777
|
-
return bytelength(s) + 1
|
778
|
-
}
|
779
|
-
});
|
780
|
-
|
781
|
-
const header = codec({
|
782
|
-
bytes: 12,
|
783
|
-
encode (h, buf, offset) {
|
784
|
-
if (!buf) buf = new Uint8Array(header.encodingLength(h));
|
785
|
-
if (!offset) offset = 0;
|
786
|
-
|
787
|
-
const flags = (h.flags || 0) & 32767;
|
788
|
-
const type = h.type === 'response' ? RESPONSE_FLAG : QUERY_FLAG;
|
789
|
-
|
790
|
-
writeUInt16BE(buf, h.id || 0, offset);
|
791
|
-
writeUInt16BE(buf, flags | type, offset + 2);
|
792
|
-
writeUInt16BE(buf, h.questions.length, offset + 4);
|
793
|
-
writeUInt16BE(buf, h.answers.length, offset + 6);
|
794
|
-
writeUInt16BE(buf, h.authorities.length, offset + 8);
|
795
|
-
writeUInt16BE(buf, h.additionals.length, offset + 10);
|
796
|
-
|
797
|
-
return buf
|
798
|
-
},
|
799
|
-
decode (buf, offset) {
|
800
|
-
if (!offset) offset = 0;
|
801
|
-
if (buf.length < 12) throw new Error('Header must be 12 bytes')
|
802
|
-
const flags = readUInt16BE(buf, offset + 2);
|
803
|
-
|
804
|
-
return {
|
805
|
-
id: readUInt16BE(buf, offset),
|
806
|
-
type: flags & RESPONSE_FLAG ? 'response' : 'query',
|
807
|
-
flags: flags & 32767,
|
808
|
-
flag_qr: ((flags >> 15) & 0x1) === 1,
|
809
|
-
opcode: toString$2((flags >> 11) & 0xf),
|
810
|
-
flag_aa: ((flags >> 10) & 0x1) === 1,
|
811
|
-
flag_tc: ((flags >> 9) & 0x1) === 1,
|
812
|
-
flag_rd: ((flags >> 8) & 0x1) === 1,
|
813
|
-
flag_ra: ((flags >> 7) & 0x1) === 1,
|
814
|
-
flag_z: ((flags >> 6) & 0x1) === 1,
|
815
|
-
flag_ad: ((flags >> 5) & 0x1) === 1,
|
816
|
-
flag_cd: ((flags >> 4) & 0x1) === 1,
|
817
|
-
rcode: toString$3(flags & 0xf),
|
818
|
-
questions: new Array(readUInt16BE(buf, offset + 4)),
|
819
|
-
answers: new Array(readUInt16BE(buf, offset + 6)),
|
820
|
-
authorities: new Array(readUInt16BE(buf, offset + 8)),
|
821
|
-
additionals: new Array(readUInt16BE(buf, offset + 10))
|
822
|
-
}
|
823
|
-
},
|
824
|
-
encodingLength () {
|
825
|
-
return 12
|
826
|
-
}
|
827
|
-
});
|
828
|
-
|
829
|
-
const runknown = codec({
|
830
|
-
encode (data, buf, offset) {
|
831
|
-
if (!buf) buf = new Uint8Array(runknown.encodingLength(data));
|
832
|
-
if (!offset) offset = 0;
|
833
|
-
|
834
|
-
const dLen = data.length;
|
835
|
-
writeUInt16BE(buf, dLen, offset);
|
836
|
-
copy(data, buf, offset + 2, 0, dLen);
|
837
|
-
|
838
|
-
runknown.encode.bytes = dLen + 2;
|
839
|
-
return buf
|
840
|
-
},
|
841
|
-
decode (buf, offset) {
|
842
|
-
if (!offset) offset = 0;
|
843
|
-
|
844
|
-
const len = readUInt16BE(buf, offset);
|
845
|
-
const data = buf.slice(offset + 2, offset + 2 + len);
|
846
|
-
runknown.decode.bytes = len + 2;
|
847
|
-
return data
|
848
|
-
},
|
849
|
-
encodingLength (data) {
|
850
|
-
return data.length + 2
|
851
|
-
}
|
852
|
-
});
|
853
|
-
|
854
|
-
const rns = codec({
|
855
|
-
encode (data, buf, offset) {
|
856
|
-
if (!buf) buf = new Uint8Array(rns.encodingLength(data));
|
857
|
-
if (!offset) offset = 0;
|
858
|
-
|
859
|
-
name.encode(data, buf, offset + 2);
|
860
|
-
writeUInt16BE(buf, name.encode.bytes, offset);
|
861
|
-
rns.encode.bytes = name.encode.bytes + 2;
|
862
|
-
return buf
|
863
|
-
},
|
864
|
-
decode (buf, offset) {
|
865
|
-
if (!offset) offset = 0;
|
866
|
-
|
867
|
-
const len = readUInt16BE(buf, offset);
|
868
|
-
const dd = name.decode(buf, offset + 2);
|
869
|
-
|
870
|
-
rns.decode.bytes = len + 2;
|
871
|
-
return dd
|
872
|
-
},
|
873
|
-
encodingLength (data) {
|
874
|
-
return name.encodingLength(data) + 2
|
875
|
-
}
|
876
|
-
});
|
877
|
-
|
878
|
-
const rsoa = codec({
|
879
|
-
encode (data, buf, offset) {
|
880
|
-
if (!buf) buf = new Uint8Array(rsoa.encodingLength(data));
|
881
|
-
if (!offset) offset = 0;
|
882
|
-
|
883
|
-
const oldOffset = offset;
|
884
|
-
offset += 2;
|
885
|
-
name.encode(data.mname, buf, offset);
|
886
|
-
offset += name.encode.bytes;
|
887
|
-
name.encode(data.rname, buf, offset);
|
888
|
-
offset += name.encode.bytes;
|
889
|
-
writeUInt32BE(buf, data.serial || 0, offset);
|
890
|
-
offset += 4;
|
891
|
-
writeUInt32BE(buf, data.refresh || 0, offset);
|
892
|
-
offset += 4;
|
893
|
-
writeUInt32BE(buf, data.retry || 0, offset);
|
894
|
-
offset += 4;
|
895
|
-
writeUInt32BE(buf, data.expire || 0, offset);
|
896
|
-
offset += 4;
|
897
|
-
writeUInt32BE(buf, data.minimum || 0, offset);
|
898
|
-
offset += 4;
|
899
|
-
|
900
|
-
writeUInt16BE(buf, offset - oldOffset - 2, oldOffset);
|
901
|
-
rsoa.encode.bytes = offset - oldOffset;
|
902
|
-
return buf
|
903
|
-
},
|
904
|
-
decode (buf, offset) {
|
905
|
-
if (!offset) offset = 0;
|
906
|
-
|
907
|
-
const oldOffset = offset;
|
908
|
-
|
909
|
-
const data = {};
|
910
|
-
offset += 2;
|
911
|
-
data.mname = name.decode(buf, offset);
|
912
|
-
offset += name.decode.bytes;
|
913
|
-
data.rname = name.decode(buf, offset);
|
914
|
-
offset += name.decode.bytes;
|
915
|
-
data.serial = readUInt32BE(buf, offset);
|
916
|
-
offset += 4;
|
917
|
-
data.refresh = readUInt32BE(buf, offset);
|
918
|
-
offset += 4;
|
919
|
-
data.retry = readUInt32BE(buf, offset);
|
920
|
-
offset += 4;
|
921
|
-
data.expire = readUInt32BE(buf, offset);
|
922
|
-
offset += 4;
|
923
|
-
data.minimum = readUInt32BE(buf, offset);
|
924
|
-
offset += 4;
|
925
|
-
|
926
|
-
rsoa.decode.bytes = offset - oldOffset;
|
927
|
-
return data
|
928
|
-
},
|
929
|
-
encodingLength (data) {
|
930
|
-
return 22 + name.encodingLength(data.mname) + name.encodingLength(data.rname)
|
931
|
-
}
|
932
|
-
});
|
933
|
-
|
934
|
-
const rtxt = codec({
|
935
|
-
encode (data, buf, offset) {
|
936
|
-
if (!Array.isArray(data)) data = [data];
|
937
|
-
for (let i = 0; i < data.length; i++) {
|
938
|
-
if (typeof data[i] === 'string') {
|
939
|
-
data[i] = from(data[i]);
|
940
|
-
}
|
941
|
-
if (!isU8Arr(data[i])) {
|
942
|
-
throw new Error('Must be a Buffer')
|
943
|
-
}
|
944
|
-
}
|
945
|
-
|
946
|
-
if (!buf) buf = new Uint8Array(rtxt.encodingLength(data));
|
947
|
-
if (!offset) offset = 0;
|
948
|
-
|
949
|
-
const oldOffset = offset;
|
950
|
-
offset += 2;
|
951
|
-
|
952
|
-
data.forEach(function (d) {
|
953
|
-
buf[offset++] = d.length;
|
954
|
-
copy(d, buf, offset, 0, d.length);
|
955
|
-
offset += d.length;
|
956
|
-
});
|
957
|
-
|
958
|
-
writeUInt16BE(buf, offset - oldOffset - 2, oldOffset);
|
959
|
-
rtxt.encode.bytes = offset - oldOffset;
|
960
|
-
return buf
|
961
|
-
},
|
962
|
-
decode (buf, offset) {
|
963
|
-
if (!offset) offset = 0;
|
964
|
-
const oldOffset = offset;
|
965
|
-
let remaining = readUInt16BE(buf, offset);
|
966
|
-
offset += 2;
|
967
|
-
|
968
|
-
const data = [];
|
969
|
-
while (remaining > 0) {
|
970
|
-
const len = buf[offset++];
|
971
|
-
--remaining;
|
972
|
-
if (remaining < len) {
|
973
|
-
throw new Error('Buffer overflow')
|
974
|
-
}
|
975
|
-
data.push(buf.slice(offset, offset + len));
|
976
|
-
offset += len;
|
977
|
-
remaining -= len;
|
978
|
-
}
|
979
|
-
|
980
|
-
rtxt.decode.bytes = offset - oldOffset;
|
981
|
-
return data
|
982
|
-
},
|
983
|
-
encodingLength (data) {
|
984
|
-
if (!Array.isArray(data)) data = [data];
|
985
|
-
let length = 2;
|
986
|
-
data.forEach(function (buf) {
|
987
|
-
if (typeof buf === 'string') {
|
988
|
-
length += bytelength(buf) + 1;
|
989
|
-
} else {
|
990
|
-
length += buf.length + 1;
|
991
|
-
}
|
992
|
-
});
|
993
|
-
return length
|
994
|
-
}
|
995
|
-
});
|
996
|
-
|
997
|
-
const rnull = codec({
|
998
|
-
encode (data, buf, offset) {
|
999
|
-
if (!buf) buf = new Uint8Array(rnull.encodingLength(data));
|
1000
|
-
if (!offset) offset = 0;
|
1001
|
-
|
1002
|
-
if (typeof data === 'string') data = from(data);
|
1003
|
-
if (!data) data = new Uint8Array(0);
|
1004
|
-
|
1005
|
-
const oldOffset = offset;
|
1006
|
-
offset += 2;
|
1007
|
-
|
1008
|
-
const len = data.length;
|
1009
|
-
copy(data, buf, offset, 0, len);
|
1010
|
-
offset += len;
|
1011
|
-
|
1012
|
-
writeUInt16BE(buf, offset - oldOffset - 2, oldOffset);
|
1013
|
-
rnull.encode.bytes = offset - oldOffset;
|
1014
|
-
return buf
|
1015
|
-
},
|
1016
|
-
decode (buf, offset) {
|
1017
|
-
if (!offset) offset = 0;
|
1018
|
-
const oldOffset = offset;
|
1019
|
-
const len = readUInt16BE(buf, offset);
|
1020
|
-
|
1021
|
-
offset += 2;
|
1022
|
-
|
1023
|
-
const data = buf.slice(offset, offset + len);
|
1024
|
-
offset += len;
|
1025
|
-
|
1026
|
-
rnull.decode.bytes = offset - oldOffset;
|
1027
|
-
return data
|
1028
|
-
},
|
1029
|
-
encodingLength (data) {
|
1030
|
-
if (!data) return 2
|
1031
|
-
return (isU8Arr(data) ? data.length : bytelength(data)) + 2
|
1032
|
-
}
|
1033
|
-
});
|
1034
|
-
|
1035
|
-
const rhinfo = codec({
|
1036
|
-
encode (data, buf, offset) {
|
1037
|
-
if (!buf) buf = new Uint8Array(rhinfo.encodingLength(data));
|
1038
|
-
if (!offset) offset = 0;
|
1039
|
-
|
1040
|
-
const oldOffset = offset;
|
1041
|
-
offset += 2;
|
1042
|
-
string.encode(data.cpu, buf, offset);
|
1043
|
-
offset += string.encode.bytes;
|
1044
|
-
string.encode(data.os, buf, offset);
|
1045
|
-
offset += string.encode.bytes;
|
1046
|
-
writeUInt16BE(buf, offset - oldOffset - 2, oldOffset);
|
1047
|
-
rhinfo.encode.bytes = offset - oldOffset;
|
1048
|
-
return buf
|
1049
|
-
},
|
1050
|
-
decode (buf, offset) {
|
1051
|
-
if (!offset) offset = 0;
|
1052
|
-
|
1053
|
-
const oldOffset = offset;
|
1054
|
-
|
1055
|
-
const data = {};
|
1056
|
-
offset += 2;
|
1057
|
-
data.cpu = string.decode(buf, offset);
|
1058
|
-
offset += string.decode.bytes;
|
1059
|
-
data.os = string.decode(buf, offset);
|
1060
|
-
offset += string.decode.bytes;
|
1061
|
-
rhinfo.decode.bytes = offset - oldOffset;
|
1062
|
-
return data
|
1063
|
-
},
|
1064
|
-
encodingLength (data) {
|
1065
|
-
return string.encodingLength(data.cpu) + string.encodingLength(data.os) + 2
|
1066
|
-
}
|
1067
|
-
});
|
1068
|
-
|
1069
|
-
const rptr = codec({
|
1070
|
-
encode (data, buf, offset) {
|
1071
|
-
if (!buf) buf = new Uint8Array(rptr.encodingLength(data));
|
1072
|
-
if (!offset) offset = 0;
|
1073
|
-
|
1074
|
-
name.encode(data, buf, offset + 2);
|
1075
|
-
writeUInt16BE(buf, name.encode.bytes, offset);
|
1076
|
-
rptr.encode.bytes = name.encode.bytes + 2;
|
1077
|
-
return buf
|
1078
|
-
},
|
1079
|
-
decode (buf, offset) {
|
1080
|
-
if (!offset) offset = 0;
|
1081
|
-
|
1082
|
-
const data = name.decode(buf, offset + 2);
|
1083
|
-
rptr.decode.bytes = name.decode.bytes + 2;
|
1084
|
-
return data
|
1085
|
-
},
|
1086
|
-
encodingLength (data) {
|
1087
|
-
return name.encodingLength(data) + 2
|
1088
|
-
}
|
1089
|
-
});
|
1090
|
-
|
1091
|
-
const rsrv = codec({
|
1092
|
-
encode (data, buf, offset) {
|
1093
|
-
if (!buf) buf = new Uint8Array(rsrv.encodingLength(data));
|
1094
|
-
if (!offset) offset = 0;
|
1095
|
-
|
1096
|
-
writeUInt16BE(buf, data.priority || 0, offset + 2);
|
1097
|
-
writeUInt16BE(buf, data.weight || 0, offset + 4);
|
1098
|
-
writeUInt16BE(buf, data.port || 0, offset + 6);
|
1099
|
-
name.encode(data.target, buf, offset + 8);
|
1100
|
-
|
1101
|
-
const len = name.encode.bytes + 6;
|
1102
|
-
writeUInt16BE(buf, len, offset);
|
1103
|
-
|
1104
|
-
rsrv.encode.bytes = len + 2;
|
1105
|
-
return buf
|
1106
|
-
},
|
1107
|
-
decode (buf, offset) {
|
1108
|
-
if (!offset) offset = 0;
|
1109
|
-
|
1110
|
-
const len = readUInt16BE(buf, offset);
|
1111
|
-
|
1112
|
-
const data = {};
|
1113
|
-
data.priority = readUInt16BE(buf, offset + 2);
|
1114
|
-
data.weight = readUInt16BE(buf, offset + 4);
|
1115
|
-
data.port = readUInt16BE(buf, offset + 6);
|
1116
|
-
data.target = name.decode(buf, offset + 8);
|
1117
|
-
|
1118
|
-
rsrv.decode.bytes = len + 2;
|
1119
|
-
return data
|
1120
|
-
},
|
1121
|
-
encodingLength (data) {
|
1122
|
-
return 8 + name.encodingLength(data.target)
|
1123
|
-
}
|
1124
|
-
});
|
1125
|
-
|
1126
|
-
const rcaa = codec({
|
1127
|
-
encode (data, buf, offset) {
|
1128
|
-
const len = rcaa.encodingLength(data);
|
1129
|
-
|
1130
|
-
if (!buf) buf = new Uint8Array(rcaa.encodingLength(data));
|
1131
|
-
if (!offset) offset = 0;
|
1132
|
-
|
1133
|
-
if (data.issuerCritical) {
|
1134
|
-
data.flags = rcaa.ISSUER_CRITICAL;
|
1135
|
-
}
|
1136
|
-
|
1137
|
-
writeUInt16BE(buf, len - 2, offset);
|
1138
|
-
offset += 2;
|
1139
|
-
buf[offset] = data.flags || 0;
|
1140
|
-
offset += 1;
|
1141
|
-
string.encode(data.tag, buf, offset);
|
1142
|
-
offset += string.encode.bytes;
|
1143
|
-
write(buf, data.value, offset);
|
1144
|
-
offset += bytelength(data.value);
|
1145
|
-
|
1146
|
-
rcaa.encode.bytes = len;
|
1147
|
-
return buf
|
1148
|
-
},
|
1149
|
-
decode (buf, offset) {
|
1150
|
-
if (!offset) offset = 0;
|
1151
|
-
|
1152
|
-
const len = readUInt16BE(buf, offset);
|
1153
|
-
offset += 2;
|
1154
|
-
|
1155
|
-
const oldOffset = offset;
|
1156
|
-
const data = {};
|
1157
|
-
data.flags = buf[offset];
|
1158
|
-
offset += 1;
|
1159
|
-
data.tag = string.decode(buf, offset);
|
1160
|
-
offset += string.decode.bytes;
|
1161
|
-
data.value = decode$1(buf, offset, oldOffset + len);
|
1162
|
-
|
1163
|
-
data.issuerCritical = !!(data.flags & rcaa.ISSUER_CRITICAL);
|
1164
|
-
|
1165
|
-
rcaa.decode.bytes = len + 2;
|
1166
|
-
|
1167
|
-
return data
|
1168
|
-
},
|
1169
|
-
encodingLength (data) {
|
1170
|
-
return string.encodingLength(data.tag) + string.encodingLength(data.value) + 2
|
1171
|
-
}
|
1172
|
-
});
|
1173
|
-
|
1174
|
-
rcaa.ISSUER_CRITICAL = 1 << 7;
|
1175
|
-
|
1176
|
-
const rmx = codec({
|
1177
|
-
encode (data, buf, offset) {
|
1178
|
-
if (!buf) buf = new Uint8Array(rmx.encodingLength(data));
|
1179
|
-
if (!offset) offset = 0;
|
1180
|
-
|
1181
|
-
const oldOffset = offset;
|
1182
|
-
offset += 2;
|
1183
|
-
writeUInt16BE(buf, data.preference || 0, offset);
|
1184
|
-
offset += 2;
|
1185
|
-
name.encode(data.exchange, buf, offset);
|
1186
|
-
offset += name.encode.bytes;
|
1187
|
-
|
1188
|
-
writeUInt16BE(buf, offset - oldOffset - 2, oldOffset);
|
1189
|
-
rmx.encode.bytes = offset - oldOffset;
|
1190
|
-
return buf
|
1191
|
-
},
|
1192
|
-
decode (buf, offset) {
|
1193
|
-
if (!offset) offset = 0;
|
1194
|
-
|
1195
|
-
const oldOffset = offset;
|
1196
|
-
|
1197
|
-
const data = {};
|
1198
|
-
offset += 2;
|
1199
|
-
data.preference = readUInt16BE(buf, offset);
|
1200
|
-
offset += 2;
|
1201
|
-
data.exchange = name.decode(buf, offset);
|
1202
|
-
offset += name.decode.bytes;
|
1203
|
-
|
1204
|
-
rmx.decode.bytes = offset - oldOffset;
|
1205
|
-
return data
|
1206
|
-
},
|
1207
|
-
encodingLength (data) {
|
1208
|
-
return 4 + name.encodingLength(data.exchange)
|
1209
|
-
}
|
1210
|
-
});
|
1211
|
-
|
1212
|
-
const ra = codec({
|
1213
|
-
encode (host, buf, offset) {
|
1214
|
-
if (!buf) buf = new Uint8Array(ra.encodingLength(host));
|
1215
|
-
if (!offset) offset = 0;
|
1216
|
-
|
1217
|
-
writeUInt16BE(buf, 4, offset);
|
1218
|
-
offset += 2;
|
1219
|
-
v4.encode(host, buf, offset);
|
1220
|
-
return buf
|
1221
|
-
},
|
1222
|
-
decode (buf, offset) {
|
1223
|
-
if (!offset) offset = 0;
|
1224
|
-
|
1225
|
-
offset += 2;
|
1226
|
-
const host = v4.decode(buf, offset);
|
1227
|
-
return host
|
1228
|
-
},
|
1229
|
-
bytes: 6
|
1230
|
-
});
|
1231
|
-
|
1232
|
-
const raaaa = codec({
|
1233
|
-
encode (host, buf, offset) {
|
1234
|
-
if (!buf) buf = new Uint8Array(raaaa.encodingLength(host));
|
1235
|
-
if (!offset) offset = 0;
|
1236
|
-
|
1237
|
-
writeUInt16BE(buf, 16, offset);
|
1238
|
-
offset += 2;
|
1239
|
-
v6.encode(host, buf, offset);
|
1240
|
-
raaaa.encode.bytes = 18;
|
1241
|
-
return buf
|
1242
|
-
},
|
1243
|
-
decode (buf, offset) {
|
1244
|
-
if (!offset) offset = 0;
|
1245
|
-
|
1246
|
-
offset += 2;
|
1247
|
-
const host = v6.decode(buf, offset);
|
1248
|
-
raaaa.decode.bytes = 18;
|
1249
|
-
return host
|
1250
|
-
},
|
1251
|
-
bytes: 18
|
1252
|
-
});
|
1253
|
-
|
1254
|
-
const alloc = size => new Uint8Array(size);
|
1255
|
-
|
1256
|
-
const roption = codec({
|
1257
|
-
encode (option, buf, offset) {
|
1258
|
-
if (!buf) buf = new Uint8Array(roption.encodingLength(option));
|
1259
|
-
if (!offset) offset = 0;
|
1260
|
-
const oldOffset = offset;
|
1261
|
-
|
1262
|
-
const code = toCode(option.code);
|
1263
|
-
writeUInt16BE(buf, code, offset);
|
1264
|
-
offset += 2;
|
1265
|
-
if (option.data) {
|
1266
|
-
writeUInt16BE(buf, option.data.length, offset);
|
1267
|
-
offset += 2;
|
1268
|
-
copy(option.data, buf, offset);
|
1269
|
-
offset += option.data.length;
|
1270
|
-
} else {
|
1271
|
-
switch (code) {
|
1272
|
-
// case 3: NSID. No encode makes sense.
|
1273
|
-
// case 5,6,7: Not implementable
|
1274
|
-
case 8: // ECS
|
1275
|
-
{
|
1276
|
-
// note: do IP math before calling
|
1277
|
-
const spl = option.sourcePrefixLength || 0;
|
1278
|
-
const fam = option.family || familyOf(option.ip);
|
1279
|
-
const ipBuf = encode$2(option.ip, alloc);
|
1280
|
-
const ipLen = Math.ceil(spl / 8);
|
1281
|
-
writeUInt16BE(buf, ipLen + 4, offset);
|
1282
|
-
offset += 2;
|
1283
|
-
writeUInt16BE(buf, fam, offset);
|
1284
|
-
offset += 2;
|
1285
|
-
buf[offset++] = spl;
|
1286
|
-
buf[offset++] = option.scopePrefixLength || 0;
|
1287
|
-
|
1288
|
-
copy(ipBuf, buf, offset, 0, ipLen);
|
1289
|
-
offset += ipLen;
|
1290
|
-
}
|
1291
|
-
break
|
1292
|
-
// case 9: EXPIRE (experimental)
|
1293
|
-
// case 10: COOKIE. No encode makes sense.
|
1294
|
-
case 11: // KEEP-ALIVE
|
1295
|
-
if (option.timeout) {
|
1296
|
-
writeUInt16BE(buf, 2, offset);
|
1297
|
-
offset += 2;
|
1298
|
-
writeUInt16BE(buf, option.timeout, offset);
|
1299
|
-
offset += 2;
|
1300
|
-
} else {
|
1301
|
-
writeUInt16BE(buf, 0, offset);
|
1302
|
-
offset += 2;
|
1303
|
-
}
|
1304
|
-
break
|
1305
|
-
case 12: // PADDING
|
1306
|
-
{
|
1307
|
-
const len = option.length || 0;
|
1308
|
-
writeUInt16BE(buf, len, offset);
|
1309
|
-
offset += 2;
|
1310
|
-
buf.fill(0, offset, offset + len);
|
1311
|
-
offset += len;
|
1312
|
-
}
|
1313
|
-
break
|
1314
|
-
// case 13: CHAIN. Experimental.
|
1315
|
-
case 14: // KEY-TAG
|
1316
|
-
{
|
1317
|
-
const tagsLen = option.tags.length * 2;
|
1318
|
-
writeUInt16BE(buf, tagsLen, offset);
|
1319
|
-
offset += 2;
|
1320
|
-
for (const tag of option.tags) {
|
1321
|
-
writeUInt16BE(buf, tag, offset);
|
1322
|
-
offset += 2;
|
1323
|
-
}
|
1324
|
-
}
|
1325
|
-
break
|
1326
|
-
default:
|
1327
|
-
throw new Error(`Unknown roption code: ${option.code}`)
|
1328
|
-
}
|
1329
|
-
}
|
1330
|
-
|
1331
|
-
roption.encode.bytes = offset - oldOffset;
|
1332
|
-
return buf
|
1333
|
-
},
|
1334
|
-
decode (buf, offset) {
|
1335
|
-
if (!offset) offset = 0;
|
1336
|
-
const option = {};
|
1337
|
-
option.code = readUInt16BE(buf, offset);
|
1338
|
-
option.type = toString(option.code);
|
1339
|
-
offset += 2;
|
1340
|
-
const len = readUInt16BE(buf, offset);
|
1341
|
-
offset += 2;
|
1342
|
-
option.data = buf.slice(offset, offset + len);
|
1343
|
-
switch (option.code) {
|
1344
|
-
// case 3: NSID. No decode makes sense.
|
1345
|
-
case 8: // ECS
|
1346
|
-
option.family = readUInt16BE(buf, offset);
|
1347
|
-
offset += 2;
|
1348
|
-
option.sourcePrefixLength = buf[offset++];
|
1349
|
-
option.scopePrefixLength = buf[offset++];
|
1350
|
-
{
|
1351
|
-
const padded = new Uint8Array((option.family === 1) ? 4 : 16);
|
1352
|
-
copy(buf, padded, 0, offset, offset + len - 4);
|
1353
|
-
option.ip = decode$2(padded);
|
1354
|
-
}
|
1355
|
-
break
|
1356
|
-
// case 12: Padding. No decode makes sense.
|
1357
|
-
case 11: // KEEP-ALIVE
|
1358
|
-
if (len > 0) {
|
1359
|
-
option.timeout = readUInt16BE(buf, offset);
|
1360
|
-
offset += 2;
|
1361
|
-
}
|
1362
|
-
break
|
1363
|
-
case 14:
|
1364
|
-
option.tags = [];
|
1365
|
-
for (let i = 0; i < len; i += 2) {
|
1366
|
-
option.tags.push(readUInt16BE(buf, offset));
|
1367
|
-
offset += 2;
|
1368
|
-
}
|
1369
|
-
// don't worry about default. caller will use data if desired
|
1370
|
-
}
|
1371
|
-
|
1372
|
-
roption.decode.bytes = len + 4;
|
1373
|
-
return option
|
1374
|
-
},
|
1375
|
-
encodingLength (option) {
|
1376
|
-
if (option.data) {
|
1377
|
-
return option.data.length + 4
|
1378
|
-
}
|
1379
|
-
const code = toCode(option.code);
|
1380
|
-
switch (code) {
|
1381
|
-
case 8: // ECS
|
1382
|
-
{
|
1383
|
-
const spl = option.sourcePrefixLength || 0;
|
1384
|
-
return Math.ceil(spl / 8) + 8
|
1385
|
-
}
|
1386
|
-
case 11: // KEEP-ALIVE
|
1387
|
-
return (typeof option.timeout === 'number') ? 6 : 4
|
1388
|
-
case 12: // PADDING
|
1389
|
-
return option.length + 4
|
1390
|
-
case 14: // KEY-TAG
|
1391
|
-
return 4 + (option.tags.length * 2)
|
1392
|
-
}
|
1393
|
-
throw new Error(`Unknown roption code: ${option.code}`)
|
1394
|
-
}
|
1395
|
-
});
|
1396
|
-
|
1397
|
-
const ropt = codec({
|
1398
|
-
encode (options, buf, offset) {
|
1399
|
-
if (!buf) buf = new Uint8Array(ropt.encodingLength(options));
|
1400
|
-
if (!offset) offset = 0;
|
1401
|
-
const oldOffset = offset;
|
1402
|
-
|
1403
|
-
const rdlen = encodingLengthList(options, roption);
|
1404
|
-
writeUInt16BE(buf, rdlen, offset);
|
1405
|
-
offset = encodeList(options, roption, buf, offset + 2);
|
1406
|
-
|
1407
|
-
ropt.encode.bytes = offset - oldOffset;
|
1408
|
-
return buf
|
1409
|
-
},
|
1410
|
-
decode (buf, offset) {
|
1411
|
-
if (!offset) offset = 0;
|
1412
|
-
const oldOffset = offset;
|
1413
|
-
|
1414
|
-
const options = [];
|
1415
|
-
let rdlen = readUInt16BE(buf, offset);
|
1416
|
-
offset += 2;
|
1417
|
-
let o = 0;
|
1418
|
-
while (rdlen > 0) {
|
1419
|
-
options[o++] = roption.decode(buf, offset);
|
1420
|
-
offset += roption.decode.bytes;
|
1421
|
-
rdlen -= roption.decode.bytes;
|
1422
|
-
}
|
1423
|
-
ropt.decode.bytes = offset - oldOffset;
|
1424
|
-
return options
|
1425
|
-
},
|
1426
|
-
encodingLength (options) {
|
1427
|
-
return 2 + encodingLengthList(options || [], roption)
|
1428
|
-
}
|
1429
|
-
});
|
1430
|
-
|
1431
|
-
const rdnskey = codec({
|
1432
|
-
encode (key, buf, offset) {
|
1433
|
-
if (!buf) buf = new Uint8Array(rdnskey.encodingLength(key));
|
1434
|
-
if (!offset) offset = 0;
|
1435
|
-
const oldOffset = offset;
|
1436
|
-
|
1437
|
-
const keydata = key.key;
|
1438
|
-
if (!isU8Arr(keydata)) {
|
1439
|
-
throw new Error('Key must be a Buffer')
|
1440
|
-
}
|
1441
|
-
|
1442
|
-
offset += 2; // Leave space for length
|
1443
|
-
writeUInt16BE(buf, key.flags, offset);
|
1444
|
-
offset += 2;
|
1445
|
-
buf[offset] = rdnskey.PROTOCOL_DNSSEC;
|
1446
|
-
offset += 1;
|
1447
|
-
buf[offset] = key.algorithm;
|
1448
|
-
offset += 1;
|
1449
|
-
copy(keydata, buf, offset, 0, keydata.length);
|
1450
|
-
offset += keydata.length;
|
1451
|
-
|
1452
|
-
rdnskey.encode.bytes = offset - oldOffset;
|
1453
|
-
writeUInt16BE(buf, rdnskey.encode.bytes - 2, oldOffset);
|
1454
|
-
return buf
|
1455
|
-
},
|
1456
|
-
decode (buf, offset) {
|
1457
|
-
if (!offset) offset = 0;
|
1458
|
-
const oldOffset = offset;
|
1459
|
-
|
1460
|
-
const key = {};
|
1461
|
-
const length = readUInt16BE(buf, offset);
|
1462
|
-
offset += 2;
|
1463
|
-
key.flags = readUInt16BE(buf, offset);
|
1464
|
-
offset += 2;
|
1465
|
-
if (buf[offset] !== rdnskey.PROTOCOL_DNSSEC) {
|
1466
|
-
throw new Error('Protocol must be 3')
|
1467
|
-
}
|
1468
|
-
offset += 1;
|
1469
|
-
key.algorithm = buf[offset];
|
1470
|
-
offset += 1;
|
1471
|
-
key.key = buf.slice(offset, oldOffset + length + 2);
|
1472
|
-
offset += key.key.length;
|
1473
|
-
rdnskey.decode.bytes = offset - oldOffset;
|
1474
|
-
return key
|
1475
|
-
},
|
1476
|
-
encodingLength (key) {
|
1477
|
-
return 6 + bytelength(key.key)
|
1478
|
-
}
|
1479
|
-
});
|
1480
|
-
|
1481
|
-
rdnskey.PROTOCOL_DNSSEC = 3;
|
1482
|
-
rdnskey.ZONE_KEY = 0x80;
|
1483
|
-
rdnskey.SECURE_ENTRYPOINT = 0x8000;
|
1484
|
-
|
1485
|
-
const rrrsig = codec({
|
1486
|
-
encode (sig, buf, offset) {
|
1487
|
-
if (!buf) buf = new Uint8Array(rrrsig.encodingLength(sig));
|
1488
|
-
if (!offset) offset = 0;
|
1489
|
-
const oldOffset = offset;
|
1490
|
-
|
1491
|
-
const signature = sig.signature;
|
1492
|
-
if (!isU8Arr(signature)) {
|
1493
|
-
throw new Error('Signature must be a Buffer')
|
1494
|
-
}
|
1495
|
-
|
1496
|
-
offset += 2; // Leave space for length
|
1497
|
-
writeUInt16BE(buf, toType(sig.typeCovered), offset);
|
1498
|
-
offset += 2;
|
1499
|
-
buf[offset] = sig.algorithm;
|
1500
|
-
offset += 1;
|
1501
|
-
buf[offset] = sig.labels;
|
1502
|
-
offset += 1;
|
1503
|
-
writeUInt32BE(buf, sig.originalTTL, offset);
|
1504
|
-
offset += 4;
|
1505
|
-
writeUInt32BE(buf, sig.expiration, offset);
|
1506
|
-
offset += 4;
|
1507
|
-
writeUInt32BE(buf, sig.inception, offset);
|
1508
|
-
offset += 4;
|
1509
|
-
writeUInt16BE(buf, sig.keyTag, offset);
|
1510
|
-
offset += 2;
|
1511
|
-
name.encode(sig.signersName, buf, offset);
|
1512
|
-
offset += name.encode.bytes;
|
1513
|
-
copy(signature, buf, offset, 0, signature.length);
|
1514
|
-
offset += signature.length;
|
1515
|
-
|
1516
|
-
rrrsig.encode.bytes = offset - oldOffset;
|
1517
|
-
writeUInt16BE(buf, rrrsig.encode.bytes - 2, oldOffset);
|
1518
|
-
return buf
|
1519
|
-
},
|
1520
|
-
decode (buf, offset) {
|
1521
|
-
if (!offset) offset = 0;
|
1522
|
-
const oldOffset = offset;
|
1523
|
-
|
1524
|
-
const sig = {};
|
1525
|
-
const length = readUInt16BE(buf, offset);
|
1526
|
-
offset += 2;
|
1527
|
-
sig.typeCovered = toString$4(readUInt16BE(buf, offset));
|
1528
|
-
offset += 2;
|
1529
|
-
sig.algorithm = buf[offset];
|
1530
|
-
offset += 1;
|
1531
|
-
sig.labels = buf[offset];
|
1532
|
-
offset += 1;
|
1533
|
-
sig.originalTTL = readUInt32BE(buf, offset);
|
1534
|
-
offset += 4;
|
1535
|
-
sig.expiration = readUInt32BE(buf, offset);
|
1536
|
-
offset += 4;
|
1537
|
-
sig.inception = readUInt32BE(buf, offset);
|
1538
|
-
offset += 4;
|
1539
|
-
sig.keyTag = readUInt16BE(buf, offset);
|
1540
|
-
offset += 2;
|
1541
|
-
sig.signersName = name.decode(buf, offset);
|
1542
|
-
offset += name.decode.bytes;
|
1543
|
-
sig.signature = buf.slice(offset, oldOffset + length + 2);
|
1544
|
-
offset += sig.signature.length;
|
1545
|
-
rrrsig.decode.bytes = offset - oldOffset;
|
1546
|
-
return sig
|
1547
|
-
},
|
1548
|
-
encodingLength (sig) {
|
1549
|
-
return 20 +
|
1550
|
-
name.encodingLength(sig.signersName) +
|
1551
|
-
bytelength(sig.signature)
|
1552
|
-
}
|
1553
|
-
});
|
1554
|
-
const rrp = codec({
|
1555
|
-
encode (data, buf, offset) {
|
1556
|
-
if (!buf) buf = new Uint8Array(rrp.encodingLength(data));
|
1557
|
-
if (!offset) offset = 0;
|
1558
|
-
const oldOffset = offset;
|
1559
|
-
|
1560
|
-
offset += 2; // Leave space for length
|
1561
|
-
name.encode(data.mbox || '.', buf, offset);
|
1562
|
-
offset += name.encode.bytes;
|
1563
|
-
name.encode(data.txt || '.', buf, offset);
|
1564
|
-
offset += name.encode.bytes;
|
1565
|
-
rrp.encode.bytes = offset - oldOffset;
|
1566
|
-
writeUInt16BE(buf, rrp.encode.bytes - 2, oldOffset);
|
1567
|
-
return buf
|
1568
|
-
},
|
1569
|
-
decode (buf, offset) {
|
1570
|
-
if (!offset) offset = 0;
|
1571
|
-
const oldOffset = offset;
|
1572
|
-
|
1573
|
-
const data = {};
|
1574
|
-
offset += 2;
|
1575
|
-
data.mbox = name.decode(buf, offset) || '.';
|
1576
|
-
offset += name.decode.bytes;
|
1577
|
-
data.txt = name.decode(buf, offset) || '.';
|
1578
|
-
offset += name.decode.bytes;
|
1579
|
-
rrp.decode.bytes = offset - oldOffset;
|
1580
|
-
return data
|
1581
|
-
},
|
1582
|
-
encodingLength (data) {
|
1583
|
-
return 2 + name.encodingLength(data.mbox || '.') + name.encodingLength(data.txt || '.')
|
1584
|
-
}
|
1585
|
-
});
|
1586
|
-
|
1587
|
-
const typebitmap = codec({
|
1588
|
-
encode (typelist, buf, offset) {
|
1589
|
-
if (!buf) buf = new Uint8Array(typebitmap.encodingLength(typelist));
|
1590
|
-
if (!offset) offset = 0;
|
1591
|
-
const oldOffset = offset;
|
1592
|
-
|
1593
|
-
const typesByWindow = [];
|
1594
|
-
for (let i = 0; i < typelist.length; i++) {
|
1595
|
-
const typeid = toType(typelist[i]);
|
1596
|
-
if (typesByWindow[typeid >> 8] === undefined) {
|
1597
|
-
typesByWindow[typeid >> 8] = [];
|
1598
|
-
}
|
1599
|
-
typesByWindow[typeid >> 8][(typeid >> 3) & 0x1F] |= 1 << (7 - (typeid & 0x7));
|
1600
|
-
}
|
1601
|
-
|
1602
|
-
for (let i = 0; i < typesByWindow.length; i++) {
|
1603
|
-
if (typesByWindow[i] !== undefined) {
|
1604
|
-
const windowBuf = from(typesByWindow[i]);
|
1605
|
-
buf[offset] = i;
|
1606
|
-
offset += 1;
|
1607
|
-
buf[offset] = windowBuf.length;
|
1608
|
-
offset += 1;
|
1609
|
-
copy(windowBuf, buf, offset, 0, windowBuf.length);
|
1610
|
-
offset += windowBuf.length;
|
1611
|
-
}
|
1612
|
-
}
|
1613
|
-
|
1614
|
-
typebitmap.encode.bytes = offset - oldOffset;
|
1615
|
-
return buf
|
1616
|
-
},
|
1617
|
-
decode (buf, offset, length) {
|
1618
|
-
if (!offset) offset = 0;
|
1619
|
-
const oldOffset = offset;
|
1620
|
-
|
1621
|
-
const typelist = [];
|
1622
|
-
while (offset - oldOffset < length) {
|
1623
|
-
const window = buf[offset];
|
1624
|
-
offset += 1;
|
1625
|
-
const windowLength = buf[offset];
|
1626
|
-
offset += 1;
|
1627
|
-
for (let i = 0; i < windowLength; i++) {
|
1628
|
-
const b = buf[offset + i];
|
1629
|
-
for (let j = 0; j < 8; j++) {
|
1630
|
-
if (b & (1 << (7 - j))) {
|
1631
|
-
const typeid = toString$4((window << 8) | (i << 3) | j);
|
1632
|
-
typelist.push(typeid);
|
1633
|
-
}
|
1634
|
-
}
|
1635
|
-
}
|
1636
|
-
offset += windowLength;
|
1637
|
-
}
|
1638
|
-
|
1639
|
-
typebitmap.decode.bytes = offset - oldOffset;
|
1640
|
-
return typelist
|
1641
|
-
},
|
1642
|
-
encodingLength (typelist) {
|
1643
|
-
const extents = [];
|
1644
|
-
for (let i = 0; i < typelist.length; i++) {
|
1645
|
-
const typeid = toType(typelist[i]);
|
1646
|
-
extents[typeid >> 8] = Math.max(extents[typeid >> 8] || 0, typeid & 0xFF);
|
1647
|
-
}
|
1648
|
-
|
1649
|
-
let len = 0;
|
1650
|
-
for (let i = 0; i < extents.length; i++) {
|
1651
|
-
if (extents[i] !== undefined) {
|
1652
|
-
len += 2 + Math.ceil((extents[i] + 1) / 8);
|
1653
|
-
}
|
1654
|
-
}
|
1655
|
-
|
1656
|
-
return len
|
1657
|
-
}
|
1658
|
-
});
|
1659
|
-
|
1660
|
-
const rnsec = codec({
|
1661
|
-
encode (record, buf, offset) {
|
1662
|
-
if (!buf) buf = new Uint8Array(rnsec.encodingLength(record));
|
1663
|
-
if (!offset) offset = 0;
|
1664
|
-
const oldOffset = offset;
|
1665
|
-
|
1666
|
-
offset += 2; // Leave space for length
|
1667
|
-
name.encode(record.nextDomain, buf, offset);
|
1668
|
-
offset += name.encode.bytes;
|
1669
|
-
typebitmap.encode(record.rrtypes, buf, offset);
|
1670
|
-
offset += typebitmap.encode.bytes;
|
1671
|
-
|
1672
|
-
rnsec.encode.bytes = offset - oldOffset;
|
1673
|
-
writeUInt16BE(buf, rnsec.encode.bytes - 2, oldOffset);
|
1674
|
-
return buf
|
1675
|
-
},
|
1676
|
-
decode (buf, offset) {
|
1677
|
-
if (!offset) offset = 0;
|
1678
|
-
const oldOffset = offset;
|
1679
|
-
|
1680
|
-
const record = {};
|
1681
|
-
const length = readUInt16BE(buf, offset);
|
1682
|
-
offset += 2;
|
1683
|
-
record.nextDomain = name.decode(buf, offset);
|
1684
|
-
offset += name.decode.bytes;
|
1685
|
-
record.rrtypes = typebitmap.decode(buf, offset, length - (offset - oldOffset));
|
1686
|
-
offset += typebitmap.decode.bytes;
|
1687
|
-
|
1688
|
-
rnsec.decode.bytes = offset - oldOffset;
|
1689
|
-
return record
|
1690
|
-
},
|
1691
|
-
encodingLength (record) {
|
1692
|
-
return 2 +
|
1693
|
-
name.encodingLength(record.nextDomain) +
|
1694
|
-
typebitmap.encodingLength(record.rrtypes)
|
1695
|
-
}
|
1696
|
-
});
|
1697
|
-
|
1698
|
-
const rnsec3 = codec({
|
1699
|
-
encode (record, buf, offset) {
|
1700
|
-
if (!buf) buf = new Uint8Array(rnsec3.encodingLength(record));
|
1701
|
-
if (!offset) offset = 0;
|
1702
|
-
const oldOffset = offset;
|
1703
|
-
|
1704
|
-
const salt = record.salt;
|
1705
|
-
if (!isU8Arr(salt)) {
|
1706
|
-
throw new Error('salt must be a Buffer')
|
1707
|
-
}
|
1708
|
-
|
1709
|
-
const nextDomain = record.nextDomain;
|
1710
|
-
if (!isU8Arr(nextDomain)) {
|
1711
|
-
throw new Error('nextDomain must be a Buffer')
|
1712
|
-
}
|
1713
|
-
|
1714
|
-
offset += 2; // Leave space for length
|
1715
|
-
buf[offset] = record.algorithm;
|
1716
|
-
offset += 1;
|
1717
|
-
buf[offset] = record.flags;
|
1718
|
-
offset += 1;
|
1719
|
-
writeUInt16BE(buf, record.iterations, offset);
|
1720
|
-
offset += 2;
|
1721
|
-
buf[offset] = salt.length;
|
1722
|
-
offset += 1;
|
1723
|
-
copy(salt, buf, offset, 0, salt.length);
|
1724
|
-
offset += salt.length;
|
1725
|
-
buf[offset] = nextDomain.length;
|
1726
|
-
offset += 1;
|
1727
|
-
copy(nextDomain, buf, offset, 0, nextDomain.length);
|
1728
|
-
offset += nextDomain.length;
|
1729
|
-
typebitmap.encode(record.rrtypes, buf, offset);
|
1730
|
-
offset += typebitmap.encode.bytes;
|
1731
|
-
|
1732
|
-
rnsec3.encode.bytes = offset - oldOffset;
|
1733
|
-
writeUInt16BE(buf, rnsec3.encode.bytes - 2, oldOffset);
|
1734
|
-
return buf
|
1735
|
-
},
|
1736
|
-
decode (buf, offset) {
|
1737
|
-
if (!offset) offset = 0;
|
1738
|
-
const oldOffset = offset;
|
1739
|
-
|
1740
|
-
const record = {};
|
1741
|
-
const length = readUInt16BE(buf, offset);
|
1742
|
-
offset += 2;
|
1743
|
-
record.algorithm = buf[offset];
|
1744
|
-
offset += 1;
|
1745
|
-
record.flags = buf[offset];
|
1746
|
-
offset += 1;
|
1747
|
-
record.iterations = readUInt16BE(buf, offset);
|
1748
|
-
offset += 2;
|
1749
|
-
const saltLength = buf[offset];
|
1750
|
-
offset += 1;
|
1751
|
-
record.salt = buf.slice(offset, offset + saltLength);
|
1752
|
-
offset += saltLength;
|
1753
|
-
const hashLength = buf[offset];
|
1754
|
-
offset += 1;
|
1755
|
-
record.nextDomain = buf.slice(offset, offset + hashLength);
|
1756
|
-
offset += hashLength;
|
1757
|
-
record.rrtypes = typebitmap.decode(buf, offset, length - (offset - oldOffset));
|
1758
|
-
offset += typebitmap.decode.bytes;
|
1759
|
-
|
1760
|
-
rnsec3.decode.bytes = offset - oldOffset;
|
1761
|
-
return record
|
1762
|
-
},
|
1763
|
-
encodingLength (record) {
|
1764
|
-
return 8 +
|
1765
|
-
record.salt.length +
|
1766
|
-
record.nextDomain.length +
|
1767
|
-
typebitmap.encodingLength(record.rrtypes)
|
1768
|
-
}
|
1769
|
-
});
|
1770
|
-
|
1771
|
-
const rds = codec({
|
1772
|
-
encode (digest, buf, offset) {
|
1773
|
-
if (!buf) buf = new Uint8Array(rds.encodingLength(digest));
|
1774
|
-
if (!offset) offset = 0;
|
1775
|
-
const oldOffset = offset;
|
1776
|
-
|
1777
|
-
const digestdata = digest.digest;
|
1778
|
-
if (!isU8Arr(digestdata)) {
|
1779
|
-
throw new Error('Digest must be a Buffer')
|
1780
|
-
}
|
1781
|
-
|
1782
|
-
offset += 2; // Leave space for length
|
1783
|
-
writeUInt16BE(buf, digest.keyTag, offset);
|
1784
|
-
offset += 2;
|
1785
|
-
buf[offset] = digest.algorithm;
|
1786
|
-
offset += 1;
|
1787
|
-
buf[offset] = digest.digestType;
|
1788
|
-
offset += 1;
|
1789
|
-
copy(digestdata, buf, offset, 0, digestdata.length);
|
1790
|
-
offset += digestdata.length;
|
1791
|
-
|
1792
|
-
rds.encode.bytes = offset - oldOffset;
|
1793
|
-
writeUInt16BE(buf, rds.encode.bytes - 2, oldOffset);
|
1794
|
-
return buf
|
1795
|
-
},
|
1796
|
-
decode (buf, offset) {
|
1797
|
-
if (!offset) offset = 0;
|
1798
|
-
const oldOffset = offset;
|
1799
|
-
|
1800
|
-
const digest = {};
|
1801
|
-
const length = readUInt16BE(buf, offset);
|
1802
|
-
offset += 2;
|
1803
|
-
digest.keyTag = readUInt16BE(buf, offset);
|
1804
|
-
offset += 2;
|
1805
|
-
digest.algorithm = buf[offset];
|
1806
|
-
offset += 1;
|
1807
|
-
digest.digestType = buf[offset];
|
1808
|
-
offset += 1;
|
1809
|
-
digest.digest = buf.slice(offset, oldOffset + length + 2);
|
1810
|
-
offset += digest.digest.length;
|
1811
|
-
rds.decode.bytes = offset - oldOffset;
|
1812
|
-
return digest
|
1813
|
-
},
|
1814
|
-
encodingLength (digest) {
|
1815
|
-
return 6 + bytelength(digest.digest)
|
1816
|
-
}
|
1817
|
-
});
|
1818
|
-
|
1819
|
-
function renc (type) {
|
1820
|
-
switch (type.toUpperCase()) {
|
1821
|
-
case 'A': return ra
|
1822
|
-
case 'PTR': return rptr
|
1823
|
-
case 'CNAME': return rptr
|
1824
|
-
case 'DNAME': return rptr
|
1825
|
-
case 'TXT': return rtxt
|
1826
|
-
case 'NULL': return rnull
|
1827
|
-
case 'AAAA': return raaaa
|
1828
|
-
case 'SRV': return rsrv
|
1829
|
-
case 'HINFO': return rhinfo
|
1830
|
-
case 'CAA': return rcaa
|
1831
|
-
case 'NS': return rns
|
1832
|
-
case 'SOA': return rsoa
|
1833
|
-
case 'MX': return rmx
|
1834
|
-
case 'OPT': return ropt
|
1835
|
-
case 'DNSKEY': return rdnskey
|
1836
|
-
case 'RRSIG': return rrrsig
|
1837
|
-
case 'RP': return rrp
|
1838
|
-
case 'NSEC': return rnsec
|
1839
|
-
case 'NSEC3': return rnsec3
|
1840
|
-
case 'DS': return rds
|
1841
|
-
}
|
1842
|
-
return runknown
|
1843
|
-
}
|
1844
|
-
|
1845
|
-
const answer = codec({
|
1846
|
-
encode (a, buf, offset) {
|
1847
|
-
if (!buf) buf = new Uint8Array(answer.encodingLength(a));
|
1848
|
-
if (!offset) offset = 0;
|
1849
|
-
|
1850
|
-
const oldOffset = offset;
|
1851
|
-
|
1852
|
-
name.encode(a.name, buf, offset);
|
1853
|
-
offset += name.encode.bytes;
|
1854
|
-
|
1855
|
-
writeUInt16BE(buf, toType(a.type), offset);
|
1856
|
-
|
1857
|
-
if (a.type.toUpperCase() === 'OPT') {
|
1858
|
-
if (a.name !== '.') {
|
1859
|
-
throw new Error('OPT name must be root.')
|
1860
|
-
}
|
1861
|
-
writeUInt16BE(buf, a.udpPayloadSize || 4096, offset + 2);
|
1862
|
-
buf[offset + 4] = a.extendedRcode || 0;
|
1863
|
-
buf[offset + 5] = a.ednsVersion || 0;
|
1864
|
-
writeUInt16BE(buf, a.flags || 0, offset + 6);
|
1865
|
-
|
1866
|
-
offset += 8;
|
1867
|
-
ropt.encode(a.options || [], buf, offset);
|
1868
|
-
offset += ropt.encode.bytes;
|
1869
|
-
} else {
|
1870
|
-
let klass = toClass(a.class === undefined ? 'IN' : a.class);
|
1871
|
-
if (a.flush) klass |= FLUSH_MASK; // the 1st bit of the class is the flush bit
|
1872
|
-
writeUInt16BE(buf, klass, offset + 2);
|
1873
|
-
writeUInt32BE(buf, a.ttl || 0, offset + 4);
|
1874
|
-
|
1875
|
-
offset += 8;
|
1876
|
-
const enc = renc(a.type);
|
1877
|
-
enc.encode(a.data, buf, offset);
|
1878
|
-
offset += enc.encode.bytes;
|
1879
|
-
}
|
1880
|
-
|
1881
|
-
answer.encode.bytes = offset - oldOffset;
|
1882
|
-
return buf
|
1883
|
-
},
|
1884
|
-
decode (buf, offset) {
|
1885
|
-
if (!offset) offset = 0;
|
1886
|
-
|
1887
|
-
const a = {};
|
1888
|
-
const oldOffset = offset;
|
1889
|
-
|
1890
|
-
a.name = name.decode(buf, offset);
|
1891
|
-
offset += name.decode.bytes;
|
1892
|
-
a.type = toString$4(readUInt16BE(buf, offset));
|
1893
|
-
if (a.type === 'OPT') {
|
1894
|
-
a.udpPayloadSize = readUInt16BE(buf, offset + 2);
|
1895
|
-
a.extendedRcode = buf[offset + 4];
|
1896
|
-
a.ednsVersion = buf[offset + 5];
|
1897
|
-
a.flags = readUInt16BE(buf, offset + 6);
|
1898
|
-
a.flag_do = ((a.flags >> 15) & 0x1) === 1;
|
1899
|
-
a.options = ropt.decode(buf, offset + 8);
|
1900
|
-
offset += 8 + ropt.decode.bytes;
|
1901
|
-
} else {
|
1902
|
-
const klass = readUInt16BE(buf, offset + 2);
|
1903
|
-
a.ttl = readUInt32BE(buf, offset + 4);
|
1904
|
-
|
1905
|
-
a.class = toString$1(klass & NOT_FLUSH_MASK);
|
1906
|
-
a.flush = !!(klass & FLUSH_MASK);
|
1907
|
-
|
1908
|
-
const enc = renc(a.type);
|
1909
|
-
a.data = enc.decode(buf, offset + 8);
|
1910
|
-
offset += 8 + enc.decode.bytes;
|
1911
|
-
}
|
1912
|
-
|
1913
|
-
answer.decode.bytes = offset - oldOffset;
|
1914
|
-
return a
|
1915
|
-
},
|
1916
|
-
encodingLength (a) {
|
1917
|
-
const data = (a.data !== null && a.data !== undefined) ? a.data : a.options;
|
1918
|
-
return name.encodingLength(a.name) + 8 + renc(a.type).encodingLength(data)
|
1919
|
-
}
|
1920
|
-
});
|
1921
|
-
|
1922
|
-
const question = codec({
|
1923
|
-
encode (q, buf, offset) {
|
1924
|
-
if (!buf) buf = new Uint8Array(question.encodingLength(q));
|
1925
|
-
if (!offset) offset = 0;
|
1926
|
-
|
1927
|
-
const oldOffset = offset;
|
1928
|
-
|
1929
|
-
name.encode(q.name, buf, offset);
|
1930
|
-
offset += name.encode.bytes;
|
1931
|
-
|
1932
|
-
writeUInt16BE(buf, toType(q.type), offset);
|
1933
|
-
offset += 2;
|
1934
|
-
|
1935
|
-
writeUInt16BE(buf, toClass(q.class === undefined ? 'IN' : q.class), offset);
|
1936
|
-
offset += 2;
|
1937
|
-
|
1938
|
-
question.encode.bytes = offset - oldOffset;
|
1939
|
-
return q
|
1940
|
-
},
|
1941
|
-
decode (buf, offset) {
|
1942
|
-
if (!offset) offset = 0;
|
1943
|
-
|
1944
|
-
const oldOffset = offset;
|
1945
|
-
const q = {};
|
1946
|
-
|
1947
|
-
q.name = name.decode(buf, offset);
|
1948
|
-
offset += name.decode.bytes;
|
1949
|
-
|
1950
|
-
q.type = toString$4(readUInt16BE(buf, offset));
|
1951
|
-
offset += 2;
|
1952
|
-
|
1953
|
-
q.class = toString$1(readUInt16BE(buf, offset));
|
1954
|
-
offset += 2;
|
1955
|
-
|
1956
|
-
const qu = !!(q.class & QU_MASK);
|
1957
|
-
if (qu) q.class &= NOT_QU_MASK;
|
1958
|
-
|
1959
|
-
question.decode.bytes = offset - oldOffset;
|
1960
|
-
return q
|
1961
|
-
},
|
1962
|
-
encodingLength (q) {
|
1963
|
-
return name.encodingLength(q.name) + 4
|
1964
|
-
}
|
1965
|
-
});
|
1966
|
-
const RECURSION_DESIRED = 1 << 8;
|
1967
|
-
|
1968
|
-
const packet = {
|
1969
|
-
encode: function (result, buf, offset) {
|
1970
|
-
const allocing = !buf;
|
1971
|
-
|
1972
|
-
if (allocing) buf = new Uint8Array(encodingLength(result));
|
1973
|
-
if (!offset) offset = 0;
|
1974
|
-
|
1975
|
-
const oldOffset = offset;
|
1976
|
-
|
1977
|
-
if (!result.questions) result.questions = [];
|
1978
|
-
if (!result.answers) result.answers = [];
|
1979
|
-
if (!result.authorities) result.authorities = [];
|
1980
|
-
if (!result.additionals) result.additionals = [];
|
1981
|
-
|
1982
|
-
header.encode(result, buf, offset);
|
1983
|
-
offset += header.encode.bytes;
|
1984
|
-
|
1985
|
-
offset = encodeList(result.questions, question, buf, offset);
|
1986
|
-
offset = encodeList(result.answers, answer, buf, offset);
|
1987
|
-
offset = encodeList(result.authorities, answer, buf, offset);
|
1988
|
-
offset = encodeList(result.additionals, answer, buf, offset);
|
1989
|
-
|
1990
|
-
packet.encode.bytes = offset - oldOffset;
|
1991
|
-
|
1992
|
-
// just a quick sanity check
|
1993
|
-
if (allocing && encode.bytes !== buf.length) {
|
1994
|
-
return buf.slice(0, encode.bytes)
|
1995
|
-
}
|
1996
|
-
|
1997
|
-
return buf
|
1998
|
-
},
|
1999
|
-
decode: function (buf, offset) {
|
2000
|
-
if (!offset) offset = 0;
|
2001
|
-
|
2002
|
-
const oldOffset = offset;
|
2003
|
-
const result = header.decode(buf, offset);
|
2004
|
-
offset += header.decode.bytes;
|
2005
|
-
|
2006
|
-
offset = decodeList(result.questions, question, buf, offset);
|
2007
|
-
offset = decodeList(result.answers, answer, buf, offset);
|
2008
|
-
offset = decodeList(result.authorities, answer, buf, offset);
|
2009
|
-
offset = decodeList(result.additionals, answer, buf, offset);
|
2010
|
-
|
2011
|
-
packet.decode.bytes = offset - oldOffset;
|
2012
|
-
|
2013
|
-
return result
|
2014
|
-
},
|
2015
|
-
encodingLength: function (result) {
|
2016
|
-
return header.encodingLength(result) +
|
2017
|
-
encodingLengthList(result.questions || [], question) +
|
2018
|
-
encodingLengthList(result.answers || [], answer) +
|
2019
|
-
encodingLengthList(result.authorities || [], answer) +
|
2020
|
-
encodingLengthList(result.additionals || [], answer)
|
2021
|
-
}
|
2022
|
-
};
|
2023
|
-
packet.encode.bytes = 0;
|
2024
|
-
packet.decode.bytes = 0;
|
2025
|
-
|
2026
|
-
const encode = packet.encode;
|
2027
|
-
const decode = packet.decode;
|
2028
|
-
const encodingLength = packet.encodingLength;
|
2029
|
-
|
2030
|
-
function encodingLengthList (list, enc) {
|
2031
|
-
let len = 0;
|
2032
|
-
for (let i = 0; i < list.length; i++) len += enc.encodingLength(list[i]);
|
2033
|
-
return len
|
2034
|
-
}
|
2035
|
-
|
2036
|
-
function encodeList (list, enc, buf, offset) {
|
2037
|
-
for (let i = 0; i < list.length; i++) {
|
2038
|
-
enc.encode(list[i], buf, offset);
|
2039
|
-
offset += enc.encode.bytes;
|
2040
|
-
}
|
2041
|
-
return offset
|
2042
|
-
}
|
2043
|
-
|
2044
|
-
function decodeList (list, enc, buf, offset) {
|
2045
|
-
for (let i = 0; i < list.length; i++) {
|
2046
|
-
list[i] = enc.decode(buf, offset);
|
2047
|
-
offset += enc.decode.bytes;
|
2048
|
-
}
|
2049
|
-
return offset
|
2050
|
-
}
|
2051
|
-
|
2052
|
-
const PREFERS_PADDING = 1;
|
2053
|
-
const PREFERS_NO_PADDING = 2;
|
2054
|
-
|
2055
|
-
function make (name, charset, padding, paddingMode) {
|
2056
|
-
if (charset.length !== 64) {
|
2057
|
-
throw new Error(`Charset needs to be 64 characters long! (${charset.length})`)
|
2058
|
-
}
|
2059
|
-
const byCharCode = new Uint8Array(256);
|
2060
|
-
const byNum = new Uint8Array(64);
|
2061
|
-
for (let i = 0; i < 64; i += 1) {
|
2062
|
-
const code = charset.charCodeAt(i);
|
2063
|
-
if (code > 255) {
|
2064
|
-
throw new Error(`Character #${i} in charset [code=${code}, char=${charset.charAt(i)}] is too high! (max=255)`)
|
2065
|
-
}
|
2066
|
-
if (byCharCode[code] !== 0) {
|
2067
|
-
throw new Error(`Character [code=${code}, char=${charset.charAt(i)}] is more than once in the charset!`)
|
2068
|
-
}
|
2069
|
-
byCharCode[code] = i;
|
2070
|
-
byNum[i] = code;
|
2071
|
-
}
|
2072
|
-
const padCode = padding.charCodeAt(0);
|
2073
|
-
const codec = {
|
2074
|
-
name,
|
2075
|
-
encodingLength (str) {
|
2076
|
-
const strLen = str.length;
|
2077
|
-
const len = strLen * 0.75 | 0;
|
2078
|
-
if (str.charCodeAt(strLen - 1) === padCode) {
|
2079
|
-
if (str.charCodeAt(strLen - 2) === padCode) {
|
2080
|
-
return len - 2
|
2081
|
-
}
|
2082
|
-
return len - 1
|
2083
|
-
}
|
2084
|
-
return len
|
2085
|
-
},
|
2086
|
-
encode (str, buffer, offset) {
|
2087
|
-
if (buffer === null || buffer === undefined) {
|
2088
|
-
buffer = new Uint8Array(codec.encodingLength(str));
|
2089
|
-
}
|
2090
|
-
if (offset === null || offset === undefined) {
|
2091
|
-
offset = 0;
|
2092
|
-
}
|
2093
|
-
|
2094
|
-
let strLen = str.length;
|
2095
|
-
if (str.charCodeAt(strLen - 1) === padCode) {
|
2096
|
-
if (str.charCodeAt(strLen - 2) === padCode) {
|
2097
|
-
strLen -= 2;
|
2098
|
-
} else {
|
2099
|
-
strLen -= 1;
|
2100
|
-
}
|
2101
|
-
}
|
2102
|
-
|
2103
|
-
const padding = strLen % 4;
|
2104
|
-
const safeLen = strLen - padding;
|
2105
|
-
|
2106
|
-
let off = offset;
|
2107
|
-
let i = 0;
|
2108
|
-
while (i < safeLen) {
|
2109
|
-
const code =
|
2110
|
-
(byCharCode[str.charCodeAt(i)] << 18) |
|
2111
|
-
(byCharCode[str.charCodeAt(i + 1)] << 12) |
|
2112
|
-
(byCharCode[str.charCodeAt(i + 2)] << 6) |
|
2113
|
-
byCharCode[str.charCodeAt(i + 3)];
|
2114
|
-
buffer[off++] = code >> 16;
|
2115
|
-
buffer[off++] = code >> 8;
|
2116
|
-
buffer[off++] = code;
|
2117
|
-
i += 4;
|
2118
|
-
}
|
2119
|
-
|
2120
|
-
if (padding === 3) {
|
2121
|
-
const code =
|
2122
|
-
(byCharCode[str.charCodeAt(i)] << 10) |
|
2123
|
-
(byCharCode[str.charCodeAt(i + 1)] << 4) |
|
2124
|
-
(byCharCode[str.charCodeAt(i + 2)] >> 2);
|
2125
|
-
buffer[off++] = code >> 8;
|
2126
|
-
buffer[off++] = code;
|
2127
|
-
} else if (padding === 2) {
|
2128
|
-
buffer[off++] = (byCharCode[str.charCodeAt(i)] << 2) |
|
2129
|
-
(byCharCode[str.charCodeAt(i + 1)] >> 4);
|
2130
|
-
}
|
2131
|
-
|
2132
|
-
codec.encode.bytes = off - offset;
|
2133
|
-
return buffer
|
2134
|
-
},
|
2135
|
-
decode (buffer, start, end) {
|
2136
|
-
if (start === null || start === undefined) {
|
2137
|
-
start = 0;
|
2138
|
-
}
|
2139
|
-
if (end === null || end === undefined) {
|
2140
|
-
end = buffer.length;
|
2141
|
-
}
|
2142
|
-
|
2143
|
-
const length = end - start;
|
2144
|
-
const pad = length % 3;
|
2145
|
-
const safeEnd = start + length - pad;
|
2146
|
-
const codes = [];
|
2147
|
-
for (let off = start; off < safeEnd; off += 3) {
|
2148
|
-
const num = (buffer[off] << 16) | ((buffer[off + 1] << 8)) | buffer[off + 2];
|
2149
|
-
codes.push(
|
2150
|
-
byNum[num >> 18 & 0x3F],
|
2151
|
-
byNum[num >> 12 & 0x3F],
|
2152
|
-
byNum[num >> 6 & 0x3F],
|
2153
|
-
byNum[num & 0x3F]
|
2154
|
-
);
|
2155
|
-
}
|
2156
|
-
|
2157
|
-
if (pad === 2) {
|
2158
|
-
const num = (buffer[end - 2] << 8) + buffer[end - 1];
|
2159
|
-
codes.push(
|
2160
|
-
byNum[num >> 10],
|
2161
|
-
byNum[(num >> 4) & 0x3F],
|
2162
|
-
byNum[(num << 2) & 0x3F]
|
2163
|
-
);
|
2164
|
-
if (paddingMode === PREFERS_PADDING) {
|
2165
|
-
codes.push(padCode);
|
2166
|
-
}
|
2167
|
-
} else if (pad === 1) {
|
2168
|
-
const num = buffer[end - 1];
|
2169
|
-
codes.push(
|
2170
|
-
byNum[num >> 2],
|
2171
|
-
byNum[(num << 4) & 0x3F]
|
2172
|
-
);
|
2173
|
-
if (paddingMode === PREFERS_PADDING) {
|
2174
|
-
codes.push(padCode, padCode);
|
2175
|
-
}
|
2176
|
-
}
|
2177
|
-
|
2178
|
-
codec.decode.bytes = length;
|
2179
|
-
return String.fromCharCode.apply(String, codes)
|
2180
|
-
}
|
2181
|
-
};
|
2182
|
-
return codec
|
2183
|
-
}
|
2184
|
-
|
2185
|
-
make('base64', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', '=', PREFERS_PADDING);
|
2186
|
-
// https://datatracker.ietf.org/doc/html/rfc4648#section-5
|
2187
|
-
const base64URL = make('base64-url', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_', '=', PREFERS_NO_PADDING);
|
2188
|
-
|
2189
|
-
let AbortError = typeof global !== 'undefined' ? global.AbortError : typeof window !== 'undefined' ? window.AbortError : null;
|
2190
|
-
if (!AbortError) {
|
2191
|
-
AbortError = class AbortError extends Error {
|
2192
|
-
constructor (message = 'Request aborted.') {
|
2193
|
-
super(message);
|
2194
|
-
}
|
2195
|
-
};
|
2196
|
-
}
|
2197
|
-
AbortError.prototype.name = 'AbortError';
|
2198
|
-
AbortError.prototype.code = 'ABORT_ERR';
|
2199
|
-
|
2200
|
-
const URL = (typeof globalThis !== 'undefined' && globalThis.URL) || require('url').URL;
|
2201
|
-
|
2202
|
-
class HTTPStatusError extends Error {
|
2203
|
-
constructor (uri, code, method) {
|
2204
|
-
super('status=' + code + ' while requesting ' + uri + ' [' + method + ']');
|
2205
|
-
this.uri = uri;
|
2206
|
-
this.status = code;
|
2207
|
-
this.method = method;
|
2208
|
-
}
|
2209
|
-
|
2210
|
-
toJSON () {
|
2211
|
-
return {
|
2212
|
-
code: this.code,
|
2213
|
-
uri: this.uri,
|
2214
|
-
status: this.status,
|
2215
|
-
method: this.method,
|
2216
|
-
endpoint: this.endpoint
|
2217
|
-
}
|
2218
|
-
}
|
2219
|
-
}
|
2220
|
-
HTTPStatusError.prototype.name = 'HTTPStatusError';
|
2221
|
-
HTTPStatusError.prototype.code = 'HTTP_STATUS';
|
2222
|
-
|
2223
|
-
class ResponseError extends Error {
|
2224
|
-
constructor (message, cause) {
|
2225
|
-
super(message);
|
2226
|
-
this.cause = cause;
|
2227
|
-
}
|
2228
|
-
|
2229
|
-
toJSON () {
|
2230
|
-
return {
|
2231
|
-
message: this.message,
|
2232
|
-
endpoint: this.endpoint,
|
2233
|
-
code: this.code,
|
2234
|
-
cause: reduceError(this.cause)
|
2235
|
-
}
|
2236
|
-
}
|
2237
|
-
}
|
2238
|
-
ResponseError.prototype.name = 'ResponseError';
|
2239
|
-
ResponseError.prototype.code = 'RESPONSE_ERR';
|
2240
|
-
|
2241
|
-
class TimeoutError extends Error {
|
2242
|
-
constructor (timeout) {
|
2243
|
-
super('Timeout (t=' + timeout + ').');
|
2244
|
-
this.timeout = timeout;
|
2245
|
-
}
|
2246
|
-
|
2247
|
-
toJSON () {
|
2248
|
-
return {
|
2249
|
-
code: this.code,
|
2250
|
-
endpoint: this.endpoint,
|
2251
|
-
timeout: this.timeout
|
2252
|
-
}
|
2253
|
-
}
|
2254
|
-
}
|
2255
|
-
TimeoutError.prototype.name = 'TimeoutError';
|
2256
|
-
TimeoutError.prototype.code = 'ETIMEOUT';
|
2257
|
-
|
2258
|
-
const v4Regex = /^((\d{1,3}\.){3,3}\d{1,3})(:(\d{2,5}))?$/;
|
2259
|
-
const v6Regex = /^((::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?)(:(\d{2,5}))?$/i;
|
2260
|
-
|
2261
|
-
function reduceError (err) {
|
2262
|
-
if (typeof err === 'string') {
|
2263
|
-
return {
|
2264
|
-
message: err
|
2265
|
-
}
|
2266
|
-
}
|
2267
|
-
try {
|
2268
|
-
const json = JSON.stringify(err);
|
2269
|
-
if (json !== '{}') {
|
2270
|
-
return JSON.parse(json)
|
2271
|
-
}
|
2272
|
-
} catch (e) {}
|
2273
|
-
const error = {
|
2274
|
-
message: String(err.message || err)
|
2275
|
-
};
|
2276
|
-
if (err.code !== undefined) {
|
2277
|
-
error.code = String(err.code);
|
2278
|
-
}
|
2279
|
-
return error
|
2280
|
-
}
|
2281
|
-
|
2282
|
-
const baseParts = /^(([a-z0-9]+:)\/\/)?([^/[\s:]+|\[[^\]]+\])?(:([^/\s]+))?(\/[^\s]*)?(.*)$/;
|
2283
|
-
const httpFlags = /\[(post|get|((ipv4|ipv6|name)=([^\]]+)))\]/ig;
|
2284
|
-
const updFlags = /\[(((pk|name)=([^\]]+)))\]/ig;
|
2285
|
-
|
2286
|
-
function parseEndpoint (endpoint) {
|
2287
|
-
const parts = baseParts.exec(endpoint);
|
2288
|
-
const protocol = parts[2] || 'https:';
|
2289
|
-
const host = parts[3];
|
2290
|
-
const port = parts[5];
|
2291
|
-
const path = parts[6];
|
2292
|
-
const rest = parts[7];
|
2293
|
-
if (protocol === 'https:' || protocol === 'http:') {
|
2294
|
-
const flags = parseFlags(rest, httpFlags);
|
2295
|
-
return {
|
2296
|
-
name: flags.name,
|
2297
|
-
protocol,
|
2298
|
-
ipv4: flags.ipv4,
|
2299
|
-
ipv6: flags.ipv6,
|
2300
|
-
host,
|
2301
|
-
port,
|
2302
|
-
path,
|
2303
|
-
method: flags.post ? 'POST' : 'GET'
|
2304
|
-
}
|
2305
|
-
}
|
2306
|
-
if (protocol === 'udp:' || protocol === 'udp4:' || protocol === 'udp6:') {
|
2307
|
-
const flags = parseFlags(rest, updFlags);
|
2308
|
-
const v6Parts = /^\[(.*)\]$/.exec(host);
|
2309
|
-
if (v6Parts && protocol === 'udp4:') {
|
2310
|
-
throw new Error(`Endpoint parsing error: Cannot use ipv6 host with udp4: (endpoint=${endpoint})`)
|
2311
|
-
}
|
2312
|
-
if (!v6Parts && protocol === 'udp6:') {
|
2313
|
-
throw new Error(`Endpoint parsing error: Incorrectly formatted host for udp6: (endpoint=${endpoint})`)
|
2314
|
-
}
|
2315
|
-
if (v6Parts) {
|
2316
|
-
return new UDP6Endpoint({ protocol: 'udp6:', ipv6: v6Parts[1], port, pk: flags.pk, name: flags.name })
|
2317
|
-
}
|
2318
|
-
return new UDP4Endpoint({ protocol: 'udp4:', ipv4: host, port, pk: flags.pk, name: flags.name })
|
2319
|
-
}
|
2320
|
-
throw new InvalidProtocolError(protocol, endpoint)
|
2321
|
-
}
|
2322
|
-
|
2323
|
-
function parseFlags (rest, regex) {
|
2324
|
-
regex.lastIndex = 0;
|
2325
|
-
const result = {};
|
2326
|
-
while (true) {
|
2327
|
-
const match = regex.exec(rest);
|
2328
|
-
if (!match) break
|
2329
|
-
if (match[2]) {
|
2330
|
-
result[match[3].toLowerCase()] = match[4];
|
2331
|
-
} else {
|
2332
|
-
result[match[1].toLowerCase()] = true;
|
2333
|
-
}
|
2334
|
-
}
|
2335
|
-
return result
|
2336
|
-
}
|
2337
|
-
|
2338
|
-
class InvalidProtocolError extends Error {
|
2339
|
-
constructor (protocol, endpoint) {
|
2340
|
-
super(`Invalid Endpoint: unsupported protocol "${protocol}" for endpoint: ${endpoint}, supported protocols: ${supportedProtocols.join(', ')}`);
|
2341
|
-
this.protocol = protocol;
|
2342
|
-
this.endpoint = endpoint;
|
2343
|
-
}
|
2344
|
-
|
2345
|
-
toJSON () {
|
2346
|
-
return {
|
2347
|
-
code: this.code,
|
2348
|
-
endpoint: this.endpoint,
|
2349
|
-
timeout: this.timeout
|
2350
|
-
}
|
2351
|
-
}
|
2352
|
-
}
|
2353
|
-
InvalidProtocolError.prototype.name = 'InvalidProtocolError';
|
2354
|
-
InvalidProtocolError.prototype.code = 'EPROTOCOL';
|
2355
|
-
|
2356
|
-
const supportedProtocols = ['http:', 'https:', 'udp4:', 'udp6:'];
|
2357
|
-
|
2358
|
-
class BaseEndpoint {
|
2359
|
-
constructor (opts, isHTTP) {
|
2360
|
-
this.name = opts.name || null;
|
2361
|
-
this.protocol = opts.protocol;
|
2362
|
-
const port = typeof opts.port === 'string' ? opts.port = parseInt(opts.port, 10) : opts.port;
|
2363
|
-
if (port === undefined || port === null) {
|
2364
|
-
this.port = isHTTP
|
2365
|
-
? (this.protocol === 'https:' ? 443 : 80)
|
2366
|
-
: (opts.pk ? 443 : 53);
|
2367
|
-
} else if (typeof port !== 'number' && !isNaN(port)) {
|
2368
|
-
throw new Error(`Invalid Endpoint: port "${opts.port}" needs to be a number: ${JSON.stringify(opts)}`)
|
2369
|
-
} else {
|
2370
|
-
this.port = port;
|
2371
|
-
}
|
2372
|
-
}
|
2373
|
-
|
2374
|
-
toJSON () {
|
2375
|
-
return this.toString()
|
2376
|
-
}
|
2377
|
-
}
|
2378
|
-
|
2379
|
-
class UDPEndpoint extends BaseEndpoint {
|
2380
|
-
constructor (opts) {
|
2381
|
-
super(opts, false);
|
2382
|
-
this.pk = opts.pk || null;
|
2383
|
-
}
|
2384
|
-
|
2385
|
-
toString () {
|
2386
|
-
const port = this.port !== (this.pk ? 443 : 53) ? `:${this.port}` : '';
|
2387
|
-
const pk = this.pk ? ` [pk=${this.pk}]` : '';
|
2388
|
-
const name = this.name ? ` [name=${this.name}]` : '';
|
2389
|
-
return `udp://${this.ipv4 || `[${this.ipv6}]`}${port}${pk}${name}`
|
2390
|
-
}
|
2391
|
-
}
|
2392
|
-
|
2393
|
-
class UDP4Endpoint extends UDPEndpoint {
|
2394
|
-
constructor (opts) {
|
2395
|
-
super(Object.assign({ protocol: 'udp4:' }, opts));
|
2396
|
-
if (!opts.ipv4 || typeof opts.ipv4 !== 'string') {
|
2397
|
-
throw new Error(`Invalid Endpoint: .ipv4 "${opts.ipv4}" needs to be set: ${JSON.stringify(opts)}`)
|
2398
|
-
}
|
2399
|
-
this.ipv4 = opts.ipv4;
|
2400
|
-
}
|
2401
|
-
}
|
2402
|
-
|
2403
|
-
class UDP6Endpoint extends UDPEndpoint {
|
2404
|
-
constructor (opts) {
|
2405
|
-
super(Object.assign({ protocol: 'udp6:' }, opts));
|
2406
|
-
if (!opts.ipv6 || typeof opts.ipv6 !== 'string') {
|
2407
|
-
throw new Error(`Invalid Endpoint: .ipv6 "${opts.ipv6}" needs to be set: ${JSON.stringify(opts)}`)
|
2408
|
-
}
|
2409
|
-
this.ipv6 = opts.ipv6;
|
2410
|
-
}
|
2411
|
-
}
|
2412
|
-
|
2413
|
-
function safeHost (host) {
|
2414
|
-
return v6Regex.test(host) && !v4Regex.test(host) ? `[${host}]` : host
|
2415
|
-
}
|
2416
|
-
|
2417
|
-
class HTTPEndpoint extends BaseEndpoint {
|
2418
|
-
constructor (opts) {
|
2419
|
-
super(Object.assign({ protocol: 'https:' }, opts), true);
|
2420
|
-
if (!opts.host) {
|
2421
|
-
if (opts.ipv4) {
|
2422
|
-
opts.host = opts.ipv4;
|
2423
|
-
}
|
2424
|
-
if (opts.ipv6) {
|
2425
|
-
opts.host = `[${opts.ipv6}]`;
|
2426
|
-
}
|
2427
|
-
}
|
2428
|
-
if (!opts.host || typeof opts.host !== 'string') {
|
2429
|
-
throw new Error(`Invalid Endpoint: host "${opts.path}" needs to be set: ${JSON.stringify(opts)}`)
|
2430
|
-
}
|
2431
|
-
this.host = opts.host;
|
2432
|
-
this.path = opts.path || '/dns-query';
|
2433
|
-
this.method = /^post$/i.test(opts.method) ? 'POST' : 'GET';
|
2434
|
-
this.ipv4 = opts.ipv4;
|
2435
|
-
this.ipv6 = opts.ipv6;
|
2436
|
-
if (!this.ipv6) {
|
2437
|
-
const v6Parts = v6Regex.exec(this.host);
|
2438
|
-
if (v6Parts) {
|
2439
|
-
this.ipv6 = v6Parts[1];
|
2440
|
-
}
|
2441
|
-
}
|
2442
|
-
if (!this.ipv4) {
|
2443
|
-
if (v4Regex.test(this.host)) {
|
2444
|
-
this.ipv4 = this.host;
|
2445
|
-
}
|
2446
|
-
}
|
2447
|
-
const url = `${this.protocol}//${safeHost(this.host)}:${this.port}${this.path}`;
|
2448
|
-
try {
|
2449
|
-
this.url = new URL(url);
|
2450
|
-
} catch (err) {
|
2451
|
-
throw new Error(err.message + ` [${url}]`)
|
2452
|
-
}
|
2453
|
-
}
|
2454
|
-
|
2455
|
-
toString () {
|
2456
|
-
const protocol = this.protocol === 'https:' ? '' : 'http://';
|
2457
|
-
const port = this.port !== (this.protocol === 'https:' ? 443 : 80) ? `:${this.port}` : '';
|
2458
|
-
const method = this.method !== 'GET' ? ' [post]' : '';
|
2459
|
-
const path = this.path === '/dns-query' ? '' : this.path;
|
2460
|
-
const name = this.name ? ` [name=${this.name}]` : '';
|
2461
|
-
const ipv4 = this.ipv4 && this.ipv4 !== this.host ? ` [ipv4=${this.ipv4}]` : '';
|
2462
|
-
const ipv6 = this.ipv6 && this.ipv6 !== this.host ? ` [ipv6=${this.ipv6}]` : '';
|
2463
|
-
return `${protocol}${safeHost(this.host)}${port}${path}${method}${ipv4}${ipv6}${name}`
|
2464
|
-
}
|
2465
|
-
}
|
2466
|
-
|
2467
|
-
function toEndpoint (input) {
|
2468
|
-
let opts;
|
2469
|
-
if (typeof input === 'string') {
|
2470
|
-
opts = parseEndpoint(input);
|
2471
|
-
} else {
|
2472
|
-
if (typeof input !== 'object' || input === null || Array.isArray(input)) {
|
2473
|
-
throw new Error(`Can not convert ${input} to an endpoint`)
|
2474
|
-
} else if (input instanceof BaseEndpoint) {
|
2475
|
-
return input
|
2476
|
-
}
|
2477
|
-
opts = input;
|
2478
|
-
}
|
2479
|
-
if (opts.protocol === null || opts.protocol === undefined) {
|
2480
|
-
opts.protocol = 'https:';
|
2481
|
-
}
|
2482
|
-
const protocol = opts.protocol;
|
2483
|
-
if (protocol === 'udp4:') {
|
2484
|
-
return new UDP4Endpoint(opts)
|
2485
|
-
}
|
2486
|
-
if (protocol === 'udp6:') {
|
2487
|
-
return new UDP6Endpoint(opts)
|
2488
|
-
}
|
2489
|
-
if (protocol === 'https:' || protocol === 'http:') {
|
2490
|
-
return new HTTPEndpoint(opts)
|
2491
|
-
}
|
2492
|
-
throw new InvalidProtocolError(protocol, JSON.stringify(opts))
|
2493
|
-
}
|
2494
|
-
|
2495
|
-
/* global XMLHttpRequest, localStorage */
|
2496
|
-
const contentType = 'application/dns-message';
|
2497
|
-
|
2498
|
-
function noop () { }
|
2499
|
-
|
2500
|
-
function queryDns () {
|
2501
|
-
throw new Error('Only "doh" endpoints are supported in the browser')
|
2502
|
-
}
|
2503
|
-
|
2504
|
-
async function loadJSON (url, cache, timeout, abortSignal) {
|
2505
|
-
const cacheKey = cache ? cache.localStoragePrefix + cache.name : null;
|
2506
|
-
if (cacheKey) {
|
2507
|
-
try {
|
2508
|
-
const cached = JSON.parse(localStorage.getItem(cacheKey));
|
2509
|
-
if (cached && cached.time > cache.maxTime) {
|
2510
|
-
return cached
|
2511
|
-
}
|
2512
|
-
} catch (err) {}
|
2513
|
-
}
|
2514
|
-
const { data } = await requestRaw(url, 'GET', null, timeout, abortSignal);
|
2515
|
-
const result = {
|
2516
|
-
time: Date.now(),
|
2517
|
-
data: JSON.parse(decode$1(data))
|
2518
|
-
};
|
2519
|
-
if (cacheKey) {
|
2520
|
-
try {
|
2521
|
-
localStorage.setItem(cacheKey, JSON.stringify(result));
|
2522
|
-
} catch (err) {
|
2523
|
-
result.time = null;
|
2524
|
-
}
|
2525
|
-
}
|
2526
|
-
return result
|
2527
|
-
}
|
2528
|
-
|
2529
|
-
function requestRaw (url, method, data, timeout, abortSignal) {
|
2530
|
-
return new Promise((resolve, reject) => {
|
2531
|
-
const target = new URL(url);
|
2532
|
-
if (method === 'GET' && data) {
|
2533
|
-
target.search = '?dns=' + base64URL.decode(data);
|
2534
|
-
}
|
2535
|
-
const uri = target.toString();
|
2536
|
-
const xhr = new XMLHttpRequest();
|
2537
|
-
xhr.open(method, uri, true);
|
2538
|
-
xhr.setRequestHeader('Accept', contentType);
|
2539
|
-
if (method === 'POST') {
|
2540
|
-
xhr.setRequestHeader('Content-Type', contentType);
|
2541
|
-
}
|
2542
|
-
xhr.responseType = 'arraybuffer';
|
2543
|
-
xhr.timeout = timeout;
|
2544
|
-
xhr.ontimeout = ontimeout;
|
2545
|
-
xhr.onreadystatechange = onreadystatechange;
|
2546
|
-
xhr.onerror = onerror;
|
2547
|
-
xhr.onload = onload;
|
2548
|
-
if (method === 'POST') {
|
2549
|
-
xhr.send(data);
|
2550
|
-
} else {
|
2551
|
-
xhr.send();
|
2552
|
-
}
|
2553
|
-
|
2554
|
-
if (abortSignal) {
|
2555
|
-
abortSignal.addEventListener('abort', onabort);
|
2556
|
-
}
|
2557
|
-
|
2558
|
-
function ontimeout () {
|
2559
|
-
finish(new TimeoutError(timeout));
|
2560
|
-
try {
|
2561
|
-
xhr.abort();
|
2562
|
-
} catch (e) { }
|
2563
|
-
}
|
2564
|
-
|
2565
|
-
function onload () {
|
2566
|
-
if (xhr.status !== 200) {
|
2567
|
-
finish(new HTTPStatusError(uri, xhr.status, method));
|
2568
|
-
} else {
|
2569
|
-
let buf;
|
2570
|
-
if (typeof xhr.response === 'string') {
|
2571
|
-
buf = encode$1(xhr.response);
|
2572
|
-
} else if (xhr.response instanceof Uint8Array) {
|
2573
|
-
buf = xhr.response;
|
2574
|
-
} else if (Array.isArray(xhr.response) || xhr.response instanceof ArrayBuffer) {
|
2575
|
-
buf = new Uint8Array(xhr.response);
|
2576
|
-
} else {
|
2577
|
-
throw new Error('Unprocessable response ' + xhr.response)
|
2578
|
-
}
|
2579
|
-
finish(null, buf);
|
2580
|
-
}
|
2581
|
-
}
|
2582
|
-
|
2583
|
-
function onreadystatechange () {
|
2584
|
-
if (xhr.readyState > 1 && xhr.status !== 200 && xhr.status !== 0) {
|
2585
|
-
finish(new HTTPStatusError(uri, xhr.status, method));
|
2586
|
-
try {
|
2587
|
-
xhr.abort();
|
2588
|
-
} catch (e) { }
|
2589
|
-
}
|
2590
|
-
}
|
2591
|
-
|
2592
|
-
let finish = function (error, data) {
|
2593
|
-
finish = noop;
|
2594
|
-
if (abortSignal) {
|
2595
|
-
abortSignal.removeEventListener('abort', onabort);
|
2596
|
-
}
|
2597
|
-
if (error) {
|
2598
|
-
resolve({
|
2599
|
-
error,
|
2600
|
-
response: xhr
|
2601
|
-
});
|
2602
|
-
} else {
|
2603
|
-
resolve({
|
2604
|
-
data,
|
2605
|
-
response: xhr
|
2606
|
-
});
|
2607
|
-
}
|
2608
|
-
};
|
2609
|
-
|
2610
|
-
function onerror () {
|
2611
|
-
finish(xhr.status === 200 ? new Error('Inexplicable XHR Error') : new HTTPStatusError(uri, xhr.status, method));
|
2612
|
-
}
|
2613
|
-
|
2614
|
-
function onabort () {
|
2615
|
-
finish(new AbortError());
|
2616
|
-
try {
|
2617
|
-
xhr.abort();
|
2618
|
-
} catch (e) { }
|
2619
|
-
}
|
2620
|
-
})
|
2621
|
-
}
|
2622
|
-
|
2623
|
-
function request (url, method, packet, timeout, abortSignal) {
|
2624
|
-
return requestRaw(url, method, packet, timeout, abortSignal)
|
2625
|
-
}
|
2626
|
-
|
2627
|
-
function processResolvers$1 (resolvers) {
|
2628
|
-
return resolvers.filter(resolver => resolver.cors || resolver.endpoint.cors)
|
2629
|
-
}
|
2630
|
-
|
2631
|
-
const resolvers = {
|
2632
|
-
data: [
|
2633
|
-
{
|
2634
|
-
name: 'adfree.usableprivacy.net',
|
2635
|
-
endpoint: {
|
2636
|
-
protocol: 'https:',
|
2637
|
-
host: 'adfree.usableprivacy.net'
|
2638
|
-
},
|
2639
|
-
description: 'Public updns DoH service with advertising, tracker and malware filters.\nHosted in Europe by @usableprivacy, details see: https://docs.usableprivacy.com',
|
2640
|
-
country: 'Germany',
|
2641
|
-
location: {
|
2642
|
-
lat: 51.2993,
|
2643
|
-
long: 9.491
|
2644
|
-
},
|
2645
|
-
filter: true
|
2646
|
-
},
|
2647
|
-
{
|
2648
|
-
name: 'adguard-dns-doh',
|
2649
|
-
endpoint: {
|
2650
|
-
protocol: 'https:',
|
2651
|
-
host: 'dns.adguard.com',
|
2652
|
-
ipv4: '94.140.15.15'
|
2653
|
-
},
|
2654
|
-
description: 'Remove ads and protect your computer from malware (over DoH)',
|
2655
|
-
country: 'France',
|
2656
|
-
location: {
|
2657
|
-
lat: 48.8582,
|
2658
|
-
long: 2.3387
|
2659
|
-
},
|
2660
|
-
filter: true
|
2661
|
-
},
|
2662
|
-
{
|
2663
|
-
name: 'adguard-dns-family-doh',
|
2664
|
-
endpoint: {
|
2665
|
-
protocol: 'https:',
|
2666
|
-
host: 'dns-family.adguard.com',
|
2667
|
-
ipv4: '94.140.15.16'
|
2668
|
-
},
|
2669
|
-
description: 'Adguard DNS with safesearch and adult content blocking (over DoH)',
|
2670
|
-
country: 'France',
|
2671
|
-
location: {
|
2672
|
-
lat: 48.8582,
|
2673
|
-
long: 2.3387
|
2674
|
-
},
|
2675
|
-
filter: true
|
2676
|
-
},
|
2677
|
-
{
|
2678
|
-
name: 'adguard-dns-unfiltered-doh',
|
2679
|
-
endpoint: {
|
2680
|
-
protocol: 'https:',
|
2681
|
-
host: 'dns-unfiltered.adguard.com',
|
2682
|
-
ipv4: '94.140.14.140'
|
2683
|
-
},
|
2684
|
-
description: 'AdGuard public DNS servers without filters (over DoH)',
|
2685
|
-
country: 'France',
|
2686
|
-
location: {
|
2687
|
-
lat: 48.8582,
|
2688
|
-
long: 2.3387
|
2689
|
-
}
|
2690
|
-
},
|
2691
|
-
{
|
2692
|
-
name: 'ahadns-doh-chi',
|
2693
|
-
endpoint: {
|
2694
|
-
protocol: 'https:',
|
2695
|
-
host: 'doh.chi.ahadns.net',
|
2696
|
-
cors: true
|
2697
|
-
},
|
2698
|
-
description: 'A zero logging DNS with support for DNS-over-HTTPS (DoH) & DNS-over-TLS (DoT). Blocks ads, malware, trackers, viruses, ransomware, telemetry and more. No persistent logs. DNSSEC. Hosted in Chicago, USA. By https://ahadns.com/\nServer statistics can be seen at: https://statistics.ahadns.com/?server=chi',
|
2699
|
-
country: 'United States',
|
2700
|
-
location: {
|
2701
|
-
lat: 41.8483,
|
2702
|
-
long: -87.6517
|
2703
|
-
},
|
2704
|
-
filter: true,
|
2705
|
-
cors: true
|
2706
|
-
},
|
2707
|
-
{
|
2708
|
-
name: 'ahadns-doh-in',
|
2709
|
-
endpoint: {
|
2710
|
-
protocol: 'https:',
|
2711
|
-
host: 'doh.in.ahadns.net',
|
2712
|
-
cors: true
|
2713
|
-
},
|
2714
|
-
description: 'A zero logging DNS with support for DNS-over-HTTPS (DoH) & DNS-over-TLS (DoT). Blocks ads, malware, trackers, viruses, ransomware, telemetry and more. No persistent logs. DNSSEC. Hosted in Mumbai, India. By https://ahadns.com/\nServer statistics can be seen at: https://statistics.ahadns.com/?server=in',
|
2715
|
-
country: 'India',
|
2716
|
-
location: {
|
2717
|
-
lat: 19.0748,
|
2718
|
-
long: 72.8856
|
2719
|
-
},
|
2720
|
-
filter: true,
|
2721
|
-
cors: true
|
2722
|
-
},
|
2723
|
-
{
|
2724
|
-
name: 'ahadns-doh-la',
|
2725
|
-
endpoint: {
|
2726
|
-
protocol: 'https:',
|
2727
|
-
host: 'doh.la.ahadns.net',
|
2728
|
-
cors: true
|
2729
|
-
},
|
2730
|
-
description: 'A zero logging DNS with support for DNS-over-HTTPS (DoH) & DNS-over-TLS (DoT). Blocks ads, malware, trackers, viruses, ransomware, telemetry and more. No persistent logs. DNSSEC. Hosted in Los Angeles, USA. By https://ahadns.com/\nServer statistics can be seen at: https://statistics.ahadns.com/?server=la',
|
2731
|
-
country: 'United States',
|
2732
|
-
location: {
|
2733
|
-
lat: 34.0549,
|
2734
|
-
long: -118.2578
|
2735
|
-
},
|
2736
|
-
filter: true,
|
2737
|
-
cors: true
|
2738
|
-
},
|
2739
|
-
{
|
2740
|
-
name: 'ahadns-doh-nl',
|
2741
|
-
endpoint: {
|
2742
|
-
protocol: 'https:',
|
2743
|
-
host: 'doh.nl.ahadns.net',
|
2744
|
-
cors: true
|
2745
|
-
},
|
2746
|
-
description: 'A zero logging DNS with support for DNS-over-HTTPS (DoH) & DNS-over-TLS (DoT). Blocks ads, malware, trackers, viruses, ransomware, telemetry and more. No persistent logs. DNSSEC. Hosted in Amsterdam, Netherlands. By https://ahadns.com/\nServer statistics can be seen at: https://statistics.ahadns.com/?server=nl',
|
2747
|
-
country: 'Netherlands',
|
2748
|
-
location: {
|
2749
|
-
lat: 52.3824,
|
2750
|
-
long: 4.8995
|
2751
|
-
},
|
2752
|
-
filter: true,
|
2753
|
-
cors: true
|
2754
|
-
},
|
2755
|
-
{
|
2756
|
-
name: 'ahadns-doh-ny',
|
2757
|
-
endpoint: {
|
2758
|
-
protocol: 'https:',
|
2759
|
-
host: 'doh.ny.ahadns.net',
|
2760
|
-
cors: true
|
2761
|
-
},
|
2762
|
-
description: 'A zero logging DNS with support for DNS-over-HTTPS (DoH) & DNS-over-TLS (DoT). Blocks ads, malware, trackers, viruses, ransomware, telemetry and more. No persistent logs. DNSSEC. Hosted in New York. By https://ahadns.com/\nServer statistics can be seen at: https://statistics.ahadns.com/?server=ny',
|
2763
|
-
country: 'United States',
|
2764
|
-
location: {
|
2765
|
-
lat: 40.7308,
|
2766
|
-
long: -73.9975
|
2767
|
-
},
|
2768
|
-
filter: true,
|
2769
|
-
cors: true
|
2770
|
-
},
|
2771
|
-
{
|
2772
|
-
name: 'ahadns-doh-pl',
|
2773
|
-
endpoint: {
|
2774
|
-
protocol: 'https:',
|
2775
|
-
host: 'doh.pl.ahadns.net',
|
2776
|
-
cors: true
|
2777
|
-
},
|
2778
|
-
description: 'A zero logging DNS with support for DNS-over-HTTPS (DoH) & DNS-over-TLS (DoT). Blocks ads, malware, trackers, viruses, ransomware, telemetry and more. No persistent logs. DNSSEC. Hosted in Poland. By https://ahadns.com/\nServer statistics can be seen at: https://statistics.ahadns.com/?server=pl',
|
2779
|
-
country: 'Netherlands',
|
2780
|
-
location: {
|
2781
|
-
lat: 52.3824,
|
2782
|
-
long: 4.8995
|
2783
|
-
},
|
2784
|
-
filter: true,
|
2785
|
-
cors: true
|
2786
|
-
},
|
2787
|
-
{
|
2788
|
-
name: 'alidns-doh',
|
2789
|
-
endpoint: {
|
2790
|
-
protocol: 'https:',
|
2791
|
-
host: 'dns.alidns.com',
|
2792
|
-
ipv4: '223.5.5.5',
|
2793
|
-
cors: true
|
2794
|
-
},
|
2795
|
-
description: 'A public DNS resolver that supports DoH/DoT in mainland China, provided by Alibaba-Cloud.\nWarning: GFW filtering rules are applied by that resolver.\nHomepage: https://alidns.com/',
|
2796
|
-
country: 'China',
|
2797
|
-
location: {
|
2798
|
-
lat: 34.7725,
|
2799
|
-
long: 113.7266
|
2800
|
-
},
|
2801
|
-
filter: true,
|
2802
|
-
log: true,
|
2803
|
-
cors: true
|
2804
|
-
},
|
2805
|
-
{
|
2806
|
-
name: 'ams-ads-doh-nl',
|
2807
|
-
endpoint: {
|
2808
|
-
protocol: 'https:',
|
2809
|
-
host: 'dnsnl-noads.alekberg.net'
|
2810
|
-
},
|
2811
|
-
description: 'Resolver in Amsterdam. DoH protocol. Non-logging. Blocks ads, malware and trackers. DNSSEC enabled.',
|
2812
|
-
country: 'Romania',
|
2813
|
-
location: {
|
2814
|
-
lat: 45.9968,
|
2815
|
-
long: 24.997
|
2816
|
-
},
|
2817
|
-
filter: true
|
2818
|
-
},
|
2819
|
-
{
|
2820
|
-
name: 'ams-doh-nl',
|
2821
|
-
endpoint: {
|
2822
|
-
protocol: 'https:',
|
2823
|
-
host: 'dnsnl.alekberg.net'
|
2824
|
-
},
|
2825
|
-
description: 'Resolver in Amsterdam. DoH protocol. Non-logging, non-filtering, DNSSEC.',
|
2826
|
-
country: 'Romania',
|
2827
|
-
location: {
|
2828
|
-
lat: 45.9968,
|
2829
|
-
long: 24.997
|
2830
|
-
}
|
2831
|
-
},
|
2832
|
-
{
|
2833
|
-
name: 'att',
|
2834
|
-
endpoint: {
|
2835
|
-
protocol: 'https:',
|
2836
|
-
host: 'dohtrial.att.net'
|
2837
|
-
},
|
2838
|
-
description: 'AT&T test DoH server.',
|
2839
|
-
log: true
|
2840
|
-
},
|
2841
|
-
{
|
2842
|
-
name: 'bcn-ads-doh',
|
2843
|
-
endpoint: {
|
2844
|
-
protocol: 'https:',
|
2845
|
-
host: 'dnses-noads.alekberg.net'
|
2846
|
-
},
|
2847
|
-
description: 'Resolver in Spain. DoH protocol. Non-logging, remove ads and malware, DNSSEC.',
|
2848
|
-
country: 'Spain',
|
2849
|
-
location: {
|
2850
|
-
lat: 41.3891,
|
2851
|
-
long: 2.1611
|
2852
|
-
},
|
2853
|
-
filter: true
|
2854
|
-
},
|
2855
|
-
{
|
2856
|
-
name: 'bcn-doh',
|
2857
|
-
endpoint: {
|
2858
|
-
protocol: 'https:',
|
2859
|
-
host: 'dnses.alekberg.net'
|
2860
|
-
},
|
2861
|
-
description: 'Resolver in Spain. DoH protocol. Non-logging, non-filtering, DNSSEC.',
|
2862
|
-
country: 'Spain',
|
2863
|
-
location: {
|
2864
|
-
lat: 41.3891,
|
2865
|
-
long: 2.1611
|
2866
|
-
}
|
2867
|
-
},
|
2868
|
-
{
|
2869
|
-
name: 'brahma-world',
|
2870
|
-
endpoint: {
|
2871
|
-
protocol: 'https:',
|
2872
|
-
host: 'dns.brahma.world'
|
2873
|
-
},
|
2874
|
-
description: 'DNS-over-HTTPS server. Non Logging, filters ads, trackers and malware. DNSSEC ready, QNAME Minimization, No EDNS Client-Subnet.\nHosted in Stockholm, Sweden. (https://dns.brahma.world)',
|
2875
|
-
country: 'United States',
|
2876
|
-
location: {
|
2877
|
-
lat: 37.751,
|
2878
|
-
long: -97.822
|
2879
|
-
},
|
2880
|
-
filter: true
|
2881
|
-
},
|
2882
|
-
{
|
2883
|
-
name: 'cisco-doh',
|
2884
|
-
endpoint: {
|
2885
|
-
protocol: 'https:',
|
2886
|
-
host: 'doh.opendns.com',
|
2887
|
-
ipv4: '146.112.41.2'
|
2888
|
-
},
|
2889
|
-
description: 'Remove your DNS blind spot (DoH protocol)\nWarning: modifies your queries to include a copy of your network\naddress when forwarding them to a selection of companies and organizations.',
|
2890
|
-
country: 'United States',
|
2891
|
-
location: {
|
2892
|
-
lat: 37.751,
|
2893
|
-
long: -97.822
|
2894
|
-
},
|
2895
|
-
filter: true,
|
2896
|
-
log: true
|
2897
|
-
},
|
2898
|
-
{
|
2899
|
-
name: 'cloudflare',
|
2900
|
-
endpoint: {
|
2901
|
-
protocol: 'https:',
|
2902
|
-
host: 'dns.cloudflare.com',
|
2903
|
-
ipv4: '1.0.0.1',
|
2904
|
-
cors: true
|
2905
|
-
},
|
2906
|
-
description: 'Cloudflare DNS (anycast) - aka 1.1.1.1 / 1.0.0.1',
|
2907
|
-
country: 'Australia',
|
2908
|
-
location: {
|
2909
|
-
lat: -33.494,
|
2910
|
-
long: 143.2104
|
2911
|
-
},
|
2912
|
-
cors: true
|
2913
|
-
},
|
2914
|
-
{
|
2915
|
-
name: 'cloudflare-family',
|
2916
|
-
endpoint: {
|
2917
|
-
protocol: 'https:',
|
2918
|
-
host: 'family.cloudflare-dns.com',
|
2919
|
-
ipv4: '1.0.0.3',
|
2920
|
-
cors: true
|
2921
|
-
},
|
2922
|
-
description: 'Cloudflare DNS (anycast) with malware protection and parental control - aka 1.1.1.3 / 1.0.0.3',
|
2923
|
-
country: 'Australia',
|
2924
|
-
location: {
|
2925
|
-
lat: -33.494,
|
2926
|
-
long: 143.2104
|
2927
|
-
},
|
2928
|
-
filter: true,
|
2929
|
-
cors: true
|
2930
|
-
},
|
2931
|
-
{
|
2932
|
-
name: 'cloudflare-ipv6',
|
2933
|
-
endpoint: {
|
2934
|
-
protocol: 'https:',
|
2935
|
-
host: '1dot1dot1dot1.cloudflare-dns.com',
|
2936
|
-
cors: true
|
2937
|
-
},
|
2938
|
-
description: 'Cloudflare DNS over IPv6 (anycast)',
|
2939
|
-
country: 'United States',
|
2940
|
-
location: {
|
2941
|
-
lat: 37.751,
|
2942
|
-
long: -97.822
|
2943
|
-
},
|
2944
|
-
cors: true
|
2945
|
-
},
|
2946
|
-
{
|
2947
|
-
name: 'cloudflare-security',
|
2948
|
-
endpoint: {
|
2949
|
-
protocol: 'https:',
|
2950
|
-
host: 'security.cloudflare-dns.com',
|
2951
|
-
ipv4: '1.0.0.2',
|
2952
|
-
cors: true
|
2953
|
-
},
|
2954
|
-
description: 'Cloudflare DNS (anycast) with malware blocking - aka 1.1.1.2 / 1.0.0.2',
|
2955
|
-
country: 'Australia',
|
2956
|
-
location: {
|
2957
|
-
lat: -33.494,
|
2958
|
-
long: 143.2104
|
2959
|
-
},
|
2960
|
-
filter: true,
|
2961
|
-
cors: true
|
2962
|
-
},
|
2963
|
-
{
|
2964
|
-
name: 'controld-block-malware',
|
2965
|
-
endpoint: {
|
2966
|
-
protocol: 'https:',
|
2967
|
-
host: 'freedns.controld.com',
|
2968
|
-
path: '/p1'
|
2969
|
-
},
|
2970
|
-
description: 'ControlD Free DNS. Take CONTROL of your Internet. Block unwanted content, bypass geo-restrictions and be more productive. DoH protocol and No logging. - https://controld.com/free-dns\nThis DNS blocks Malware domains.',
|
2971
|
-
country: 'Canada',
|
2972
|
-
location: {
|
2973
|
-
lat: 43.6319,
|
2974
|
-
long: -79.3716
|
2975
|
-
},
|
2976
|
-
filter: true
|
2977
|
-
},
|
2978
|
-
{
|
2979
|
-
name: 'controld-block-malware-ad',
|
2980
|
-
endpoint: {
|
2981
|
-
protocol: 'https:',
|
2982
|
-
host: 'freedns.controld.com',
|
2983
|
-
path: '/p2'
|
2984
|
-
},
|
2985
|
-
description: 'ControlD Free DNS. Take CONTROL of your Internet. Block unwanted content, bypass geo-restrictions and be more productive. DoH protocol and No logging. - https://controld.com/free-dns\nThis DNS blocks Malware, Ads & Tracking domains.',
|
2986
|
-
country: 'Canada',
|
2987
|
-
location: {
|
2988
|
-
lat: 43.6319,
|
2989
|
-
long: -79.3716
|
2990
|
-
},
|
2991
|
-
filter: true
|
2992
|
-
},
|
2993
|
-
{
|
2994
|
-
name: 'controld-block-malware-ad-social',
|
2995
|
-
endpoint: {
|
2996
|
-
protocol: 'https:',
|
2997
|
-
host: 'freedns.controld.com',
|
2998
|
-
path: '/p3'
|
2999
|
-
},
|
3000
|
-
description: 'ControlD Free DNS. Take CONTROL of your Internet. Block unwanted content, bypass geo-restrictions and be more productive. DoH protocol and No logging. - https://controld.com/free-dns\nThis DNS blocks Malware, Ads & Tracking and Social Networks domains.',
|
3001
|
-
country: 'Canada',
|
3002
|
-
location: {
|
3003
|
-
lat: 43.6319,
|
3004
|
-
long: -79.3716
|
3005
|
-
},
|
3006
|
-
filter: true
|
3007
|
-
},
|
3008
|
-
{
|
3009
|
-
name: 'controld-family-friendly',
|
3010
|
-
endpoint: {
|
3011
|
-
protocol: 'https:',
|
3012
|
-
host: 'freedns.controld.com',
|
3013
|
-
path: '/family'
|
3014
|
-
},
|
3015
|
-
description: 'ControlD Free DNS. Take CONTROL of your Internet. Block unwanted content, bypass geo-restrictions and be more productive. DoH protocol and No logging. - https://controld.com/free-dns\nThis DNS blocks Malware, Ads & Tracking, Adult Content and Drugs domains.',
|
3016
|
-
country: 'Canada',
|
3017
|
-
location: {
|
3018
|
-
lat: 43.6319,
|
3019
|
-
long: -79.3716
|
3020
|
-
},
|
3021
|
-
filter: true
|
3022
|
-
},
|
3023
|
-
{
|
3024
|
-
name: 'controld-uncensored',
|
3025
|
-
endpoint: {
|
3026
|
-
protocol: 'https:',
|
3027
|
-
host: 'freedns.controld.com',
|
3028
|
-
path: '/uncensored'
|
3029
|
-
},
|
3030
|
-
description: 'ControlD Free DNS. Take CONTROL of your Internet. Block unwanted content, bypass geo-restrictions and be more productive. DoH protocol and No logging. - https://controld.com/free-dns\nThis DNS unblocks censored domains from various countries.',
|
3031
|
-
country: 'Canada',
|
3032
|
-
location: {
|
3033
|
-
lat: 43.6319,
|
3034
|
-
long: -79.3716
|
3035
|
-
}
|
3036
|
-
},
|
3037
|
-
{
|
3038
|
-
name: 'controld-unfiltered',
|
3039
|
-
endpoint: {
|
3040
|
-
protocol: 'https:',
|
3041
|
-
host: 'freedns.controld.com',
|
3042
|
-
path: '/p0'
|
3043
|
-
},
|
3044
|
-
description: 'ControlD Free DNS. Take CONTROL of your Internet. Block unwanted content, bypass geo-restrictions and be more productive. DoH protocol and No logging. - https://controld.com/free-dns\nThis is a Unfiltered DNS, no DNS record blocking or manipulation here, if you want to block Malware, Ads & Tracking or Social Network domains, use the other ControlD DNS configs.',
|
3045
|
-
country: 'Canada',
|
3046
|
-
location: {
|
3047
|
-
lat: 43.6319,
|
3048
|
-
long: -79.3716
|
3049
|
-
}
|
3050
|
-
},
|
3051
|
-
{
|
3052
|
-
name: 'dns.digitale-gesellschaft.ch',
|
3053
|
-
endpoint: {
|
3054
|
-
protocol: 'https:',
|
3055
|
-
host: 'dns.digitale-gesellschaft.ch'
|
3056
|
-
},
|
3057
|
-
description: 'Public DoH resolver operated by the Digital Society (https://www.digitale-gesellschaft.ch).\nHosted in Zurich, Switzerland.\nNon-logging, non-filtering, supports DNSSEC.',
|
3058
|
-
country: 'Switzerland',
|
3059
|
-
location: {
|
3060
|
-
lat: 47.1449,
|
3061
|
-
long: 8.1551
|
3062
|
-
}
|
3063
|
-
},
|
3064
|
-
{
|
3065
|
-
name: 'dns.ryan-palmer',
|
3066
|
-
endpoint: {
|
3067
|
-
protocol: 'https:',
|
3068
|
-
host: 'dns1.ryan-palmer.com'
|
3069
|
-
},
|
3070
|
-
description: 'Non-logging, non-filtering, DNSSEC DoH Server. Hosted in the UK.',
|
3071
|
-
country: 'United Kingdom',
|
3072
|
-
location: {
|
3073
|
-
lat: 51.5164,
|
3074
|
-
long: -0.093
|
3075
|
-
}
|
3076
|
-
},
|
3077
|
-
{
|
3078
|
-
name: 'dns.sb',
|
3079
|
-
endpoint: {
|
3080
|
-
protocol: 'https:',
|
3081
|
-
host: 'doh.sb',
|
3082
|
-
ipv4: '185.222.222.222',
|
3083
|
-
cors: true
|
3084
|
-
},
|
3085
|
-
description: 'DNSSEC-enabled DoH server by https://xtom.com/\nhttps://dns.sb/doh/',
|
3086
|
-
country: 'Unknown',
|
3087
|
-
location: {
|
3088
|
-
lat: 47,
|
3089
|
-
long: 8
|
3090
|
-
},
|
3091
|
-
cors: true
|
3092
|
-
},
|
3093
|
-
{
|
3094
|
-
name: 'dns.therifleman.name',
|
3095
|
-
endpoint: {
|
3096
|
-
protocol: 'https:',
|
3097
|
-
host: 'dns.therifleman.name'
|
3098
|
-
},
|
3099
|
-
description: 'DNS-over-HTTPS DNS forwarder from Mumbai, India. Blocks web and Android trackers and ads.\nIP addresses are not logged, but queries are logged for 24 hours for debugging.\nReport issues, send suggestions @ joker349 at protonmail.com.\nAlso supports DoT (for android) @ dns.therifleman.name and plain DNS @ 172.104.206.174',
|
3100
|
-
country: 'United States',
|
3101
|
-
location: {
|
3102
|
-
lat: 37.751,
|
3103
|
-
long: -97.822
|
3104
|
-
},
|
3105
|
-
filter: true
|
3106
|
-
},
|
3107
|
-
{
|
3108
|
-
name: 'dnsforfamily-doh',
|
3109
|
-
endpoint: {
|
3110
|
-
protocol: 'https:',
|
3111
|
-
host: 'dns-doh.dnsforfamily.com'
|
3112
|
-
},
|
3113
|
-
description: '(DoH Protocol) (Now supports DNSSEC). Block adult websites, gambling websites, malwares and advertisements.\nIt also enforces safe search in: Google, YouTube, Bing, DuckDuckGo and Yandex.\nSocial websites like Facebook and Instagram are not blocked. No DNS queries are logged.\nAs of 26-May-2022 5.9 million websites are blocked and new websites are added to blacklist daily.\nCompletely free, no ads or any commercial motive. Operating for 4 years now.\nProvided by: https://dnsforfamily.com',
|
3114
|
-
country: 'Finland',
|
3115
|
-
location: {
|
3116
|
-
lat: 60.1758,
|
3117
|
-
long: 24.9349
|
3118
|
-
},
|
3119
|
-
filter: true
|
3120
|
-
},
|
3121
|
-
{
|
3122
|
-
name: 'dnsforfamily-doh-no-safe-search',
|
3123
|
-
endpoint: {
|
3124
|
-
protocol: 'https:',
|
3125
|
-
host: 'dns-doh-no-safe-search.dnsforfamily.com'
|
3126
|
-
},
|
3127
|
-
description: '(DoH Protocol) (Now supports DNSSEC) Block adult websites, gambling websites, malwares and advertisements.\nUnlike other dnsforfamily servers, this one does not enforces safe search. So Google, YouTube, Bing, DuckDuckGo and Yandex are completely accessible without any restriction.\nSocial websites like Facebook and Instagram are not blocked. No DNS queries are logged.\nAs of 26-May-2022 5.9 million websites are blocked and new websites are added to blacklist daily.\nCompletely free, no ads or any commercial motive. Operating for 4 years now.\nWarning: This server is incompatible with anonymization.\nProvided by: https://dnsforfamily.com',
|
3128
|
-
country: 'Finland',
|
3129
|
-
location: {
|
3130
|
-
lat: 60.1758,
|
3131
|
-
long: 24.9349
|
3132
|
-
},
|
3133
|
-
filter: true
|
3134
|
-
},
|
3135
|
-
{
|
3136
|
-
name: 'dnsforge.de',
|
3137
|
-
endpoint: {
|
3138
|
-
protocol: 'https:',
|
3139
|
-
host: 'dnsforge.de',
|
3140
|
-
cors: true
|
3141
|
-
},
|
3142
|
-
description: 'Public DoH resolver running with Pihole for Adblocking (https://dnsforge.de).\nNon-logging, AD-filtering, supports DNSSEC. Hosted in Germany.',
|
3143
|
-
country: 'Germany',
|
3144
|
-
location: {
|
3145
|
-
lat: 52.2998,
|
3146
|
-
long: 9.447
|
3147
|
-
},
|
3148
|
-
filter: true,
|
3149
|
-
cors: true
|
3150
|
-
},
|
3151
|
-
{
|
3152
|
-
name: 'dnshome-doh',
|
3153
|
-
endpoint: {
|
3154
|
-
protocol: 'https:',
|
3155
|
-
host: 'dns.dnshome.de'
|
3156
|
-
},
|
3157
|
-
description: 'https://www.dnshome.de/ public resolver in Germany'
|
3158
|
-
},
|
3159
|
-
{
|
3160
|
-
name: 'dnspod-doh',
|
3161
|
-
endpoint: {
|
3162
|
-
protocol: 'https:',
|
3163
|
-
host: 'doh.pub',
|
3164
|
-
cors: true
|
3165
|
-
},
|
3166
|
-
description: 'A public DNS resolver in mainland China provided by DNSPod (Tencent Cloud).\nhttps://www.dnspod.cn/Products/Public.DNS?lang=en',
|
3167
|
-
filter: true,
|
3168
|
-
log: true,
|
3169
|
-
cors: true
|
3170
|
-
},
|
3171
|
-
{
|
3172
|
-
name: 'dnswarden-asia-adblock-dohv4',
|
3173
|
-
endpoint: {
|
3174
|
-
protocol: 'https:',
|
3175
|
-
host: 'doh.asia.dnswarden.com',
|
3176
|
-
path: '/adblock'
|
3177
|
-
},
|
3178
|
-
description: 'Hosted in Singapore. For more information look [here](https://github.com/bhanupratapys/dnswarden) or [here](https://dnswarden.com).',
|
3179
|
-
country: 'Singapore',
|
3180
|
-
location: {
|
3181
|
-
lat: 1.2929,
|
3182
|
-
long: 103.8547
|
3183
|
-
},
|
3184
|
-
filter: true
|
3185
|
-
},
|
3186
|
-
{
|
3187
|
-
name: 'dnswarden-asia-adultfilter-dohv4',
|
3188
|
-
endpoint: {
|
3189
|
-
protocol: 'https:',
|
3190
|
-
host: 'doh.asia.dnswarden.com',
|
3191
|
-
path: '/adultfilter'
|
3192
|
-
},
|
3193
|
-
description: 'Hosted in Singapore. For more information look [here](https://github.com/bhanupratapys/dnswarden) or [here](https://dnswarden.com).',
|
3194
|
-
country: 'Singapore',
|
3195
|
-
location: {
|
3196
|
-
lat: 1.2929,
|
3197
|
-
long: 103.8547
|
3198
|
-
},
|
3199
|
-
filter: true
|
3200
|
-
},
|
3201
|
-
{
|
3202
|
-
name: 'dnswarden-asia-uncensor-dohv4',
|
3203
|
-
endpoint: {
|
3204
|
-
protocol: 'https:',
|
3205
|
-
host: 'doh.asia.dnswarden.com',
|
3206
|
-
path: '/uncensored'
|
3207
|
-
},
|
3208
|
-
description: 'Hosted in Singapore. For more information look [here](https://github.com/bhanupratapys/dnswarden) or [here](https://dnswarden.com).',
|
3209
|
-
country: 'Singapore',
|
3210
|
-
location: {
|
3211
|
-
lat: 1.2929,
|
3212
|
-
long: 103.8547
|
3213
|
-
}
|
3214
|
-
},
|
3215
|
-
{
|
3216
|
-
name: 'dnswarden-eu-adblock-dohv4',
|
3217
|
-
endpoint: {
|
3218
|
-
protocol: 'https:',
|
3219
|
-
host: 'doh.eu.dnswarden.com'
|
3220
|
-
},
|
3221
|
-
description: 'Hosted in Germany. For more information look [here](https://github.com/bhanupratapys/dnswarden) or [here](https://dnswarden.com).',
|
3222
|
-
country: 'Germany',
|
3223
|
-
location: {
|
3224
|
-
lat: 50.1103,
|
3225
|
-
long: 8.7147
|
3226
|
-
},
|
3227
|
-
filter: true
|
3228
|
-
},
|
3229
|
-
{
|
3230
|
-
name: 'dnswarden-us-adblock-dohv4',
|
3231
|
-
endpoint: {
|
3232
|
-
protocol: 'https:',
|
3233
|
-
host: 'doh.us.dnswarden.com'
|
3234
|
-
},
|
3235
|
-
description: 'Hosted in USA (Dallas) . For more information look [here](https://github.com/bhanupratapys/dnswarden) or [here](https://dnswarden.com).',
|
3236
|
-
country: 'United States',
|
3237
|
-
location: {
|
3238
|
-
lat: 32.7889,
|
3239
|
-
long: -96.8021
|
3240
|
-
},
|
3241
|
-
filter: true
|
3242
|
-
},
|
3243
|
-
{
|
3244
|
-
name: 'doh-ch-blahdns',
|
3245
|
-
endpoint: {
|
3246
|
-
protocol: 'https:',
|
3247
|
-
host: 'doh-ch.blahdns.com',
|
3248
|
-
cors: true
|
3249
|
-
},
|
3250
|
-
description: 'Blocks ad and Tracking, no Logging, DNSSEC, Hosted in Switzerland. By https://blahdns.com/',
|
3251
|
-
country: 'Netherlands',
|
3252
|
-
location: {
|
3253
|
-
lat: 52.3824,
|
3254
|
-
long: 4.8995
|
3255
|
-
},
|
3256
|
-
filter: true,
|
3257
|
-
cors: true
|
3258
|
-
},
|
3259
|
-
{
|
3260
|
-
name: 'doh-cleanbrowsing-adult',
|
3261
|
-
endpoint: {
|
3262
|
-
protocol: 'https:',
|
3263
|
-
host: 'doh.cleanbrowsing.org',
|
3264
|
-
path: '/doh/adult-filter/',
|
3265
|
-
cors: true
|
3266
|
-
},
|
3267
|
-
description: 'Blocks access to all adult, pornographic and explicit sites. It does\nnot block proxy or VPNs, nor mixed-content sites. Sites like Reddit\nare allowed. Google and Bing are set to the Safe Mode.\nBy https://cleanbrowsing.org/',
|
3268
|
-
filter: true,
|
3269
|
-
cors: true
|
3270
|
-
},
|
3271
|
-
{
|
3272
|
-
name: 'doh-cleanbrowsing-family',
|
3273
|
-
endpoint: {
|
3274
|
-
protocol: 'https:',
|
3275
|
-
host: 'doh.cleanbrowsing.org',
|
3276
|
-
path: '/doh/family-filter/',
|
3277
|
-
cors: true
|
3278
|
-
},
|
3279
|
-
description: 'Blocks access to all adult, pornographic and explicit sites. It also\nblocks proxy and VPN domains that are used to bypass the filters.\nMixed content sites (like Reddit) are also blocked. Google, Bing and\nYoutube are set to the Safe Mode.\nBy https://cleanbrowsing.org/',
|
3280
|
-
filter: true,
|
3281
|
-
cors: true
|
3282
|
-
},
|
3283
|
-
{
|
3284
|
-
name: 'doh-cleanbrowsing-security',
|
3285
|
-
endpoint: {
|
3286
|
-
protocol: 'https:',
|
3287
|
-
host: 'doh.cleanbrowsing.org',
|
3288
|
-
path: '/doh/security-filter/',
|
3289
|
-
cors: true
|
3290
|
-
},
|
3291
|
-
description: 'Block access to phishing, malware and malicious domains. It does not block adult content.\nBy https://cleanbrowsing.org/',
|
3292
|
-
filter: true,
|
3293
|
-
cors: true
|
3294
|
-
},
|
3295
|
-
{
|
3296
|
-
name: 'doh-crypto-sx',
|
3297
|
-
endpoint: {
|
3298
|
-
protocol: 'https:',
|
3299
|
-
host: 'doh.crypto.sx',
|
3300
|
-
cors: true
|
3301
|
-
},
|
3302
|
-
description: 'DNS-over-HTTPS server. Anycast, no logs, no censorship, DNSSEC.\nBackend hosted by Scaleway, globally cached via Cloudflare.\nMaintained by Frank Denis.',
|
3303
|
-
country: 'United States',
|
3304
|
-
location: {
|
3305
|
-
lat: 37.751,
|
3306
|
-
long: -97.822
|
3307
|
-
},
|
3308
|
-
cors: true
|
3309
|
-
},
|
3310
|
-
{
|
3311
|
-
name: 'doh-crypto-sx-ipv6',
|
3312
|
-
endpoint: {
|
3313
|
-
protocol: 'https:',
|
3314
|
-
host: 'doh-ipv6.crypto.sx',
|
3315
|
-
cors: true
|
3316
|
-
},
|
3317
|
-
description: 'DNS-over-HTTPS server accessible over IPv6. Anycast, no logs, no censorship, DNSSEC.\nBackend hosted by Scaleway, globally cached via Cloudflare.\nMaintained by Frank Denis.',
|
3318
|
-
country: 'United States',
|
3319
|
-
location: {
|
3320
|
-
lat: 37.751,
|
3321
|
-
long: -97.822
|
3322
|
-
},
|
3323
|
-
cors: true
|
3324
|
-
},
|
3325
|
-
{
|
3326
|
-
name: 'doh-de-blahdns',
|
3327
|
-
endpoint: {
|
3328
|
-
protocol: 'https:',
|
3329
|
-
host: 'doh-de.blahdns.com',
|
3330
|
-
cors: true
|
3331
|
-
},
|
3332
|
-
description: 'Blocks ad and Tracking, no Logging, DNSSEC, Hosted in Germany. By https://blahdns.com/',
|
3333
|
-
country: 'Germany',
|
3334
|
-
location: {
|
3335
|
-
lat: 51.2993,
|
3336
|
-
long: 9.491
|
3337
|
-
},
|
3338
|
-
filter: true,
|
3339
|
-
cors: true
|
3340
|
-
},
|
3341
|
-
{
|
3342
|
-
name: 'doh-fi-blahdns',
|
3343
|
-
endpoint: {
|
3344
|
-
protocol: 'https:',
|
3345
|
-
host: 'doh-fi.blahdns.com',
|
3346
|
-
cors: true
|
3347
|
-
},
|
3348
|
-
description: 'Blocks ad and Tracking, no Logging, DNSSEC, Hosted in Finland. By https://blahdns.com/',
|
3349
|
-
country: 'Finland',
|
3350
|
-
location: {
|
3351
|
-
lat: 60.1758,
|
3352
|
-
long: 24.9349
|
3353
|
-
},
|
3354
|
-
filter: true,
|
3355
|
-
cors: true
|
3356
|
-
},
|
3357
|
-
{
|
3358
|
-
name: 'doh-ibksturm',
|
3359
|
-
endpoint: {
|
3360
|
-
protocol: 'https:',
|
3361
|
-
host: 'ibksturm.synology.me'
|
3362
|
-
},
|
3363
|
-
description: 'DoH & DoT Server, No Logging, No Filters, DNSSEC\nRunning privately by ibksturm in Thurgau, Switzerland'
|
3364
|
-
},
|
3365
|
-
{
|
3366
|
-
name: 'doh-jp-blahdns',
|
3367
|
-
endpoint: {
|
3368
|
-
protocol: 'https:',
|
3369
|
-
host: 'doh-jp.blahdns.com',
|
3370
|
-
cors: true
|
3371
|
-
},
|
3372
|
-
description: 'Blocks ad and Tracking, no Logging, DNSSEC, Hosted in Japan. By https://blahdns.com/',
|
3373
|
-
country: 'Japan',
|
3374
|
-
location: {
|
3375
|
-
lat: 35.6882,
|
3376
|
-
long: 139.7532
|
3377
|
-
},
|
3378
|
-
filter: true,
|
3379
|
-
cors: true
|
3380
|
-
},
|
3381
|
-
{
|
3382
|
-
name: 'doh.ffmuc.net',
|
3383
|
-
endpoint: {
|
3384
|
-
protocol: 'https:',
|
3385
|
-
host: 'doh.ffmuc.net'
|
3386
|
-
},
|
3387
|
-
description: 'An open (non-logging, non-filtering, non-censoring) DoH resolver operated by Freifunk Munich with nodes in DE.\nhttps://ffmuc.net/',
|
3388
|
-
country: 'Germany',
|
3389
|
-
location: {
|
3390
|
-
lat: 51.2993,
|
3391
|
-
long: 9.491
|
3392
|
-
}
|
3393
|
-
},
|
3394
|
-
{
|
3395
|
-
name: 'doh.tiarap.org',
|
3396
|
-
endpoint: {
|
3397
|
-
protocol: 'https:',
|
3398
|
-
host: 'doh.tiarap.org'
|
3399
|
-
},
|
3400
|
-
description: 'Non-Logging DNS-over-HTTPS server, cached via Cloudflare.\nFilters out ads, trackers and malware, NO ECS, supports DNSSEC.',
|
3401
|
-
country: 'United States',
|
3402
|
-
location: {
|
3403
|
-
lat: 37.751,
|
3404
|
-
long: -97.822
|
3405
|
-
},
|
3406
|
-
filter: true
|
3407
|
-
},
|
3408
|
-
{
|
3409
|
-
name: 'google',
|
3410
|
-
endpoint: {
|
3411
|
-
protocol: 'https:',
|
3412
|
-
host: 'dns.google',
|
3413
|
-
ipv4: '8.8.8.8',
|
3414
|
-
cors: true
|
3415
|
-
},
|
3416
|
-
description: 'Google DNS (anycast)',
|
3417
|
-
country: 'United States',
|
3418
|
-
location: {
|
3419
|
-
lat: 37.751,
|
3420
|
-
long: -97.822
|
3421
|
-
},
|
3422
|
-
log: true,
|
3423
|
-
cors: true
|
3424
|
-
},
|
3425
|
-
{
|
3426
|
-
name: 'hdns',
|
3427
|
-
endpoint: {
|
3428
|
-
protocol: 'https:',
|
3429
|
-
host: 'query.hdns.io',
|
3430
|
-
cors: true
|
3431
|
-
},
|
3432
|
-
description: 'HDNS is a public DNS resolver that supports Handshake domains.\nhttps://www.hdns.io',
|
3433
|
-
country: 'United States',
|
3434
|
-
location: {
|
3435
|
-
lat: 37.7771,
|
3436
|
-
long: -122.406
|
3437
|
-
},
|
3438
|
-
cors: true
|
3439
|
-
},
|
3440
|
-
{
|
3441
|
-
name: 'he',
|
3442
|
-
endpoint: {
|
3443
|
-
protocol: 'https:',
|
3444
|
-
host: 'ordns.he.net'
|
3445
|
-
},
|
3446
|
-
description: 'Hurricane Electric DoH server (anycast)\nUnknown logging policy.',
|
3447
|
-
country: 'United States',
|
3448
|
-
location: {
|
3449
|
-
lat: 37.751,
|
3450
|
-
long: -97.822
|
3451
|
-
},
|
3452
|
-
log: true
|
3453
|
-
},
|
3454
|
-
{
|
3455
|
-
name: 'id-gmail-doh',
|
3456
|
-
endpoint: {
|
3457
|
-
protocol: 'https:',
|
3458
|
-
host: 'doh.tiar.app'
|
3459
|
-
},
|
3460
|
-
description: 'Non-Logging DNS-over-HTTPS server located in Singapore.\nFilters out ads, trackers and malware, supports DNSSEC, provided by id-gmail.',
|
3461
|
-
country: 'Singapore',
|
3462
|
-
location: {
|
3463
|
-
lat: 1.2929,
|
3464
|
-
long: 103.8547
|
3465
|
-
},
|
3466
|
-
filter: true
|
3467
|
-
},
|
3468
|
-
{
|
3469
|
-
name: 'iij',
|
3470
|
-
endpoint: {
|
3471
|
-
protocol: 'https:',
|
3472
|
-
host: 'public.dns.iij.jp'
|
3473
|
-
},
|
3474
|
-
description: 'DoH server operated by Internet Initiative Japan in Tokyo.\nhttps://www.iij.ad.jp/',
|
3475
|
-
country: 'Japan',
|
3476
|
-
location: {
|
3477
|
-
lat: 35.69,
|
3478
|
-
long: 139.69
|
3479
|
-
},
|
3480
|
-
log: true
|
3481
|
-
},
|
3482
|
-
{
|
3483
|
-
name: 'iqdns-doh',
|
3484
|
-
endpoint: {
|
3485
|
-
protocol: 'https:',
|
3486
|
-
host: 'a.passcloud.xyz'
|
3487
|
-
},
|
3488
|
-
description: 'Non-logging DoH service runned by V2EX.com user johnsonwil.\nReturns "no such domain" for anti-Chinese government websites. Supports DNSSEC.\nFor more information: https://www.v2ex.com/t/785666',
|
3489
|
-
filter: true
|
3490
|
-
},
|
3491
|
-
{
|
3492
|
-
name: 'jp.tiar.app-doh',
|
3493
|
-
endpoint: {
|
3494
|
-
protocol: 'https:',
|
3495
|
-
host: 'jp.tiar.app'
|
3496
|
-
},
|
3497
|
-
description: 'Non-Logging, Non-Filtering DNS-over-HTTPS server in Japan.\nNo ECS, Support DNSSEC',
|
3498
|
-
country: 'Japan',
|
3499
|
-
location: {
|
3500
|
-
lat: 35.6882,
|
3501
|
-
long: 139.7532
|
3502
|
-
}
|
3503
|
-
},
|
3504
|
-
{
|
3505
|
-
name: 'jp.tiarap.org',
|
3506
|
-
endpoint: {
|
3507
|
-
protocol: 'https:',
|
3508
|
-
host: 'jp.tiarap.org'
|
3509
|
-
},
|
3510
|
-
description: 'DNS-over-HTTPS Server. Non-Logging, Non-Filtering, No ECS, Support DNSSEC.\nCached via Cloudflare.'
|
3511
|
-
},
|
3512
|
-
{
|
3513
|
-
name: 'libredns',
|
3514
|
-
endpoint: {
|
3515
|
-
protocol: 'https:',
|
3516
|
-
host: 'doh.libredns.gr'
|
3517
|
-
},
|
3518
|
-
description: 'DoH server in Germany. No logging, but no DNS padding and no DNSSEC support.\nhttps://libredns.gr/',
|
3519
|
-
country: 'Germany',
|
3520
|
-
location: {
|
3521
|
-
lat: 51.2993,
|
3522
|
-
long: 9.491
|
3523
|
-
}
|
3524
|
-
},
|
3525
|
-
{
|
3526
|
-
name: 'nextdns',
|
3527
|
-
endpoint: {
|
3528
|
-
protocol: 'https:',
|
3529
|
-
host: 'anycsast.dns.nextdns.io'
|
3530
|
-
},
|
3531
|
-
description: 'NextDNS is a cloud-based private DNS service that gives you full control\nover what is allowed and what is blocked on the Internet.\nDNSSEC, Anycast, Non-logging, NoFilters\nhttps://www.nextdns.io/',
|
3532
|
-
country: 'Netherlands',
|
3533
|
-
location: {
|
3534
|
-
lat: 52.3891,
|
3535
|
-
long: 4.6563
|
3536
|
-
}
|
3537
|
-
},
|
3538
|
-
{
|
3539
|
-
name: 'nextdns-ultralow',
|
3540
|
-
endpoint: {
|
3541
|
-
protocol: 'https:',
|
3542
|
-
host: 'dns.nextdns.io',
|
3543
|
-
path: '/dnscrypt-proxy'
|
3544
|
-
},
|
3545
|
-
description: 'NextDNS is a cloud-based private DNS service that gives you full control\nover what is allowed and what is blocked on the Internet.\nhttps://www.nextdns.io/\nTo select the server location, the "-ultralow" variant relies on bootstrap servers\ninstead of anycast.'
|
3546
|
-
},
|
3547
|
-
{
|
3548
|
-
name: 'njalla-doh',
|
3549
|
-
endpoint: {
|
3550
|
-
protocol: 'https:',
|
3551
|
-
host: 'dns.njal.la',
|
3552
|
-
cors: true
|
3553
|
-
},
|
3554
|
-
description: 'Non-logging DoH server in Sweden operated by Njalla.\nhttps://dns.njal.la/',
|
3555
|
-
country: 'Sweden',
|
3556
|
-
location: {
|
3557
|
-
lat: 59.3247,
|
3558
|
-
long: 18.056
|
3559
|
-
},
|
3560
|
-
cors: true
|
3561
|
-
},
|
3562
|
-
{
|
3563
|
-
name: 'odoh-cloudflare',
|
3564
|
-
endpoint: {
|
3565
|
-
protocol: 'https:',
|
3566
|
-
host: 'odoh.cloudflare-dns.com',
|
3567
|
-
cors: true
|
3568
|
-
},
|
3569
|
-
description: 'Cloudflare ODoH server.\nhttps://cloudflare.com',
|
3570
|
-
cors: true
|
3571
|
-
},
|
3572
|
-
{
|
3573
|
-
name: 'odoh-crypto-sx',
|
3574
|
-
endpoint: {
|
3575
|
-
protocol: 'https:',
|
3576
|
-
host: 'odoh.crypto.sx',
|
3577
|
-
cors: true
|
3578
|
-
},
|
3579
|
-
description: 'ODoH target server. Anycast, no logs.\nBackend hosted by Scaleway. Maintained by Frank Denis.',
|
3580
|
-
cors: true
|
3581
|
-
},
|
3582
|
-
{
|
3583
|
-
name: 'odoh-id-gmail',
|
3584
|
-
endpoint: {
|
3585
|
-
protocol: 'https:',
|
3586
|
-
host: 'doh.tiar.app',
|
3587
|
-
path: '/odoh'
|
3588
|
-
},
|
3589
|
-
description: 'ODoH target server. Based in Singapore, no logs.\nFilter ads, trackers and malware.',
|
3590
|
-
filter: true
|
3591
|
-
},
|
3592
|
-
{
|
3593
|
-
name: 'odoh-jp.tiar.app',
|
3594
|
-
endpoint: {
|
3595
|
-
protocol: 'https:',
|
3596
|
-
host: 'jp.tiar.app',
|
3597
|
-
path: '/odoh'
|
3598
|
-
},
|
3599
|
-
description: 'ODoH target server. no logs.'
|
3600
|
-
},
|
3601
|
-
{
|
3602
|
-
name: 'odoh-jp.tiarap.org',
|
3603
|
-
endpoint: {
|
3604
|
-
protocol: 'https:',
|
3605
|
-
host: 'jp.tiarap.org',
|
3606
|
-
path: '/odoh'
|
3607
|
-
},
|
3608
|
-
description: 'ODoH target server via Cloudflare, no logs.'
|
3609
|
-
},
|
3610
|
-
{
|
3611
|
-
name: 'odoh-resolver4.dns.openinternet.io',
|
3612
|
-
endpoint: {
|
3613
|
-
protocol: 'https:',
|
3614
|
-
host: 'resolver4.dns.openinternet.io'
|
3615
|
-
},
|
3616
|
-
description: "ODoH target server. no logs, no filter, DNSSEC.\nRunning on dedicated hardware colocated at Sonic.net in Santa Rosa, CA in the United States.\nUses Sonic's recusrive DNS servers as upstream resolvers (but is not affiliated with Sonic\nin any way). Provided by https://openinternet.io"
|
3617
|
-
},
|
3618
|
-
{
|
3619
|
-
name: 'odoh-tiarap.org',
|
3620
|
-
endpoint: {
|
3621
|
-
protocol: 'https:',
|
3622
|
-
host: 'doh.tiarap.org',
|
3623
|
-
path: '/odoh'
|
3624
|
-
},
|
3625
|
-
description: 'ODoH target server via Cloudflare, no logs.\nFilter ads, trackers and malware.',
|
3626
|
-
filter: true
|
3627
|
-
},
|
3628
|
-
{
|
3629
|
-
name: 'publicarray-au2-doh',
|
3630
|
-
endpoint: {
|
3631
|
-
protocol: 'https:',
|
3632
|
-
host: 'doh-2.seby.io',
|
3633
|
-
cors: true
|
3634
|
-
},
|
3635
|
-
description: 'DNSSEC • OpenNIC • Non-logging • Uncensored - hosted on ovh.com.au\nMaintained by publicarray - https://dns.seby.io',
|
3636
|
-
country: 'Australia',
|
3637
|
-
location: {
|
3638
|
-
lat: -33.8591,
|
3639
|
-
long: 151.2002
|
3640
|
-
},
|
3641
|
-
cors: true
|
3642
|
-
},
|
3643
|
-
{
|
3644
|
-
name: 'puredns-doh',
|
3645
|
-
endpoint: {
|
3646
|
-
protocol: 'https:',
|
3647
|
-
host: 'puredns.org',
|
3648
|
-
ipv4: '146.190.6.13',
|
3649
|
-
cors: true
|
3650
|
-
},
|
3651
|
-
description: 'Public uncensored DNS resolver in Singapore - https://puredns.org\n** Only available in Indonesia and Singapore **',
|
3652
|
-
country: 'United States',
|
3653
|
-
location: {
|
3654
|
-
lat: 37.751,
|
3655
|
-
long: -97.822
|
3656
|
-
},
|
3657
|
-
cors: true
|
3658
|
-
},
|
3659
|
-
{
|
3660
|
-
name: 'quad101',
|
3661
|
-
endpoint: {
|
3662
|
-
protocol: 'https:',
|
3663
|
-
host: 'dns.twnic.tw',
|
3664
|
-
cors: true
|
3665
|
-
},
|
3666
|
-
description: 'DNSSEC-aware public resolver by the Taiwan Network Information Center (TWNIC)\nhttps://101.101.101.101/index_en.html',
|
3667
|
-
cors: true
|
3668
|
-
},
|
3669
|
-
{
|
3670
|
-
name: 'quad9-doh-ip4-port443-filter-ecs-pri',
|
3671
|
-
endpoint: {
|
3672
|
-
protocol: 'https:',
|
3673
|
-
host: 'dns11.quad9.net',
|
3674
|
-
ipv4: '149.112.112.11'
|
3675
|
-
},
|
3676
|
-
description: 'Quad9 (anycast) dnssec/no-log/filter/ecs 9.9.9.11 - 149.112.112.11',
|
3677
|
-
country: 'United States',
|
3678
|
-
location: {
|
3679
|
-
lat: 37.751,
|
3680
|
-
long: -97.822
|
3681
|
-
},
|
3682
|
-
filter: true
|
3683
|
-
},
|
3684
|
-
{
|
3685
|
-
name: 'quad9-doh-ip4-port443-filter-pri',
|
3686
|
-
endpoint: {
|
3687
|
-
protocol: 'https:',
|
3688
|
-
host: 'dns.quad9.net',
|
3689
|
-
ipv4: '149.112.112.112'
|
3690
|
-
},
|
3691
|
-
description: 'Quad9 (anycast) dnssec/no-log/filter 9.9.9.9 - 149.112.112.9 - 149.112.112.112',
|
3692
|
-
country: 'United States',
|
3693
|
-
location: {
|
3694
|
-
lat: 37.751,
|
3695
|
-
long: -97.822
|
3696
|
-
},
|
3697
|
-
filter: true
|
3698
|
-
},
|
3699
|
-
{
|
3700
|
-
name: 'quad9-doh-ip4-port443-nofilter-ecs-pri',
|
3701
|
-
endpoint: {
|
3702
|
-
protocol: 'https:',
|
3703
|
-
host: 'dns12.quad9.net',
|
3704
|
-
ipv4: '9.9.9.12'
|
3705
|
-
},
|
3706
|
-
description: 'Quad9 (anycast) no-dnssec/no-log/no-filter/ecs 9.9.9.12 - 149.112.112.12',
|
3707
|
-
country: 'United States',
|
3708
|
-
location: {
|
3709
|
-
lat: 37.751,
|
3710
|
-
long: -97.822
|
3711
|
-
}
|
3712
|
-
},
|
3713
|
-
{
|
3714
|
-
name: 'quad9-doh-ip4-port443-nofilter-pri',
|
3715
|
-
endpoint: {
|
3716
|
-
protocol: 'https:',
|
3717
|
-
host: 'dns10.quad9.net',
|
3718
|
-
ipv4: '149.112.112.10'
|
3719
|
-
},
|
3720
|
-
description: 'Quad9 (anycast) no-dnssec/no-log/no-filter 9.9.9.10 - 149.112.112.10',
|
3721
|
-
country: 'United States',
|
3722
|
-
location: {
|
3723
|
-
lat: 37.751,
|
3724
|
-
long: -97.822
|
3725
|
-
}
|
3726
|
-
},
|
3727
|
-
{
|
3728
|
-
name: 'quad9-doh-ip6-port5053-filter-pri',
|
3729
|
-
endpoint: {
|
3730
|
-
protocol: 'https:',
|
3731
|
-
host: 'dns9.quad9.net'
|
3732
|
-
},
|
3733
|
-
description: 'Quad9 (anycast) dnssec/no-log/filter 2620:fe::fe - 2620:fe::9 - 2620:fe::fe:9',
|
3734
|
-
country: 'United States',
|
3735
|
-
location: {
|
3736
|
-
lat: 37.751,
|
3737
|
-
long: -97.822
|
3738
|
-
},
|
3739
|
-
filter: true
|
3740
|
-
},
|
3741
|
-
{
|
3742
|
-
name: 'safesurfer-doh',
|
3743
|
-
endpoint: {
|
3744
|
-
protocol: 'https:',
|
3745
|
-
host: 'doh.safesurfer.io'
|
3746
|
-
},
|
3747
|
-
description: 'Family safety focused blocklist for over 2 million adult sites, as well as phishing and malware and more.\nFree to use, paid for customizing blocking for more categories+sites and viewing usage at my.safesurfer.io. Logs taken for viewing\nusage, data never sold - https://safesurfer.io',
|
3748
|
-
filter: true,
|
3749
|
-
log: true
|
3750
|
-
},
|
3751
|
-
{
|
3752
|
-
name: 'sth-ads-doh-se',
|
3753
|
-
endpoint: {
|
3754
|
-
protocol: 'https:',
|
3755
|
-
host: 'dnsse-noads.alekberg.net'
|
3756
|
-
},
|
3757
|
-
description: 'Resolver in Stockholm, Sweden. DoH server. Non-logging, remove ads and malware, DNSSEC.',
|
3758
|
-
country: 'Bulgaria',
|
3759
|
-
location: {
|
3760
|
-
lat: 42.696,
|
3761
|
-
long: 23.332
|
3762
|
-
},
|
3763
|
-
filter: true
|
3764
|
-
},
|
3765
|
-
{
|
3766
|
-
name: 'sth-doh-se',
|
3767
|
-
endpoint: {
|
3768
|
-
protocol: 'https:',
|
3769
|
-
host: 'dnsse.alekberg.net'
|
3770
|
-
},
|
3771
|
-
description: 'Resolver in Stockholm, Sweden. DoH server. Non-logging, non-filtering, DNSSEC.',
|
3772
|
-
country: 'Bulgaria',
|
3773
|
-
location: {
|
3774
|
-
lat: 42.696,
|
3775
|
-
long: 23.332
|
3776
|
-
}
|
3777
|
-
},
|
3778
|
-
{
|
3779
|
-
name: 'switch',
|
3780
|
-
endpoint: {
|
3781
|
-
protocol: 'https:',
|
3782
|
-
host: 'dns.switch.ch'
|
3783
|
-
},
|
3784
|
-
description: 'Public DoH service provided by SWITCH in Switzerland\nhttps://www.switch.ch\nProvides protection against malware, but does not block ads.',
|
3785
|
-
filter: true
|
3786
|
-
},
|
3787
|
-
{
|
3788
|
-
name: 'uncensoreddns-dk-ipv4',
|
3789
|
-
endpoint: {
|
3790
|
-
protocol: 'https:',
|
3791
|
-
host: 'unicast.uncensoreddns.org'
|
3792
|
-
},
|
3793
|
-
description: 'Also known as censurfridns.\nDoH, no logs, no filter, DNSSEC, unicast hosted in Denmark - https://blog.uncensoreddns.org',
|
3794
|
-
country: 'Denmark',
|
3795
|
-
location: {
|
3796
|
-
lat: 55.7123,
|
3797
|
-
long: 12.0564
|
3798
|
-
}
|
3799
|
-
},
|
3800
|
-
{
|
3801
|
-
name: 'uncensoreddns-ipv4',
|
3802
|
-
endpoint: {
|
3803
|
-
protocol: 'https:',
|
3804
|
-
host: 'anycast.uncensoreddns.org'
|
3805
|
-
},
|
3806
|
-
description: 'Also known as censurfridns.\nDoH, no logs, no filter, DNSSEC, anycast - https://blog.uncensoreddns.org',
|
3807
|
-
country: 'Denmark',
|
3808
|
-
location: {
|
3809
|
-
lat: 55.7123,
|
3810
|
-
long: 12.0564
|
3811
|
-
}
|
3812
|
-
},
|
3813
|
-
{
|
3814
|
-
name: 'v.dnscrypt.uk-doh-ipv4',
|
3815
|
-
endpoint: {
|
3816
|
-
protocol: 'https:',
|
3817
|
-
host: 'v.dnscrypt.uk'
|
3818
|
-
},
|
3819
|
-
description: 'DoH, no logs, uncensored, DNSSEC. Hosted in London UK on Digital Ocean\nhttps://www.dnscrypt.uk',
|
3820
|
-
country: 'United Kingdom',
|
3821
|
-
location: {
|
3822
|
-
lat: 51.4964,
|
3823
|
-
long: -0.1224
|
3824
|
-
}
|
3825
|
-
}
|
3826
|
-
],
|
3827
|
-
time: 1654187067783
|
3828
|
-
};
|
3829
|
-
|
3830
|
-
function processResolvers (res) {
|
3831
|
-
const time = (res.time === null || res.time === undefined) ? Date.now() : res.time;
|
3832
|
-
const resolvers = processResolvers$1(res.data.map(resolver => {
|
3833
|
-
resolver.endpoint = toEndpoint(Object.assign({ name: resolver.name }, resolver.endpoint));
|
3834
|
-
return resolver
|
3835
|
-
}));
|
3836
|
-
const endpoints = resolvers.map(resolver => resolver.endpoint);
|
3837
|
-
return {
|
3838
|
-
data: {
|
3839
|
-
resolvers,
|
3840
|
-
resolverByName: resolvers.reduce((byName, resolver) => {
|
3841
|
-
byName[resolver.name] = resolver;
|
3842
|
-
return byName
|
3843
|
-
}, {}),
|
3844
|
-
endpoints,
|
3845
|
-
endpointByName: endpoints.reduce((byName, endpoint) => {
|
3846
|
-
byName[endpoint.name] = endpoint;
|
3847
|
-
return byName
|
3848
|
-
}, {})
|
3849
|
-
},
|
3850
|
-
time
|
3851
|
-
}
|
3852
|
-
}
|
3853
|
-
|
3854
|
-
const backup = processResolvers(resolvers);
|
3855
|
-
|
3856
|
-
function toMultiQuery (singleQuery) {
|
3857
|
-
const query = Object.assign({
|
3858
|
-
type: 'query'
|
3859
|
-
}, singleQuery);
|
3860
|
-
delete query.question;
|
3861
|
-
query.questions = [];
|
3862
|
-
if (singleQuery.question) {
|
3863
|
-
query.questions.push(singleQuery.question);
|
3864
|
-
}
|
3865
|
-
return query
|
3866
|
-
}
|
3867
|
-
|
3868
|
-
function queryOne (endpoint, query, timeout, abortSignal) {
|
3869
|
-
if (abortSignal && abortSignal.aborted) {
|
3870
|
-
return Promise.reject(new AbortError())
|
3871
|
-
}
|
3872
|
-
if (endpoint.protocol === 'udp4:' || endpoint.protocol === 'udp6:') {
|
3873
|
-
return queryDns()
|
3874
|
-
}
|
3875
|
-
return queryDoh(endpoint, query, timeout, abortSignal)
|
3876
|
-
}
|
3877
|
-
|
3878
|
-
function queryDoh (endpoint, query, timeout, abortSignal) {
|
3879
|
-
return request(
|
3880
|
-
endpoint.url,
|
3881
|
-
endpoint.method,
|
3882
|
-
encode(Object.assign({
|
3883
|
-
flags: RECURSION_DESIRED
|
3884
|
-
}, query)),
|
3885
|
-
timeout,
|
3886
|
-
abortSignal
|
3887
|
-
).then(
|
3888
|
-
function (res) {
|
3889
|
-
const data = res.data;
|
3890
|
-
const response = res.response;
|
3891
|
-
let error = res.error;
|
3892
|
-
if (error === undefined) {
|
3893
|
-
if (data.length === 0) {
|
3894
|
-
error = new ResponseError('Empty.');
|
3895
|
-
} else {
|
3896
|
-
try {
|
3897
|
-
const decoded = decode(data);
|
3898
|
-
decoded.response = response;
|
3899
|
-
return decoded
|
3900
|
-
} catch (err) {
|
3901
|
-
error = new ResponseError('Invalid packet (cause=' + err.message + ')', err);
|
3902
|
-
}
|
3903
|
-
}
|
3904
|
-
}
|
3905
|
-
throw Object.assign(error, { response })
|
3906
|
-
}
|
3907
|
-
)
|
3908
|
-
}
|
3909
|
-
|
3910
|
-
const UPDATE_URL = new URL('https://martinheidegger.github.io/dns-query/resolvers.json');
|
3911
|
-
|
3912
|
-
function isNameString (entry) {
|
3913
|
-
return /^@/.test(entry)
|
3914
|
-
}
|
3915
|
-
|
3916
|
-
class Wellknown {
|
3917
|
-
constructor (opts) {
|
3918
|
-
this.opts = Object.assign({
|
3919
|
-
timeout: 5000,
|
3920
|
-
update: true,
|
3921
|
-
updateURL: UPDATE_URL,
|
3922
|
-
persist: false,
|
3923
|
-
localStoragePrefix: 'dnsquery_',
|
3924
|
-
maxAge: 300000 // 5 minutes
|
3925
|
-
}, opts);
|
3926
|
-
this._dataP = null;
|
3927
|
-
}
|
3928
|
-
|
3929
|
-
_data (force, outdated) {
|
3930
|
-
if (!force && this._dataP !== null) {
|
3931
|
-
return this._dataP.then(res => {
|
3932
|
-
if (res.time < Date.now() - this.opts.maxAge) {
|
3933
|
-
return this._data(true, res)
|
3934
|
-
}
|
3935
|
-
return res
|
3936
|
-
})
|
3937
|
-
}
|
3938
|
-
this._dataP = (!this.opts.update
|
3939
|
-
? Promise.resolve(backup)
|
3940
|
-
: loadJSON(
|
3941
|
-
this.opts.updateURL,
|
3942
|
-
this.opts.persist
|
3943
|
-
? {
|
3944
|
-
name: 'resolvers.json',
|
3945
|
-
localStoragePrefix: this.opts.localStoragePrefix,
|
3946
|
-
maxTime: Date.now() - this.opts.maxAge
|
3947
|
-
}
|
3948
|
-
: null,
|
3949
|
-
this.opts.timeout
|
3950
|
-
)
|
3951
|
-
.then(res => processResolvers({
|
3952
|
-
data: res.data.resolvers,
|
3953
|
-
time: res.time
|
3954
|
-
}))
|
3955
|
-
.catch(() => outdated || backup)
|
3956
|
-
);
|
3957
|
-
return this._dataP
|
3958
|
-
}
|
3959
|
-
|
3960
|
-
data () {
|
3961
|
-
return this._data(false).then(data => data.data)
|
3962
|
-
}
|
3963
|
-
|
3964
|
-
endpoints (input) {
|
3965
|
-
if (input === null || input === undefined) {
|
3966
|
-
return this.data().then(data => data.endpoints)
|
3967
|
-
}
|
3968
|
-
if (input === 'doh') {
|
3969
|
-
input = filterDoh;
|
3970
|
-
}
|
3971
|
-
if (input === 'dns') {
|
3972
|
-
input = filterDns;
|
3973
|
-
}
|
3974
|
-
if (typeof input === 'function') {
|
3975
|
-
return this.data().then(data => data.endpoints.filter(input))
|
3976
|
-
}
|
3977
|
-
if (typeof input === 'string' || typeof input[Symbol.iterator] !== 'function') {
|
3978
|
-
return Promise.reject(new Error(`Endpoints (${input}) needs to be iterable (array).`))
|
3979
|
-
}
|
3980
|
-
input = Array.from(input).filter(Boolean);
|
3981
|
-
if (input.findIndex(isNameString) === -1) {
|
3982
|
-
try {
|
3983
|
-
return Promise.resolve(input.map(toEndpoint))
|
3984
|
-
} catch (err) {
|
3985
|
-
return Promise.reject(err)
|
3986
|
-
}
|
3987
|
-
}
|
3988
|
-
return this.data().then(data =>
|
3989
|
-
input.map(entry => {
|
3990
|
-
if (isNameString(entry)) {
|
3991
|
-
const found = data.endpointByName[entry.substring(1)];
|
3992
|
-
if (!found) {
|
3993
|
-
throw new Error(`Endpoint ${entry} is not known.`)
|
3994
|
-
}
|
3995
|
-
return found
|
3996
|
-
}
|
3997
|
-
return toEndpoint(entry)
|
3998
|
-
})
|
3999
|
-
)
|
4000
|
-
}
|
4001
|
-
}
|
4002
|
-
|
4003
|
-
new Wellknown();
|
4004
|
-
|
4005
|
-
function isPromise (input) {
|
4006
|
-
if (input === null) {
|
4007
|
-
return false
|
4008
|
-
}
|
4009
|
-
if (typeof input !== 'object') {
|
4010
|
-
return false
|
4011
|
-
}
|
4012
|
-
return typeof input.then === 'function'
|
4013
|
-
}
|
4014
|
-
|
4015
|
-
function toPromise (input) {
|
4016
|
-
return isPromise(input) ? input : Promise.resolve(input)
|
4017
|
-
}
|
4018
|
-
|
4019
|
-
function query (q, opts) {
|
4020
|
-
opts = Object.assign({
|
4021
|
-
retries: 5,
|
4022
|
-
timeout: 30000 // 30 seconds
|
4023
|
-
}, opts);
|
4024
|
-
if (!q.question) return Promise.reject(new Error('To request data you need to specify a .question!'))
|
4025
|
-
return toPromise(opts.endpoints)
|
4026
|
-
.then(endpoints => {
|
4027
|
-
if (!Array.isArray(endpoints) || endpoints.length === 0) {
|
4028
|
-
throw new Error('No endpoints defined to lookup dns records.')
|
4029
|
-
}
|
4030
|
-
return queryN(endpoints.map(toEndpoint), toMultiQuery(q), opts)
|
4031
|
-
})
|
4032
|
-
.then(data => {
|
4033
|
-
data.question = data.questions[0];
|
4034
|
-
delete data.questions;
|
4035
|
-
return data
|
4036
|
-
})
|
4037
|
-
}
|
4038
|
-
|
4039
|
-
function queryN (endpoints, q, opts) {
|
4040
|
-
const endpoint = endpoints.length === 1
|
4041
|
-
? endpoints[0]
|
4042
|
-
: endpoints[Math.floor(Math.random() * endpoints.length) % endpoints.length];
|
4043
|
-
return queryOne(endpoint, q, opts.timeout, opts.signal)
|
4044
|
-
.then(
|
4045
|
-
data => {
|
4046
|
-
// Add the endpoint to give a chance to identify which endpoint returned the result
|
4047
|
-
data.endpoint = endpoint.toString();
|
4048
|
-
return data
|
4049
|
-
},
|
4050
|
-
err => {
|
4051
|
-
if (err.name === 'AbortError' || opts.retries === 0) {
|
4052
|
-
err.endpoint = endpoint.toString();
|
4053
|
-
throw err
|
4054
|
-
}
|
4055
|
-
if (opts.retries > 0) {
|
4056
|
-
opts.retries -= 1;
|
4057
|
-
}
|
4058
|
-
return queryN(endpoints, q, opts)
|
4059
|
-
}
|
4060
|
-
)
|
4061
|
-
}
|
4062
|
-
|
4063
|
-
function filterDoh (endpoint) {
|
4064
|
-
return endpoint.protocol === 'https:' || endpoint.protocol === 'http:'
|
4065
|
-
}
|
4066
|
-
|
4067
|
-
function filterDns (endpoint) {
|
4068
|
-
return endpoint.protocol === 'udp4:' || endpoint.protocol === 'udp6:'
|
4069
|
-
}
|
4070
|
-
|
4071
|
-
const log$3 = debug("waku:dns-over-https");
|
4072
|
-
class DnsOverHttps {
|
4073
|
-
/**
|
4074
|
-
* Create new Dns-Over-Http DNS client.
|
4075
|
-
*
|
4076
|
-
* @param endpoints The endpoints for Dns-Over-Https queries;
|
4077
|
-
* Defaults to [[DnsOverHttps.DefaultEndpoints]].
|
4078
|
-
* @param retries Retries if a given endpoint fails.
|
4079
|
-
*
|
4080
|
-
* @throws {code: string} If DNS query fails.
|
4081
|
-
*/
|
4082
|
-
constructor(endpoints = DnsOverHttps.DefaultEndpoints, retries = 3) {
|
4083
|
-
this.endpoints = endpoints;
|
4084
|
-
this.retries = retries;
|
4085
|
-
}
|
4086
|
-
/**
|
4087
|
-
* Resolves a TXT record
|
4088
|
-
*
|
4089
|
-
* @param domain The domain name
|
4090
|
-
*
|
4091
|
-
* @throws if the query fails
|
4092
|
-
*/
|
4093
|
-
async resolveTXT(domain) {
|
4094
|
-
let answers;
|
4095
|
-
try {
|
4096
|
-
const res = await query({
|
4097
|
-
question: { type: "TXT", name: domain },
|
4098
|
-
}, {
|
4099
|
-
endpoints: this.endpoints,
|
4100
|
-
retries: this.retries,
|
4101
|
-
});
|
4102
|
-
answers = res.answers;
|
4103
|
-
}
|
4104
|
-
catch (error) {
|
4105
|
-
log$3("query failed: ", error);
|
4106
|
-
throw new Error("DNS query failed");
|
4107
|
-
}
|
4108
|
-
if (!answers)
|
4109
|
-
throw new Error(`Could not resolve ${domain}`);
|
4110
|
-
const data = answers.map((a) => a.data);
|
4111
|
-
const result = [];
|
4112
|
-
data.forEach((d) => {
|
4113
|
-
if (typeof d === "string") {
|
4114
|
-
result.push(d);
|
4115
|
-
}
|
4116
|
-
else if (Array.isArray(d)) {
|
4117
|
-
d.forEach((sd) => {
|
4118
|
-
if (typeof sd === "string") {
|
4119
|
-
result.push(sd);
|
4120
|
-
}
|
4121
|
-
else {
|
4122
|
-
result.push(bytesToUtf8(sd));
|
4123
|
-
}
|
4124
|
-
});
|
4125
|
-
}
|
4126
|
-
else {
|
4127
|
-
result.push(bytesToUtf8(d));
|
4128
|
-
}
|
4129
|
-
});
|
4130
|
-
return result;
|
4131
|
-
}
|
4132
|
-
}
|
4133
|
-
/**
|
4134
|
-
* Default endpoints to use for DNS queries.
|
4135
|
-
* Taken from https://github.com/martinheidegger/dns-query as static data
|
4136
|
-
* to avoid dynamic queries.
|
4137
|
-
*
|
4138
|
-
* To dynamically retrieve other endpoints, use https://github.com/martinheidegger/dns-query#well-known-endpoints
|
4139
|
-
*/
|
4140
|
-
DnsOverHttps.DefaultEndpoints = [
|
4141
|
-
toEndpoint({
|
4142
|
-
name: "cisco-doh",
|
4143
|
-
protocol: "https:",
|
4144
|
-
host: "doh.opendns.com",
|
4145
|
-
ipv4: "146.112.41.2",
|
4146
|
-
}),
|
4147
|
-
toEndpoint({
|
4148
|
-
name: "cloudflare",
|
4149
|
-
protocol: "https:",
|
4150
|
-
host: "dns.cloudflare.com",
|
4151
|
-
ipv4: "1.0.0.1",
|
4152
|
-
}),
|
4153
|
-
];
|
4154
|
-
|
4155
|
-
var base32$1 = {exports: {}};
|
4156
|
-
|
4157
|
-
/*
|
4158
|
-
* [hi-base32]{@link https://github.com/emn178/hi-base32}
|
4159
|
-
*
|
4160
|
-
* @version 0.5.0
|
4161
|
-
* @author Chen, Yi-Cyuan [emn178@gmail.com]
|
4162
|
-
* @copyright Chen, Yi-Cyuan 2015-2018
|
4163
|
-
* @license MIT
|
4164
|
-
*/
|
4165
|
-
|
4166
|
-
(function (module) {
|
4167
|
-
/*jslint bitwise: true */
|
4168
|
-
(function () {
|
4169
|
-
|
4170
|
-
var root = typeof window === 'object' ? window : {};
|
4171
|
-
var NODE_JS = !root.HI_BASE32_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
|
4172
|
-
if (NODE_JS) {
|
4173
|
-
root = commonjsGlobal;
|
4174
|
-
}
|
4175
|
-
var COMMON_JS = !root.HI_BASE32_NO_COMMON_JS && 'object' === 'object' && module.exports;
|
4176
|
-
var BASE32_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.split('');
|
4177
|
-
var BASE32_DECODE_CHAR = {
|
4178
|
-
'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8,
|
4179
|
-
'J': 9, 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16,
|
4180
|
-
'R': 17, 'S': 18, 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24,
|
4181
|
-
'Z': 25, '2': 26, '3': 27, '4': 28, '5': 29, '6': 30, '7': 31
|
4182
|
-
};
|
4183
|
-
|
4184
|
-
var blocks = [0, 0, 0, 0, 0, 0, 0, 0];
|
4185
|
-
|
4186
|
-
var throwInvalidUtf8 = function (position, partial) {
|
4187
|
-
if (partial.length > 10) {
|
4188
|
-
partial = '...' + partial.substr(-10);
|
4189
|
-
}
|
4190
|
-
var err = new Error('Decoded data is not valid UTF-8.'
|
4191
|
-
+ ' Maybe try base32.decode.asBytes()?'
|
4192
|
-
+ ' Partial data after reading ' + position + ' bytes: ' + partial + ' <-');
|
4193
|
-
err.position = position;
|
4194
|
-
throw err;
|
4195
|
-
};
|
4196
|
-
|
4197
|
-
var toUtf8String = function (bytes) {
|
4198
|
-
var str = '', length = bytes.length, i = 0, followingChars = 0, b, c;
|
4199
|
-
while (i < length) {
|
4200
|
-
b = bytes[i++];
|
4201
|
-
if (b <= 0x7F) {
|
4202
|
-
str += String.fromCharCode(b);
|
4203
|
-
continue;
|
4204
|
-
} else if (b > 0xBF && b <= 0xDF) {
|
4205
|
-
c = b & 0x1F;
|
4206
|
-
followingChars = 1;
|
4207
|
-
} else if (b <= 0xEF) {
|
4208
|
-
c = b & 0x0F;
|
4209
|
-
followingChars = 2;
|
4210
|
-
} else if (b <= 0xF7) {
|
4211
|
-
c = b & 0x07;
|
4212
|
-
followingChars = 3;
|
4213
|
-
} else {
|
4214
|
-
throwInvalidUtf8(i, str);
|
4215
|
-
}
|
4216
|
-
|
4217
|
-
for (var j = 0; j < followingChars; ++j) {
|
4218
|
-
b = bytes[i++];
|
4219
|
-
if (b < 0x80 || b > 0xBF) {
|
4220
|
-
throwInvalidUtf8(i, str);
|
4221
|
-
}
|
4222
|
-
c <<= 6;
|
4223
|
-
c += b & 0x3F;
|
4224
|
-
}
|
4225
|
-
if (c >= 0xD800 && c <= 0xDFFF) {
|
4226
|
-
throwInvalidUtf8(i, str);
|
4227
|
-
}
|
4228
|
-
if (c > 0x10FFFF) {
|
4229
|
-
throwInvalidUtf8(i, str);
|
4230
|
-
}
|
4231
|
-
|
4232
|
-
if (c <= 0xFFFF) {
|
4233
|
-
str += String.fromCharCode(c);
|
4234
|
-
} else {
|
4235
|
-
c -= 0x10000;
|
4236
|
-
str += String.fromCharCode((c >> 10) + 0xD800);
|
4237
|
-
str += String.fromCharCode((c & 0x3FF) + 0xDC00);
|
4238
|
-
}
|
4239
|
-
}
|
4240
|
-
return str;
|
4241
|
-
};
|
4242
|
-
|
4243
|
-
var decodeAsBytes = function (base32Str) {
|
4244
|
-
if (base32Str === '') {
|
4245
|
-
return [];
|
4246
|
-
} else if (!/^[A-Z2-7=]+$/.test(base32Str)) {
|
4247
|
-
throw new Error('Invalid base32 characters');
|
4248
|
-
}
|
4249
|
-
base32Str = base32Str.replace(/=/g, '');
|
4250
|
-
var v1, v2, v3, v4, v5, v6, v7, v8, bytes = [], index = 0, length = base32Str.length;
|
4251
|
-
|
4252
|
-
// 4 char to 3 bytes
|
4253
|
-
for (var i = 0, count = length >> 3 << 3; i < count;) {
|
4254
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4255
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4256
|
-
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4257
|
-
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4258
|
-
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4259
|
-
v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4260
|
-
v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4261
|
-
v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4262
|
-
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
4263
|
-
bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
|
4264
|
-
bytes[index++] = (v4 << 4 | v5 >>> 1) & 255;
|
4265
|
-
bytes[index++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255;
|
4266
|
-
bytes[index++] = (v7 << 5 | v8) & 255;
|
4267
|
-
}
|
4268
|
-
|
4269
|
-
// remain bytes
|
4270
|
-
var remain = length - count;
|
4271
|
-
if (remain === 2) {
|
4272
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4273
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4274
|
-
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
4275
|
-
} else if (remain === 4) {
|
4276
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4277
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4278
|
-
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4279
|
-
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4280
|
-
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
4281
|
-
bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
|
4282
|
-
} else if (remain === 5) {
|
4283
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4284
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4285
|
-
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4286
|
-
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4287
|
-
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4288
|
-
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
4289
|
-
bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
|
4290
|
-
bytes[index++] = (v4 << 4 | v5 >>> 1) & 255;
|
4291
|
-
} else if (remain === 7) {
|
4292
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4293
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4294
|
-
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4295
|
-
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4296
|
-
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4297
|
-
v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4298
|
-
v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4299
|
-
bytes[index++] = (v1 << 3 | v2 >>> 2) & 255;
|
4300
|
-
bytes[index++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
|
4301
|
-
bytes[index++] = (v4 << 4 | v5 >>> 1) & 255;
|
4302
|
-
bytes[index++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255;
|
4303
|
-
}
|
4304
|
-
return bytes;
|
4305
|
-
};
|
4306
|
-
|
4307
|
-
var encodeAscii = function (str) {
|
4308
|
-
var v1, v2, v3, v4, v5, base32Str = '', length = str.length;
|
4309
|
-
for (var i = 0, count = parseInt(length / 5) * 5; i < count;) {
|
4310
|
-
v1 = str.charCodeAt(i++);
|
4311
|
-
v2 = str.charCodeAt(i++);
|
4312
|
-
v3 = str.charCodeAt(i++);
|
4313
|
-
v4 = str.charCodeAt(i++);
|
4314
|
-
v5 = str.charCodeAt(i++);
|
4315
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4316
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4317
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4318
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4319
|
-
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
4320
|
-
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
4321
|
-
BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] +
|
4322
|
-
BASE32_ENCODE_CHAR[v5 & 31];
|
4323
|
-
}
|
4324
|
-
|
4325
|
-
// remain char
|
4326
|
-
var remain = length - count;
|
4327
|
-
if (remain === 1) {
|
4328
|
-
v1 = str.charCodeAt(i);
|
4329
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4330
|
-
BASE32_ENCODE_CHAR[(v1 << 2) & 31] +
|
4331
|
-
'======';
|
4332
|
-
} else if (remain === 2) {
|
4333
|
-
v1 = str.charCodeAt(i++);
|
4334
|
-
v2 = str.charCodeAt(i);
|
4335
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4336
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4337
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4338
|
-
BASE32_ENCODE_CHAR[(v2 << 4) & 31] +
|
4339
|
-
'====';
|
4340
|
-
} else if (remain === 3) {
|
4341
|
-
v1 = str.charCodeAt(i++);
|
4342
|
-
v2 = str.charCodeAt(i++);
|
4343
|
-
v3 = str.charCodeAt(i);
|
4344
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4345
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4346
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4347
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4348
|
-
BASE32_ENCODE_CHAR[(v3 << 1) & 31] +
|
4349
|
-
'===';
|
4350
|
-
} else if (remain === 4) {
|
4351
|
-
v1 = str.charCodeAt(i++);
|
4352
|
-
v2 = str.charCodeAt(i++);
|
4353
|
-
v3 = str.charCodeAt(i++);
|
4354
|
-
v4 = str.charCodeAt(i);
|
4355
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4356
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4357
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4358
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4359
|
-
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
4360
|
-
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
4361
|
-
BASE32_ENCODE_CHAR[(v4 << 3) & 31] +
|
4362
|
-
'=';
|
4363
|
-
}
|
4364
|
-
return base32Str;
|
4365
|
-
};
|
4366
|
-
|
4367
|
-
var encodeUtf8 = function (str) {
|
4368
|
-
var v1, v2, v3, v4, v5, code, end = false, base32Str = '',
|
4369
|
-
index = 0, i, start = 0, length = str.length;
|
4370
|
-
if (str === '') {
|
4371
|
-
return base32Str;
|
4372
|
-
}
|
4373
|
-
do {
|
4374
|
-
blocks[0] = blocks[5];
|
4375
|
-
blocks[1] = blocks[6];
|
4376
|
-
blocks[2] = blocks[7];
|
4377
|
-
for (i = start; index < length && i < 5; ++index) {
|
4378
|
-
code = str.charCodeAt(index);
|
4379
|
-
if (code < 0x80) {
|
4380
|
-
blocks[i++] = code;
|
4381
|
-
} else if (code < 0x800) {
|
4382
|
-
blocks[i++] = 0xc0 | (code >> 6);
|
4383
|
-
blocks[i++] = 0x80 | (code & 0x3f);
|
4384
|
-
} else if (code < 0xd800 || code >= 0xe000) {
|
4385
|
-
blocks[i++] = 0xe0 | (code >> 12);
|
4386
|
-
blocks[i++] = 0x80 | ((code >> 6) & 0x3f);
|
4387
|
-
blocks[i++] = 0x80 | (code & 0x3f);
|
4388
|
-
} else {
|
4389
|
-
code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(++index) & 0x3ff));
|
4390
|
-
blocks[i++] = 0xf0 | (code >> 18);
|
4391
|
-
blocks[i++] = 0x80 | ((code >> 12) & 0x3f);
|
4392
|
-
blocks[i++] = 0x80 | ((code >> 6) & 0x3f);
|
4393
|
-
blocks[i++] = 0x80 | (code & 0x3f);
|
4394
|
-
}
|
4395
|
-
}
|
4396
|
-
start = i - 5;
|
4397
|
-
if (index === length) {
|
4398
|
-
++index;
|
4399
|
-
}
|
4400
|
-
if (index > length && i < 6) {
|
4401
|
-
end = true;
|
4402
|
-
}
|
4403
|
-
v1 = blocks[0];
|
4404
|
-
if (i > 4) {
|
4405
|
-
v2 = blocks[1];
|
4406
|
-
v3 = blocks[2];
|
4407
|
-
v4 = blocks[3];
|
4408
|
-
v5 = blocks[4];
|
4409
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4410
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4411
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4412
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4413
|
-
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
4414
|
-
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
4415
|
-
BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] +
|
4416
|
-
BASE32_ENCODE_CHAR[v5 & 31];
|
4417
|
-
} else if (i === 1) {
|
4418
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4419
|
-
BASE32_ENCODE_CHAR[(v1 << 2) & 31] +
|
4420
|
-
'======';
|
4421
|
-
} else if (i === 2) {
|
4422
|
-
v2 = blocks[1];
|
4423
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4424
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4425
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4426
|
-
BASE32_ENCODE_CHAR[(v2 << 4) & 31] +
|
4427
|
-
'====';
|
4428
|
-
} else if (i === 3) {
|
4429
|
-
v2 = blocks[1];
|
4430
|
-
v3 = blocks[2];
|
4431
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4432
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4433
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4434
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4435
|
-
BASE32_ENCODE_CHAR[(v3 << 1) & 31] +
|
4436
|
-
'===';
|
4437
|
-
} else {
|
4438
|
-
v2 = blocks[1];
|
4439
|
-
v3 = blocks[2];
|
4440
|
-
v4 = blocks[3];
|
4441
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4442
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4443
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4444
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4445
|
-
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
4446
|
-
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
4447
|
-
BASE32_ENCODE_CHAR[(v4 << 3) & 31] +
|
4448
|
-
'=';
|
4449
|
-
}
|
4450
|
-
} while (!end);
|
4451
|
-
return base32Str;
|
4452
|
-
};
|
4453
|
-
|
4454
|
-
var encodeBytes = function (bytes) {
|
4455
|
-
var v1, v2, v3, v4, v5, base32Str = '', length = bytes.length;
|
4456
|
-
for (var i = 0, count = parseInt(length / 5) * 5; i < count;) {
|
4457
|
-
v1 = bytes[i++];
|
4458
|
-
v2 = bytes[i++];
|
4459
|
-
v3 = bytes[i++];
|
4460
|
-
v4 = bytes[i++];
|
4461
|
-
v5 = bytes[i++];
|
4462
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4463
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4464
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4465
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4466
|
-
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
4467
|
-
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
4468
|
-
BASE32_ENCODE_CHAR[(v4 << 3 | v5 >>> 5) & 31] +
|
4469
|
-
BASE32_ENCODE_CHAR[v5 & 31];
|
4470
|
-
}
|
4471
|
-
|
4472
|
-
// remain char
|
4473
|
-
var remain = length - count;
|
4474
|
-
if (remain === 1) {
|
4475
|
-
v1 = bytes[i];
|
4476
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4477
|
-
BASE32_ENCODE_CHAR[(v1 << 2) & 31] +
|
4478
|
-
'======';
|
4479
|
-
} else if (remain === 2) {
|
4480
|
-
v1 = bytes[i++];
|
4481
|
-
v2 = bytes[i];
|
4482
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4483
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4484
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4485
|
-
BASE32_ENCODE_CHAR[(v2 << 4) & 31] +
|
4486
|
-
'====';
|
4487
|
-
} else if (remain === 3) {
|
4488
|
-
v1 = bytes[i++];
|
4489
|
-
v2 = bytes[i++];
|
4490
|
-
v3 = bytes[i];
|
4491
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4492
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4493
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4494
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4495
|
-
BASE32_ENCODE_CHAR[(v3 << 1) & 31] +
|
4496
|
-
'===';
|
4497
|
-
} else if (remain === 4) {
|
4498
|
-
v1 = bytes[i++];
|
4499
|
-
v2 = bytes[i++];
|
4500
|
-
v3 = bytes[i++];
|
4501
|
-
v4 = bytes[i];
|
4502
|
-
base32Str += BASE32_ENCODE_CHAR[v1 >>> 3] +
|
4503
|
-
BASE32_ENCODE_CHAR[(v1 << 2 | v2 >>> 6) & 31] +
|
4504
|
-
BASE32_ENCODE_CHAR[(v2 >>> 1) & 31] +
|
4505
|
-
BASE32_ENCODE_CHAR[(v2 << 4 | v3 >>> 4) & 31] +
|
4506
|
-
BASE32_ENCODE_CHAR[(v3 << 1 | v4 >>> 7) & 31] +
|
4507
|
-
BASE32_ENCODE_CHAR[(v4 >>> 2) & 31] +
|
4508
|
-
BASE32_ENCODE_CHAR[(v4 << 3) & 31] +
|
4509
|
-
'=';
|
4510
|
-
}
|
4511
|
-
return base32Str;
|
4512
|
-
};
|
4513
|
-
|
4514
|
-
var encode = function (input, asciiOnly) {
|
4515
|
-
var notString = typeof(input) !== 'string';
|
4516
|
-
if (notString && input.constructor === ArrayBuffer) {
|
4517
|
-
input = new Uint8Array(input);
|
4518
|
-
}
|
4519
|
-
if (notString) {
|
4520
|
-
return encodeBytes(input);
|
4521
|
-
} else if (asciiOnly) {
|
4522
|
-
return encodeAscii(input);
|
4523
|
-
} else {
|
4524
|
-
return encodeUtf8(input);
|
4525
|
-
}
|
4526
|
-
};
|
4527
|
-
|
4528
|
-
var decode = function (base32Str, asciiOnly) {
|
4529
|
-
if (!asciiOnly) {
|
4530
|
-
return toUtf8String(decodeAsBytes(base32Str));
|
4531
|
-
}
|
4532
|
-
if (base32Str === '') {
|
4533
|
-
return '';
|
4534
|
-
} else if (!/^[A-Z2-7=]+$/.test(base32Str)) {
|
4535
|
-
throw new Error('Invalid base32 characters');
|
4536
|
-
}
|
4537
|
-
var v1, v2, v3, v4, v5, v6, v7, v8, str = '', length = base32Str.indexOf('=');
|
4538
|
-
if (length === -1) {
|
4539
|
-
length = base32Str.length;
|
4540
|
-
}
|
4541
|
-
|
4542
|
-
// 8 char to 5 bytes
|
4543
|
-
for (var i = 0, count = length >> 3 << 3; i < count;) {
|
4544
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4545
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4546
|
-
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4547
|
-
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4548
|
-
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4549
|
-
v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4550
|
-
v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4551
|
-
v8 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4552
|
-
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) +
|
4553
|
-
String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) +
|
4554
|
-
String.fromCharCode((v4 << 4 | v5 >>> 1) & 255) +
|
4555
|
-
String.fromCharCode((v5 << 7 | v6 << 2 | v7 >>> 3) & 255) +
|
4556
|
-
String.fromCharCode((v7 << 5 | v8) & 255);
|
4557
|
-
}
|
4558
|
-
|
4559
|
-
// remain bytes
|
4560
|
-
var remain = length - count;
|
4561
|
-
if (remain === 2) {
|
4562
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4563
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4564
|
-
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255);
|
4565
|
-
} else if (remain === 4) {
|
4566
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4567
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4568
|
-
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4569
|
-
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4570
|
-
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) +
|
4571
|
-
String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255);
|
4572
|
-
} else if (remain === 5) {
|
4573
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4574
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4575
|
-
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4576
|
-
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4577
|
-
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4578
|
-
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) +
|
4579
|
-
String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) +
|
4580
|
-
String.fromCharCode((v4 << 4 | v5 >>> 1) & 255);
|
4581
|
-
} else if (remain === 7) {
|
4582
|
-
v1 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4583
|
-
v2 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4584
|
-
v3 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4585
|
-
v4 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4586
|
-
v5 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4587
|
-
v6 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4588
|
-
v7 = BASE32_DECODE_CHAR[base32Str.charAt(i++)];
|
4589
|
-
str += String.fromCharCode((v1 << 3 | v2 >>> 2) & 255) +
|
4590
|
-
String.fromCharCode((v2 << 6 | v3 << 1 | v4 >>> 4) & 255) +
|
4591
|
-
String.fromCharCode((v4 << 4 | v5 >>> 1) & 255) +
|
4592
|
-
String.fromCharCode((v5 << 7 | v6 << 2 | v7 >>> 3) & 255);
|
4593
|
-
}
|
4594
|
-
return str;
|
4595
|
-
};
|
4596
|
-
|
4597
|
-
var exports = {
|
4598
|
-
encode: encode,
|
4599
|
-
decode: decode
|
4600
|
-
};
|
4601
|
-
decode.asBytes = decodeAsBytes;
|
4602
|
-
|
4603
|
-
if (COMMON_JS) {
|
4604
|
-
module.exports = exports;
|
4605
|
-
} else {
|
4606
|
-
root.base32 = exports;
|
4607
|
-
}
|
4608
|
-
})();
|
4609
|
-
} (base32$1));
|
4610
|
-
|
4611
|
-
var base32 = base32$1.exports;
|
4612
|
-
|
4613
|
-
class ENRTree {
|
4614
|
-
/**
|
4615
|
-
* Extracts the branch subdomain referenced by a DNS tree root string after verifying
|
4616
|
-
* the root record signature with its base32 compressed public key.
|
4617
|
-
*/
|
4618
|
-
static parseAndVerifyRoot(root, publicKey) {
|
4619
|
-
if (!root.startsWith(this.ROOT_PREFIX))
|
4620
|
-
throw new Error(`ENRTree root entry must start with '${this.ROOT_PREFIX}'`);
|
4621
|
-
const rootValues = ENRTree.parseRootValues(root);
|
4622
|
-
const decodedPublicKey = base32.decode.asBytes(publicKey);
|
4623
|
-
// The signature is a 65-byte secp256k1 over the keccak256 hash
|
4624
|
-
// of the record content, excluding the `sig=` part, encoded as URL-safe base64 string
|
4625
|
-
// (Trailing recovery bit must be trimmed to pass `ecdsaVerify` method)
|
4626
|
-
const signedComponent = root.split(" sig")[0];
|
4627
|
-
const signedComponentBuffer = utf8ToBytes(signedComponent);
|
4628
|
-
const signatureBuffer = fromString(rootValues.signature, "base64url").slice(0, 64);
|
4629
|
-
const isVerified = verifySignature(signatureBuffer, keccak256(signedComponentBuffer), new Uint8Array(decodedPublicKey));
|
4630
|
-
if (!isVerified)
|
4631
|
-
throw new Error("Unable to verify ENRTree root signature");
|
4632
|
-
return rootValues.eRoot;
|
4633
|
-
}
|
4634
|
-
static parseRootValues(txt) {
|
4635
|
-
const matches = txt.match(/^enrtree-root:v1 e=([^ ]+) l=([^ ]+) seq=(\d+) sig=([^ ]+)$/);
|
4636
|
-
if (!Array.isArray(matches))
|
4637
|
-
throw new Error("Could not parse ENRTree root entry");
|
4638
|
-
matches.shift(); // The first entry is the full match
|
4639
|
-
const [eRoot, lRoot, seq, signature] = matches;
|
4640
|
-
if (!eRoot)
|
4641
|
-
throw new Error("Could not parse 'e' value from ENRTree root entry");
|
4642
|
-
if (!lRoot)
|
4643
|
-
throw new Error("Could not parse 'l' value from ENRTree root entry");
|
4644
|
-
if (!seq)
|
4645
|
-
throw new Error("Could not parse 'seq' value from ENRTree root entry");
|
4646
|
-
if (!signature)
|
4647
|
-
throw new Error("Could not parse 'sig' value from ENRTree root entry");
|
4648
|
-
return { eRoot, lRoot, seq: Number(seq), signature };
|
4649
|
-
}
|
4650
|
-
/**
|
4651
|
-
* Returns the public key and top level domain of an ENR tree entry.
|
4652
|
-
* The domain is the starting point for traversing a set of linked DNS TXT records
|
4653
|
-
* and the public key is used to verify the root entry record
|
4654
|
-
*/
|
4655
|
-
static parseTree(tree) {
|
4656
|
-
if (!tree.startsWith(this.TREE_PREFIX))
|
4657
|
-
throw new Error(`ENRTree tree entry must start with '${this.TREE_PREFIX}'`);
|
4658
|
-
const matches = tree.match(/^enrtree:\/\/([^@]+)@(.+)$/);
|
4659
|
-
if (!Array.isArray(matches))
|
4660
|
-
throw new Error("Could not parse ENRTree tree entry");
|
4661
|
-
matches.shift(); // The first entry is the full match
|
4662
|
-
const [publicKey, domain] = matches;
|
4663
|
-
if (!publicKey)
|
4664
|
-
throw new Error("Could not parse public key from ENRTree tree entry");
|
4665
|
-
if (!domain)
|
4666
|
-
throw new Error("Could not parse domain from ENRTree tree entry");
|
4667
|
-
return { publicKey, domain };
|
4668
|
-
}
|
4669
|
-
/**
|
4670
|
-
* Returns subdomains listed in an ENR branch entry. These in turn lead to
|
4671
|
-
* either further branch entries or ENR records.
|
4672
|
-
*/
|
4673
|
-
static parseBranch(branch) {
|
4674
|
-
if (!branch.startsWith(this.BRANCH_PREFIX))
|
4675
|
-
throw new Error(`ENRTree branch entry must start with '${this.BRANCH_PREFIX}'`);
|
4676
|
-
return branch.split(this.BRANCH_PREFIX)[1].split(",");
|
4677
|
-
}
|
4678
|
-
}
|
4679
|
-
ENRTree.RECORD_PREFIX = ENR.RECORD_PREFIX;
|
4680
|
-
ENRTree.TREE_PREFIX = "enrtree:";
|
4681
|
-
ENRTree.BRANCH_PREFIX = "enrtree-branch:";
|
4682
|
-
ENRTree.ROOT_PREFIX = "enrtree-root:";
|
4683
|
-
|
4684
|
-
const log$2 = debug("waku:discovery:fetch_nodes");
|
4685
|
-
/**
|
4686
|
-
* Fetch nodes using passed [[getNode]] until all wanted capabilities are
|
4687
|
-
* fulfilled or the number of [[getNode]] call exceeds the sum of
|
4688
|
-
* [[wantedNodeCapabilityCount]] plus [[errorTolerance]].
|
4689
|
-
*/
|
4690
|
-
async function fetchNodesUntilCapabilitiesFulfilled(wantedNodeCapabilityCount, errorTolerance, getNode) {
|
4691
|
-
const wanted = {
|
4692
|
-
relay: wantedNodeCapabilityCount.relay ?? 0,
|
4693
|
-
store: wantedNodeCapabilityCount.store ?? 0,
|
4694
|
-
filter: wantedNodeCapabilityCount.filter ?? 0,
|
4695
|
-
lightPush: wantedNodeCapabilityCount.lightPush ?? 0,
|
4696
|
-
};
|
4697
|
-
const maxSearches = wanted.relay + wanted.store + wanted.filter + wanted.lightPush;
|
4698
|
-
const actual = {
|
4699
|
-
relay: 0,
|
4700
|
-
store: 0,
|
4701
|
-
filter: 0,
|
4702
|
-
lightPush: 0,
|
4703
|
-
};
|
4704
|
-
let totalSearches = 0;
|
4705
|
-
const peers = [];
|
4706
|
-
while (!isSatisfied(wanted, actual) &&
|
4707
|
-
totalSearches < maxSearches + errorTolerance) {
|
4708
|
-
const peer = await getNode();
|
4709
|
-
if (peer && isNewPeer(peer, peers)) {
|
4710
|
-
// ENRs without a waku2 key are ignored.
|
4711
|
-
if (peer.waku2) {
|
4712
|
-
if (helpsSatisfyCapabilities(peer.waku2, wanted, actual)) {
|
4713
|
-
addCapabilities(peer.waku2, actual);
|
4714
|
-
peers.push(peer);
|
4715
|
-
}
|
4716
|
-
}
|
4717
|
-
log$2(`got new peer candidate from DNS address=${peer.nodeId}@${peer.ip}`);
|
4718
|
-
}
|
4719
|
-
totalSearches++;
|
4720
|
-
}
|
4721
|
-
return peers;
|
4722
|
-
}
|
4723
|
-
/**
|
4724
|
-
* Fetch nodes using passed [[getNode]] until all wanted capabilities are
|
4725
|
-
* fulfilled or the number of [[getNode]] call exceeds the sum of
|
4726
|
-
* [[wantedNodeCapabilityCount]] plus [[errorTolerance]].
|
4727
|
-
*/
|
4728
|
-
async function* yieldNodesUntilCapabilitiesFulfilled(wantedNodeCapabilityCount, errorTolerance, getNode) {
|
4729
|
-
const wanted = {
|
4730
|
-
relay: wantedNodeCapabilityCount.relay ?? 0,
|
4731
|
-
store: wantedNodeCapabilityCount.store ?? 0,
|
4732
|
-
filter: wantedNodeCapabilityCount.filter ?? 0,
|
4733
|
-
lightPush: wantedNodeCapabilityCount.lightPush ?? 0,
|
4734
|
-
};
|
4735
|
-
const maxSearches = wanted.relay + wanted.store + wanted.filter + wanted.lightPush;
|
4736
|
-
const actual = {
|
4737
|
-
relay: 0,
|
4738
|
-
store: 0,
|
4739
|
-
filter: 0,
|
4740
|
-
lightPush: 0,
|
4741
|
-
};
|
4742
|
-
let totalSearches = 0;
|
4743
|
-
const peerNodeIds = new Set();
|
4744
|
-
while (!isSatisfied(wanted, actual) &&
|
4745
|
-
totalSearches < maxSearches + errorTolerance) {
|
4746
|
-
const peer = await getNode();
|
4747
|
-
if (peer && peer.nodeId && !peerNodeIds.has(peer.nodeId)) {
|
4748
|
-
peerNodeIds.add(peer.nodeId);
|
4749
|
-
// ENRs without a waku2 key are ignored.
|
4750
|
-
if (peer.waku2) {
|
4751
|
-
if (helpsSatisfyCapabilities(peer.waku2, wanted, actual)) {
|
4752
|
-
addCapabilities(peer.waku2, actual);
|
4753
|
-
yield peer;
|
4754
|
-
}
|
4755
|
-
}
|
4756
|
-
log$2(`got new peer candidate from DNS address=${peer.nodeId}@${peer.ip}`);
|
4757
|
-
}
|
4758
|
-
totalSearches++;
|
4759
|
-
}
|
4760
|
-
}
|
4761
|
-
function isSatisfied(wanted, actual) {
|
4762
|
-
return (actual.relay >= wanted.relay &&
|
4763
|
-
actual.store >= wanted.store &&
|
4764
|
-
actual.filter >= wanted.filter &&
|
4765
|
-
actual.lightPush >= wanted.lightPush);
|
4766
|
-
}
|
4767
|
-
function isNewPeer(peer, peers) {
|
4768
|
-
if (!peer.nodeId)
|
4769
|
-
return false;
|
4770
|
-
for (const existingPeer of peers) {
|
4771
|
-
if (peer.nodeId === existingPeer.nodeId) {
|
4772
|
-
return false;
|
4773
|
-
}
|
4774
|
-
}
|
4775
|
-
return true;
|
4776
|
-
}
|
4777
|
-
function addCapabilities(node, total) {
|
4778
|
-
if (node.relay)
|
4779
|
-
total.relay += 1;
|
4780
|
-
if (node.store)
|
4781
|
-
total.store += 1;
|
4782
|
-
if (node.filter)
|
4783
|
-
total.filter += 1;
|
4784
|
-
if (node.lightPush)
|
4785
|
-
total.lightPush += 1;
|
4786
|
-
}
|
4787
|
-
/**
|
4788
|
-
* Checks if the proposed ENR [[node]] helps satisfy the [[wanted]] capabilities,
|
4789
|
-
* considering the [[actual]] capabilities of nodes retrieved so far..
|
4790
|
-
*
|
4791
|
-
* @throws If the function is called when the wanted capabilities are already fulfilled.
|
4792
|
-
*/
|
4793
|
-
function helpsSatisfyCapabilities(node, wanted, actual) {
|
4794
|
-
if (isSatisfied(wanted, actual)) {
|
4795
|
-
throw "Internal Error: Waku2 wanted capabilities are already fulfilled";
|
4796
|
-
}
|
4797
|
-
const missing = missingCapabilities(wanted, actual);
|
4798
|
-
return ((missing.relay && node.relay) ||
|
4799
|
-
(missing.store && node.store) ||
|
4800
|
-
(missing.filter && node.filter) ||
|
4801
|
-
(missing.lightPush && node.lightPush));
|
4802
|
-
}
|
4803
|
-
/**
|
4804
|
-
* Return a [[Waku2]] Object for which capabilities are set to true if they are
|
4805
|
-
* [[wanted]] yet missing from [[actual]].
|
4806
|
-
*/
|
4807
|
-
function missingCapabilities(wanted, actual) {
|
4808
|
-
return {
|
4809
|
-
relay: actual.relay < wanted.relay,
|
4810
|
-
store: actual.store < wanted.store,
|
4811
|
-
filter: actual.filter < wanted.filter,
|
4812
|
-
lightPush: actual.lightPush < wanted.lightPush,
|
4813
|
-
};
|
4814
|
-
}
|
4815
|
-
|
4816
|
-
const log$1 = debug("waku:discovery:dns");
|
4817
|
-
class DnsNodeDiscovery {
|
4818
|
-
constructor(dns) {
|
4819
|
-
this._errorTolerance = 10;
|
4820
|
-
this._DNSTreeCache = {};
|
4821
|
-
this.dns = dns;
|
4822
|
-
}
|
4823
|
-
static dnsOverHttp(dnsClient) {
|
4824
|
-
if (!dnsClient) {
|
4825
|
-
dnsClient = new DnsOverHttps();
|
4826
|
-
}
|
4827
|
-
return new DnsNodeDiscovery(dnsClient);
|
4828
|
-
}
|
4829
|
-
/**
|
4830
|
-
* Returns a list of verified peers listed in an EIP-1459 DNS tree. Method may
|
4831
|
-
* return fewer peers than requested if [[wantedNodeCapabilityCount]] requires
|
4832
|
-
* larger quantity of peers than available or the number of errors/duplicate
|
4833
|
-
* peers encountered by randomized search exceeds the sum of the fields of
|
4834
|
-
* [[wantedNodeCapabilityCount]] plus the [[_errorTolerance]] factor.
|
4835
|
-
*/
|
4836
|
-
async getPeers(enrTreeUrls, wantedNodeCapabilityCount) {
|
4837
|
-
const networkIndex = Math.floor(Math.random() * enrTreeUrls.length);
|
4838
|
-
const { publicKey, domain } = ENRTree.parseTree(enrTreeUrls[networkIndex]);
|
4839
|
-
const context = {
|
4840
|
-
domain,
|
4841
|
-
publicKey,
|
4842
|
-
visits: {},
|
4843
|
-
};
|
4844
|
-
const peers = await fetchNodesUntilCapabilitiesFulfilled(wantedNodeCapabilityCount, this._errorTolerance, () => this._search(domain, context));
|
4845
|
-
log$1("retrieved peers: ", peers.map((peer) => {
|
4846
|
-
return {
|
4847
|
-
id: peer.peerId?.toString(),
|
4848
|
-
multiaddrs: peer.multiaddrs?.map((ma) => ma.toString()),
|
4849
|
-
};
|
4850
|
-
}));
|
4851
|
-
return peers;
|
4852
|
-
}
|
4853
|
-
/**
|
4854
|
-
* {@docInherit getPeers}
|
4855
|
-
*/
|
4856
|
-
async *getNextPeer(enrTreeUrls, wantedNodeCapabilityCount) {
|
4857
|
-
const networkIndex = Math.floor(Math.random() * enrTreeUrls.length);
|
4858
|
-
const { publicKey, domain } = ENRTree.parseTree(enrTreeUrls[networkIndex]);
|
4859
|
-
const context = {
|
4860
|
-
domain,
|
4861
|
-
publicKey,
|
4862
|
-
visits: {},
|
4863
|
-
};
|
4864
|
-
for await (const peer of yieldNodesUntilCapabilitiesFulfilled(wantedNodeCapabilityCount, this._errorTolerance, () => this._search(domain, context))) {
|
4865
|
-
yield peer;
|
4866
|
-
}
|
4867
|
-
}
|
4868
|
-
/**
|
4869
|
-
* Runs a recursive, randomized descent of the DNS tree to retrieve a single
|
4870
|
-
* ENR record as an ENR. Returns null if parsing or DNS resolution fails.
|
4871
|
-
*/
|
4872
|
-
async _search(subdomain, context) {
|
4873
|
-
try {
|
4874
|
-
const entry = await this._getTXTRecord(subdomain, context);
|
4875
|
-
context.visits[subdomain] = true;
|
4876
|
-
let next;
|
4877
|
-
let branches;
|
4878
|
-
const entryType = getEntryType(entry);
|
4879
|
-
try {
|
4880
|
-
switch (entryType) {
|
4881
|
-
case ENRTree.ROOT_PREFIX:
|
4882
|
-
next = ENRTree.parseAndVerifyRoot(entry, context.publicKey);
|
4883
|
-
return await this._search(next, context);
|
4884
|
-
case ENRTree.BRANCH_PREFIX:
|
4885
|
-
branches = ENRTree.parseBranch(entry);
|
4886
|
-
next = selectRandomPath(branches, context);
|
4887
|
-
return await this._search(next, context);
|
4888
|
-
case ENRTree.RECORD_PREFIX:
|
4889
|
-
return ENR.decodeTxt(entry);
|
4890
|
-
default:
|
4891
|
-
return null;
|
4892
|
-
}
|
4893
|
-
}
|
4894
|
-
catch (error) {
|
4895
|
-
log$1(`Failed to search DNS tree ${entryType} at subdomain ${subdomain}: ${error}`);
|
4896
|
-
return null;
|
4897
|
-
}
|
4898
|
-
}
|
4899
|
-
catch (error) {
|
4900
|
-
log$1(`Failed to retrieve TXT record at subdomain ${subdomain}: ${error}`);
|
4901
|
-
return null;
|
4902
|
-
}
|
4903
|
-
}
|
4904
|
-
/**
|
4905
|
-
* Retrieves the TXT record stored at a location from either
|
4906
|
-
* this DNS tree cache or via DNS query.
|
4907
|
-
*
|
4908
|
-
* @throws if the TXT Record contains non-UTF-8 values.
|
4909
|
-
*/
|
4910
|
-
async _getTXTRecord(subdomain, context) {
|
4911
|
-
if (this._DNSTreeCache[subdomain]) {
|
4912
|
-
return this._DNSTreeCache[subdomain];
|
4913
|
-
}
|
4914
|
-
// Location is either the top level tree entry host or a subdomain of it.
|
4915
|
-
const location = subdomain !== context.domain
|
4916
|
-
? `${subdomain}.${context.domain}`
|
4917
|
-
: context.domain;
|
4918
|
-
const response = await this.dns.resolveTXT(location);
|
4919
|
-
if (!response.length)
|
4920
|
-
throw new Error("Received empty result array while fetching TXT record");
|
4921
|
-
if (!response[0].length)
|
4922
|
-
throw new Error("Received empty TXT record");
|
4923
|
-
// Branch entries can be an array of strings of comma delimited subdomains, with
|
4924
|
-
// some subdomain strings split across the array elements
|
4925
|
-
const result = response.join("");
|
4926
|
-
this._DNSTreeCache[subdomain] = result;
|
4927
|
-
return result;
|
4928
|
-
}
|
4929
|
-
}
|
4930
|
-
function getEntryType(entry) {
|
4931
|
-
if (entry.startsWith(ENRTree.ROOT_PREFIX))
|
4932
|
-
return ENRTree.ROOT_PREFIX;
|
4933
|
-
if (entry.startsWith(ENRTree.BRANCH_PREFIX))
|
4934
|
-
return ENRTree.BRANCH_PREFIX;
|
4935
|
-
if (entry.startsWith(ENRTree.RECORD_PREFIX))
|
4936
|
-
return ENRTree.RECORD_PREFIX;
|
4937
|
-
return "";
|
4938
|
-
}
|
4939
|
-
/**
|
4940
|
-
* Returns a randomly selected subdomain string from the list provided by a branch
|
4941
|
-
* entry record.
|
4942
|
-
*
|
4943
|
-
* The client must track subdomains which are already resolved to avoid
|
4944
|
-
* going into an infinite loop b/c branch entries can contain
|
4945
|
-
* circular references. It’s in the client’s best interest to traverse the
|
4946
|
-
* tree in random order.
|
4947
|
-
*/
|
4948
|
-
function selectRandomPath(branches, context) {
|
4949
|
-
// Identify domains already visited in this traversal of the DNS tree.
|
4950
|
-
// Then filter against them to prevent cycles.
|
4951
|
-
const circularRefs = {};
|
4952
|
-
for (const [idx, subdomain] of branches.entries()) {
|
4953
|
-
if (context.visits[subdomain]) {
|
4954
|
-
circularRefs[idx] = true;
|
4955
|
-
}
|
4956
|
-
}
|
4957
|
-
// If all possible paths are circular...
|
4958
|
-
if (Object.keys(circularRefs).length === branches.length) {
|
4959
|
-
throw new Error("Unresolvable circular path detected");
|
4960
|
-
}
|
4961
|
-
// Randomly select a viable path
|
4962
|
-
let index;
|
4963
|
-
do {
|
4964
|
-
index = Math.floor(Math.random() * branches.length);
|
4965
|
-
} while (circularRefs[index]);
|
4966
|
-
return branches[index];
|
4967
|
-
}
|
4968
|
-
|
4969
|
-
const log = debug("waku:peer-discovery-dns");
|
4970
|
-
/**
|
4971
|
-
* Parse options and expose function to return bootstrap peer addresses.
|
4972
|
-
*
|
4973
|
-
* @throws if an invalid combination of options is passed, see [[BootstrapOptions]] for details.
|
4974
|
-
*/
|
4975
|
-
class PeerDiscoveryDns extends EventEmitter {
|
4976
|
-
/**
|
4977
|
-
* @param enrUrl An EIP-1459 ENR Tree URL. For example:
|
4978
|
-
* "enrtree://AOFTICU2XWDULNLZGRMQS4RIZPAZEHYMV4FYHAPW563HNRAOERP7C@test.nodes.vac.dev"
|
4979
|
-
* @param wantedNodeCapabilityCount Specifies what node capabilities
|
4980
|
-
* (protocol) must be returned.
|
4981
|
-
*/
|
4982
|
-
constructor(enrUrl, wantedNodeCapabilityCount) {
|
4983
|
-
super();
|
4984
|
-
this._started = false;
|
4985
|
-
log("Use following EIP-1459 ENR Tree URL: ", enrUrl);
|
4986
|
-
const dns = DnsNodeDiscovery.dnsOverHttp();
|
4987
|
-
this.nextPeer = dns.getNextPeer.bind({}, [enrUrl], wantedNodeCapabilityCount);
|
4988
|
-
}
|
4989
|
-
/**
|
4990
|
-
* Start discovery process
|
4991
|
-
*/
|
4992
|
-
async start() {
|
4993
|
-
log("Starting peer discovery via dns");
|
4994
|
-
this._started = true;
|
4995
|
-
for await (const peer of this.nextPeer()) {
|
4996
|
-
if (!this._started)
|
4997
|
-
return;
|
4998
|
-
const peerInfos = multiaddrsToPeerInfo(peer.getFullMultiaddrs());
|
4999
|
-
peerInfos.forEach((peerInfo) => {
|
5000
|
-
this.dispatchEvent(new CustomEvent("peer", { detail: peerInfo }));
|
5001
|
-
});
|
5002
|
-
}
|
5003
|
-
}
|
5004
|
-
/**
|
5005
|
-
* Stop emitting events
|
5006
|
-
*/
|
5007
|
-
stop() {
|
5008
|
-
this._started = false;
|
5009
|
-
}
|
5010
|
-
get [symbol]() {
|
5011
|
-
return true;
|
5012
|
-
}
|
5013
|
-
get [Symbol.toStringTag]() {
|
5014
|
-
return "@waku/bootstrap";
|
5015
|
-
}
|
5016
|
-
}
|
5017
|
-
|
5018
|
-
export { PeerDiscoveryDns };
|