@waku/core 0.0.1

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.
Files changed (235) hide show
  1. package/CHANGELOG.md +614 -0
  2. package/README.md +56 -0
  3. package/bundle/browser-1e1a2f27.js +722 -0
  4. package/bundle/crypto-8551d579.js +2585 -0
  5. package/bundle/crypto-b00764b7.js +1772 -0
  6. package/bundle/enr-564d4a51.js +20785 -0
  7. package/bundle/enr-9fc5eed8.js +20786 -0
  8. package/bundle/enr-f6e82a53.js +20785 -0
  9. package/bundle/events-158407bb.js +1929 -0
  10. package/bundle/events-fcbda4dc.js +76 -0
  11. package/bundle/index-02d21809.js +20 -0
  12. package/bundle/index-0a4bdddc.js +2976 -0
  13. package/bundle/index-2ae915be.js +1854 -0
  14. package/bundle/index-64ce43f0.js +69 -0
  15. package/bundle/index-691c0be6.js +4059 -0
  16. package/bundle/index-a013a259.js +20 -0
  17. package/bundle/index-ba42b4fc.js +862 -0
  18. package/bundle/index.js +13428 -0
  19. package/bundle/lib/enr.js +8 -0
  20. package/bundle/lib/peer_discovery_dns.js +5018 -0
  21. package/bundle/lib/peer_discovery_static_list.js +75 -0
  22. package/bundle/lib/predefined_bootstrap_nodes.js +59 -0
  23. package/bundle/lib/utils.js +1 -0
  24. package/bundle/lib/wait_for_remote_peer.js +327 -0
  25. package/bundle/lib/waku_message/topic_only_message.js +4 -0
  26. package/bundle/lib/waku_message/version_0.js +4 -0
  27. package/bundle/lib/waku_message/version_1.js +463 -0
  28. package/bundle/message-e2db79d7.js +8393 -0
  29. package/bundle/multiaddr_to_peer_info-c406b1e1.js +19 -0
  30. package/bundle/multiaddr_to_peer_info-fd1de516.js +19 -0
  31. package/bundle/random_subset-75d1c511.js +26 -0
  32. package/bundle/topic_only_message-34f36fa6.js +82 -0
  33. package/bundle/utils-9a3221f2.js +815 -0
  34. package/bundle/version_0-e6fe440c.js +317 -0
  35. package/dist/index.d.ts +16 -0
  36. package/dist/index.js +17 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/lib/constants.d.ts +4 -0
  39. package/dist/lib/constants.js +5 -0
  40. package/dist/lib/constants.js.map +1 -0
  41. package/dist/lib/crypto.d.ts +34 -0
  42. package/dist/lib/crypto.js +79 -0
  43. package/dist/lib/crypto.js.map +1 -0
  44. package/dist/lib/enr/constants.d.ts +4 -0
  45. package/dist/lib/enr/constants.js +8 -0
  46. package/dist/lib/enr/constants.js.map +1 -0
  47. package/dist/lib/enr/enr.d.ts +90 -0
  48. package/dist/lib/enr/enr.js +432 -0
  49. package/dist/lib/enr/enr.js.map +1 -0
  50. package/dist/lib/enr/index.d.ts +5 -0
  51. package/dist/lib/enr/index.js +6 -0
  52. package/dist/lib/enr/index.js.map +1 -0
  53. package/dist/lib/enr/keypair/index.d.ts +8 -0
  54. package/dist/lib/enr/keypair/index.js +53 -0
  55. package/dist/lib/enr/keypair/index.js.map +1 -0
  56. package/dist/lib/enr/keypair/secp256k1.d.ts +13 -0
  57. package/dist/lib/enr/keypair/secp256k1.js +57 -0
  58. package/dist/lib/enr/keypair/secp256k1.js.map +1 -0
  59. package/dist/lib/enr/keypair/types.d.ts +13 -0
  60. package/dist/lib/enr/keypair/types.js +7 -0
  61. package/dist/lib/enr/keypair/types.js.map +1 -0
  62. package/dist/lib/enr/multiaddr_from_fields.d.ts +2 -0
  63. package/dist/lib/enr/multiaddr_from_fields.js +8 -0
  64. package/dist/lib/enr/multiaddr_from_fields.js.map +1 -0
  65. package/dist/lib/enr/multiaddrs_codec.d.ts +3 -0
  66. package/dist/lib/enr/multiaddrs_codec.js +32 -0
  67. package/dist/lib/enr/multiaddrs_codec.js.map +1 -0
  68. package/dist/lib/enr/types.d.ts +8 -0
  69. package/dist/lib/enr/types.js +3 -0
  70. package/dist/lib/enr/types.js.map +1 -0
  71. package/dist/lib/enr/v4.d.ts +3 -0
  72. package/dist/lib/enr/v4.js +14 -0
  73. package/dist/lib/enr/v4.js.map +1 -0
  74. package/dist/lib/enr/waku2_codec.d.ts +8 -0
  75. package/dist/lib/enr/waku2_codec.js +36 -0
  76. package/dist/lib/enr/waku2_codec.js.map +1 -0
  77. package/dist/lib/group_by.d.ts +3 -0
  78. package/dist/lib/group_by.js +13 -0
  79. package/dist/lib/group_by.js.map +1 -0
  80. package/dist/lib/multiaddr_to_peer_info.d.ts +3 -0
  81. package/dist/lib/multiaddr_to_peer_info.js +15 -0
  82. package/dist/lib/multiaddr_to_peer_info.js.map +1 -0
  83. package/dist/lib/peer_discovery_dns/dns.d.ts +48 -0
  84. package/dist/lib/peer_discovery_dns/dns.js +158 -0
  85. package/dist/lib/peer_discovery_dns/dns.js.map +1 -0
  86. package/dist/lib/peer_discovery_dns/dns_over_https.d.ts +32 -0
  87. package/dist/lib/peer_discovery_dns/dns_over_https.js +87 -0
  88. package/dist/lib/peer_discovery_dns/dns_over_https.js.map +1 -0
  89. package/dist/lib/peer_discovery_dns/enrtree.d.ts +33 -0
  90. package/dist/lib/peer_discovery_dns/enrtree.js +76 -0
  91. package/dist/lib/peer_discovery_dns/enrtree.js.map +1 -0
  92. package/dist/lib/peer_discovery_dns/fetch_nodes.d.ts +14 -0
  93. package/dist/lib/peer_discovery_dns/fetch_nodes.js +133 -0
  94. package/dist/lib/peer_discovery_dns/fetch_nodes.js.map +1 -0
  95. package/dist/lib/peer_discovery_dns/index.d.ts +30 -0
  96. package/dist/lib/peer_discovery_dns/index.js +54 -0
  97. package/dist/lib/peer_discovery_dns/index.js.map +1 -0
  98. package/dist/lib/peer_discovery_static_list.d.ts +44 -0
  99. package/dist/lib/peer_discovery_static_list.js +72 -0
  100. package/dist/lib/peer_discovery_static_list.js.map +1 -0
  101. package/dist/lib/predefined_bootstrap_nodes.d.ts +35 -0
  102. package/dist/lib/predefined_bootstrap_nodes.js +56 -0
  103. package/dist/lib/predefined_bootstrap_nodes.js.map +1 -0
  104. package/dist/lib/push_or_init_map.d.ts +1 -0
  105. package/dist/lib/push_or_init_map.js +9 -0
  106. package/dist/lib/push_or_init_map.js.map +1 -0
  107. package/dist/lib/random_subset.d.ts +4 -0
  108. package/dist/lib/random_subset.js +25 -0
  109. package/dist/lib/random_subset.js.map +1 -0
  110. package/dist/lib/select_connection.d.ts +2 -0
  111. package/dist/lib/select_connection.js +19 -0
  112. package/dist/lib/select_connection.js.map +1 -0
  113. package/dist/lib/select_peer.d.ts +15 -0
  114. package/dist/lib/select_peer.js +59 -0
  115. package/dist/lib/select_peer.js.map +1 -0
  116. package/dist/lib/to_proto_message.d.ts +3 -0
  117. package/dist/lib/to_proto_message.js +11 -0
  118. package/dist/lib/to_proto_message.js.map +1 -0
  119. package/dist/lib/utils.d.ts +22 -0
  120. package/dist/lib/utils.js +40 -0
  121. package/dist/lib/utils.js.map +1 -0
  122. package/dist/lib/wait_for_remote_peer.d.ts +22 -0
  123. package/dist/lib/wait_for_remote_peer.js +113 -0
  124. package/dist/lib/wait_for_remote_peer.js.map +1 -0
  125. package/dist/lib/waku.d.ts +61 -0
  126. package/dist/lib/waku.js +174 -0
  127. package/dist/lib/waku.js.map +1 -0
  128. package/dist/lib/waku_filter/filter_rpc.d.ts +25 -0
  129. package/dist/lib/waku_filter/filter_rpc.js +44 -0
  130. package/dist/lib/waku_filter/filter_rpc.js.map +1 -0
  131. package/dist/lib/waku_filter/index.d.ts +50 -0
  132. package/dist/lib/waku_filter/index.js +181 -0
  133. package/dist/lib/waku_filter/index.js.map +1 -0
  134. package/dist/lib/waku_light_push/index.d.ts +38 -0
  135. package/dist/lib/waku_light_push/index.js +83 -0
  136. package/dist/lib/waku_light_push/index.js.map +1 -0
  137. package/dist/lib/waku_light_push/push_rpc.d.ts +11 -0
  138. package/dist/lib/waku_light_push/push_rpc.js +31 -0
  139. package/dist/lib/waku_light_push/push_rpc.js.map +1 -0
  140. package/dist/lib/waku_message/constants.d.ts +12 -0
  141. package/dist/lib/waku_message/constants.js +10 -0
  142. package/dist/lib/waku_message/constants.js.map +1 -0
  143. package/dist/lib/waku_message/ecies.d.ts +17 -0
  144. package/dist/lib/waku_message/ecies.js +126 -0
  145. package/dist/lib/waku_message/ecies.js.map +1 -0
  146. package/dist/lib/waku_message/symmetric.d.ts +3 -0
  147. package/dist/lib/waku_message/symmetric.js +18 -0
  148. package/dist/lib/waku_message/symmetric.js.map +1 -0
  149. package/dist/lib/waku_message/topic_only_message.d.ts +15 -0
  150. package/dist/lib/waku_message/topic_only_message.js +31 -0
  151. package/dist/lib/waku_message/topic_only_message.js.map +1 -0
  152. package/dist/lib/waku_message/version_0.d.ts +26 -0
  153. package/dist/lib/waku_message/version_0.js +96 -0
  154. package/dist/lib/waku_message/version_0.js.map +1 -0
  155. package/dist/lib/waku_message/version_1.d.ts +93 -0
  156. package/dist/lib/waku_message/version_1.js +325 -0
  157. package/dist/lib/waku_message/version_1.js.map +1 -0
  158. package/dist/lib/waku_relay/constants.d.ts +63 -0
  159. package/dist/lib/waku_relay/constants.js +67 -0
  160. package/dist/lib/waku_relay/constants.js.map +1 -0
  161. package/dist/lib/waku_relay/index.d.ts +65 -0
  162. package/dist/lib/waku_relay/index.js +111 -0
  163. package/dist/lib/waku_relay/index.js.map +1 -0
  164. package/dist/lib/waku_store/history_rpc.d.ts +27 -0
  165. package/dist/lib/waku_store/history_rpc.js +71 -0
  166. package/dist/lib/waku_store/history_rpc.js.map +1 -0
  167. package/dist/lib/waku_store/index.d.ts +126 -0
  168. package/dist/lib/waku_store/index.js +218 -0
  169. package/dist/lib/waku_store/index.js.map +1 -0
  170. package/dist/proto/filter.d.ts +65 -0
  171. package/dist/proto/filter.js +425 -0
  172. package/dist/proto/filter.js.map +1 -0
  173. package/dist/proto/light_push.d.ts +57 -0
  174. package/dist/proto/light_push.js +369 -0
  175. package/dist/proto/light_push.js.map +1 -0
  176. package/dist/proto/message.d.ts +29 -0
  177. package/dist/proto/message.js +215 -0
  178. package/dist/proto/message.js.map +1 -0
  179. package/dist/proto/store.d.ts +104 -0
  180. package/dist/proto/store.js +602 -0
  181. package/dist/proto/store.js.map +1 -0
  182. package/dist/proto/topic_only_message.d.ts +10 -0
  183. package/dist/proto/topic_only_message.js +46 -0
  184. package/dist/proto/topic_only_message.js.map +1 -0
  185. package/package.json +292 -0
  186. package/src/index.ts +33 -0
  187. package/src/lib/constants.ts +4 -0
  188. package/src/lib/crypto.ts +100 -0
  189. package/src/lib/enr/constants.ts +10 -0
  190. package/src/lib/enr/enr.ts +516 -0
  191. package/src/lib/enr/index.ts +5 -0
  192. package/src/lib/enr/keypair/index.ts +76 -0
  193. package/src/lib/enr/keypair/secp256k1.ts +69 -0
  194. package/src/lib/enr/keypair/types.ts +14 -0
  195. package/src/lib/enr/multiaddr_from_fields.ts +18 -0
  196. package/src/lib/enr/multiaddrs_codec.ts +50 -0
  197. package/src/lib/enr/types.ts +11 -0
  198. package/src/lib/enr/v4.ts +22 -0
  199. package/src/lib/enr/waku2_codec.ts +39 -0
  200. package/src/lib/group_by.ts +14 -0
  201. package/src/lib/multiaddr_to_peer_info.ts +17 -0
  202. package/src/lib/peer_discovery_dns/dns.ts +223 -0
  203. package/src/lib/peer_discovery_dns/dns_over_https.ts +98 -0
  204. package/src/lib/peer_discovery_dns/enrtree.ts +123 -0
  205. package/src/lib/peer_discovery_dns/fetch_nodes.ts +180 -0
  206. package/src/lib/peer_discovery_dns/index.ts +84 -0
  207. package/src/lib/peer_discovery_static_list.ts +118 -0
  208. package/src/lib/predefined_bootstrap_nodes.ts +72 -0
  209. package/src/lib/push_or_init_map.ts +13 -0
  210. package/src/lib/random_subset.ts +30 -0
  211. package/src/lib/select_connection.ts +24 -0
  212. package/src/lib/select_peer.ts +77 -0
  213. package/src/lib/to_proto_message.ts +15 -0
  214. package/src/lib/utils.ts +50 -0
  215. package/src/lib/wait_for_remote_peer.ts +151 -0
  216. package/src/lib/waku.ts +258 -0
  217. package/src/lib/waku_filter/filter_rpc.ts +57 -0
  218. package/src/lib/waku_filter/index.ts +291 -0
  219. package/src/lib/waku_light_push/index.ts +137 -0
  220. package/src/lib/waku_light_push/push_rpc.ts +39 -0
  221. package/src/lib/waku_message/constants.ts +10 -0
  222. package/src/lib/waku_message/ecies.ts +194 -0
  223. package/src/lib/waku_message/symmetric.ts +33 -0
  224. package/src/lib/waku_message/topic_only_message.ts +40 -0
  225. package/src/lib/waku_message/version_0.ts +121 -0
  226. package/src/lib/waku_message/version_1.ts +457 -0
  227. package/src/lib/waku_relay/constants.ts +77 -0
  228. package/src/lib/waku_relay/index.ts +189 -0
  229. package/src/lib/waku_store/history_rpc.ts +94 -0
  230. package/src/lib/waku_store/index.ts +372 -0
  231. package/src/proto/filter.ts +602 -0
  232. package/src/proto/light_push.ts +526 -0
  233. package/src/proto/message.ts +304 -0
  234. package/src/proto/store.ts +844 -0
  235. package/src/proto/topic_only_message.ts +67 -0
@@ -0,0 +1,5018 @@
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 };