/src/node/src/cares_wrap.cc
Line | Count | Source |
1 | | // Copyright Joyent, Inc. and other Node contributors. |
2 | | // |
3 | | // Permission is hereby granted, free of charge, to any person obtaining a |
4 | | // copy of this software and associated documentation files (the |
5 | | // "Software"), to deal in the Software without restriction, including |
6 | | // without limitation the rights to use, copy, modify, merge, publish, |
7 | | // distribute, sublicense, and/or sell copies of the Software, and to permit |
8 | | // persons to whom the Software is furnished to do so, subject to the |
9 | | // following conditions: |
10 | | // |
11 | | // The above copyright notice and this permission notice shall be included |
12 | | // in all copies or substantial portions of the Software. |
13 | | // |
14 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
15 | | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
16 | | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
17 | | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
18 | | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
19 | | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
20 | | // USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 | | |
22 | | #include "cares_wrap.h" |
23 | | #include "ada.h" |
24 | | #include "async_wrap-inl.h" |
25 | | #include "base_object-inl.h" |
26 | | #include "env-inl.h" |
27 | | #include "memory_tracker-inl.h" |
28 | | #include "nbytes.h" |
29 | | #include "node.h" |
30 | | #include "node_errors.h" |
31 | | #include "node_external_reference.h" |
32 | | #include "req_wrap-inl.h" |
33 | | #include "util-inl.h" |
34 | | #include "uv.h" |
35 | | #include "v8.h" |
36 | | |
37 | | #include <cerrno> |
38 | | #include <cstring> |
39 | | #include <memory> |
40 | | #include <vector> |
41 | | #include <unordered_set> |
42 | | |
43 | | #ifndef T_TLSA |
44 | | #define T_TLSA 52 /* TLSA certificate association */ |
45 | | #endif |
46 | | |
47 | | #ifndef T_CAA |
48 | | # define T_CAA 257 /* Certification Authority Authorization */ |
49 | | #endif |
50 | | |
51 | | // OpenBSD does not define these |
52 | | #ifndef AI_ALL |
53 | | # define AI_ALL 0 |
54 | | #endif |
55 | | #ifndef AI_V4MAPPED |
56 | | # define AI_V4MAPPED 0 |
57 | | #endif |
58 | | |
59 | | |
60 | | namespace node { |
61 | | namespace cares_wrap { |
62 | | |
63 | | using v8::Array; |
64 | | using v8::ArrayBuffer; |
65 | | using v8::Context; |
66 | | using v8::DictionaryTemplate; |
67 | | using v8::EscapableHandleScope; |
68 | | using v8::Exception; |
69 | | using v8::FunctionCallbackInfo; |
70 | | using v8::FunctionTemplate; |
71 | | using v8::HandleScope; |
72 | | using v8::Int32; |
73 | | using v8::Integer; |
74 | | using v8::Isolate; |
75 | | using v8::Just; |
76 | | using v8::JustVoid; |
77 | | using v8::Local; |
78 | | using v8::LocalVector; |
79 | | using v8::Maybe; |
80 | | using v8::MaybeLocal; |
81 | | using v8::Nothing; |
82 | | using v8::Null; |
83 | | using v8::Object; |
84 | | using v8::String; |
85 | | using v8::Uint32; |
86 | | using v8::Undefined; |
87 | | using v8::Value; |
88 | | |
89 | | namespace { |
90 | | |
91 | | Mutex ares_library_mutex; |
92 | | |
93 | 0 | inline uint16_t cares_get_16bit(const unsigned char* p) { |
94 | 0 | return static_cast<uint32_t>(p[0] << 8U) | (static_cast<uint32_t>(p[1])); |
95 | 0 | } |
96 | | |
97 | 0 | void ares_poll_cb(uv_poll_t* watcher, int status, int events) { |
98 | 0 | NodeAresTask* task = ContainerOf(&NodeAresTask::poll_watcher, watcher); |
99 | 0 | ChannelWrap* channel = task->channel; |
100 | | |
101 | | /* Reset the idle timer */ |
102 | 0 | uv_timer_again(channel->timer_handle()); |
103 | |
|
104 | 0 | if (status < 0) { |
105 | | /* An error happened. Just pretend that the socket is both readable and */ |
106 | | /* writable. */ |
107 | 0 | ares_process_fd(channel->cares_channel(), task->sock, task->sock); |
108 | 0 | return; |
109 | 0 | } |
110 | | |
111 | | /* Process DNS responses */ |
112 | 0 | ares_process_fd(channel->cares_channel(), |
113 | 0 | events & UV_READABLE ? task->sock : ARES_SOCKET_BAD, |
114 | 0 | events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD); |
115 | 0 | } |
116 | | |
117 | | |
118 | 0 | void ares_poll_close_cb(uv_poll_t* watcher) { |
119 | 0 | std::unique_ptr<NodeAresTask> free_me( |
120 | 0 | ContainerOf(&NodeAresTask::poll_watcher, watcher)); |
121 | 0 | } |
122 | | |
123 | | |
124 | | /* Callback from ares when socket operation is started */ |
125 | 0 | void ares_sockstate_cb(void* data, ares_socket_t sock, int read, int write) { |
126 | 0 | ChannelWrap* channel = static_cast<ChannelWrap*>(data); |
127 | 0 | NodeAresTask* task; |
128 | |
|
129 | 0 | NodeAresTask lookup_task; |
130 | 0 | lookup_task.sock = sock; |
131 | 0 | auto it = channel->task_list()->find(&lookup_task); |
132 | |
|
133 | 0 | task = (it == channel->task_list()->end()) ? nullptr : *it; |
134 | |
|
135 | 0 | if (read || write) { |
136 | 0 | if (!task) { |
137 | | /* New socket */ |
138 | 0 | channel->StartTimer(); |
139 | |
|
140 | 0 | task = NodeAresTask::Create(channel, sock); |
141 | 0 | if (task == nullptr) { |
142 | | /* This should never happen unless we're out of memory or something */ |
143 | | /* is seriously wrong. The socket won't be polled, but the query will */ |
144 | | /* eventually time out. */ |
145 | 0 | return; |
146 | 0 | } |
147 | | |
148 | 0 | channel->task_list()->insert(task); |
149 | 0 | } |
150 | | |
151 | | /* This should never fail. If it fails anyway, the query will eventually */ |
152 | | /* time out. */ |
153 | 0 | uv_poll_start(&task->poll_watcher, |
154 | 0 | (read ? UV_READABLE : 0) | (write ? UV_WRITABLE : 0), |
155 | 0 | ares_poll_cb); |
156 | |
|
157 | 0 | } else { |
158 | 0 | if (task != nullptr) { |
159 | 0 | channel->task_list()->erase(it); |
160 | 0 | channel->env()->CloseHandle(&task->poll_watcher, ares_poll_close_cb); |
161 | 0 | } |
162 | |
|
163 | 0 | if (channel->task_list()->empty()) { |
164 | 0 | channel->CloseTimer(); |
165 | 0 | } |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | 0 | MaybeLocal<Array> HostentToNames(Environment* env, struct hostent* host) { |
170 | 0 | EscapableHandleScope scope(env->isolate()); |
171 | |
|
172 | 0 | LocalVector<Value> names(env->isolate()); |
173 | |
|
174 | 0 | for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) { |
175 | 0 | names.emplace_back(OneByteString(env->isolate(), host->h_aliases[i])); |
176 | 0 | } |
177 | |
|
178 | 0 | return scope.Escape(Array::New(env->isolate(), names.data(), names.size())); |
179 | 0 | } |
180 | | |
181 | | MaybeLocal<Array> HostentToNames(Environment* env, |
182 | | struct hostent* host, |
183 | 0 | Local<Array> names) { |
184 | 0 | size_t offset = names->Length(); |
185 | |
|
186 | 0 | for (uint32_t i = 0; host->h_aliases[i] != nullptr; ++i) { |
187 | 0 | if (names |
188 | 0 | ->Set(env->context(), |
189 | 0 | i + offset, |
190 | 0 | OneByteString(env->isolate(), host->h_aliases[i])) |
191 | 0 | .IsNothing()) { |
192 | 0 | return {}; |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | 0 | return names; |
197 | 0 | } |
198 | | |
199 | | template <typename T> |
200 | | Local<Array> AddrTTLToArray( |
201 | | Environment* env, |
202 | | const T* addrttls, |
203 | 0 | size_t naddrttls) { |
204 | 0 | MaybeStackBuffer<Local<Value>, 8> ttls(naddrttls); |
205 | 0 | for (size_t i = 0; i < naddrttls; i++) { |
206 | 0 | ttls[i] = Integer::NewFromUnsigned(env->isolate(), addrttls[i].ttl); |
207 | 0 | } |
208 | |
|
209 | 0 | return Array::New(env->isolate(), ttls.out(), naddrttls); |
210 | 0 | } Unexecuted instantiation: cares_wrap.cc:v8::Local<v8::Array> node::cares_wrap::(anonymous namespace)::AddrTTLToArray<ares_addrttl>(node::Environment*, ares_addrttl const*, unsigned long) Unexecuted instantiation: cares_wrap.cc:v8::Local<v8::Array> node::cares_wrap::(anonymous namespace)::AddrTTLToArray<ares_addr6ttl>(node::Environment*, ares_addr6ttl const*, unsigned long) |
211 | | |
212 | | Maybe<int> ParseGeneralReply(Environment* env, |
213 | | const unsigned char* buf, |
214 | | int len, |
215 | | int* type, |
216 | | Local<Array> ret, |
217 | | void* addrttls = nullptr, |
218 | 0 | int* naddrttls = nullptr) { |
219 | 0 | HandleScope handle_scope(env->isolate()); |
220 | 0 | hostent* host; |
221 | |
|
222 | 0 | int status; |
223 | 0 | switch (*type) { |
224 | 0 | case ns_t_a: |
225 | 0 | case ns_t_cname: |
226 | 0 | case ns_t_cname_or_a: |
227 | 0 | status = ares_parse_a_reply(buf, |
228 | 0 | len, |
229 | 0 | &host, |
230 | 0 | static_cast<ares_addrttl*>(addrttls), |
231 | 0 | naddrttls); |
232 | 0 | break; |
233 | 0 | case ns_t_aaaa: |
234 | 0 | status = ares_parse_aaaa_reply(buf, |
235 | 0 | len, |
236 | 0 | &host, |
237 | 0 | static_cast<ares_addr6ttl*>(addrttls), |
238 | 0 | naddrttls); |
239 | 0 | break; |
240 | 0 | case ns_t_ns: |
241 | 0 | status = ares_parse_ns_reply(buf, len, &host); |
242 | 0 | break; |
243 | 0 | case ns_t_ptr: |
244 | 0 | status = ares_parse_ptr_reply(buf, len, nullptr, 0, AF_INET, &host); |
245 | 0 | break; |
246 | 0 | default: |
247 | 0 | UNREACHABLE("Bad NS type"); |
248 | 0 | } |
249 | | |
250 | 0 | if (status != ARES_SUCCESS) return Just<int>(status); |
251 | | |
252 | 0 | CHECK_NOT_NULL(host); |
253 | 0 | HostEntPointer ptr(host); |
254 | | |
255 | | /* If it's `CNAME`, return the CNAME value; |
256 | | * And if it's `CNAME_OR_A` and it has value in `h_name` and `h_aliases[0]`, |
257 | | * we consider it's a CNAME record, otherwise we consider it's an A record. */ |
258 | 0 | if ((*type == ns_t_cname_or_a && ptr->h_name && ptr->h_aliases[0]) || |
259 | 0 | *type == ns_t_cname) { |
260 | | // A cname lookup always returns a single record but we follow the |
261 | | // common API here. |
262 | 0 | *type = ns_t_cname; |
263 | 0 | if (ret->Set(env->context(), |
264 | 0 | ret->Length(), |
265 | 0 | OneByteString(env->isolate(), ptr->h_name)) |
266 | 0 | .IsNothing()) { |
267 | 0 | return Nothing<int>(); |
268 | 0 | } |
269 | 0 | return Just<int>(ARES_SUCCESS); |
270 | 0 | } |
271 | | |
272 | 0 | if (*type == ns_t_cname_or_a) |
273 | 0 | *type = ns_t_a; |
274 | |
|
275 | 0 | if (*type == ns_t_ns) { |
276 | 0 | if (HostentToNames(env, ptr.get(), ret).IsEmpty()) { |
277 | 0 | return Nothing<int>(); |
278 | 0 | } |
279 | 0 | } else if (*type == ns_t_ptr) { |
280 | 0 | uint32_t offset = ret->Length(); |
281 | 0 | for (uint32_t i = 0; ptr->h_aliases[i] != nullptr; i++) { |
282 | 0 | auto alias = OneByteString(env->isolate(), ptr->h_aliases[i]); |
283 | 0 | if (ret->Set(env->context(), i + offset, alias).IsNothing()) { |
284 | 0 | return Nothing<int>(); |
285 | 0 | } |
286 | 0 | } |
287 | 0 | } else { |
288 | 0 | uint32_t offset = ret->Length(); |
289 | 0 | char ip[INET6_ADDRSTRLEN]; |
290 | 0 | for (uint32_t i = 0; ptr->h_addr_list[i] != nullptr; ++i) { |
291 | 0 | uv_inet_ntop(ptr->h_addrtype, ptr->h_addr_list[i], ip, sizeof(ip)); |
292 | 0 | auto address = OneByteString(env->isolate(), ip); |
293 | 0 | if (ret->Set(env->context(), i + offset, address).IsNothing()) { |
294 | 0 | return Nothing<int>(); |
295 | 0 | } |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | 0 | return Just<int>(ARES_SUCCESS); |
300 | 0 | } |
301 | | Maybe<int> ParseMxReply(Environment* env, |
302 | | const unsigned char* buf, |
303 | | int len, |
304 | | Local<Array> ret, |
305 | 0 | bool need_type = false) { |
306 | 0 | HandleScope handle_scope(env->isolate()); |
307 | |
|
308 | 0 | auto tmpl = env->mx_record_template(); |
309 | 0 | if (tmpl.IsEmpty()) { |
310 | 0 | static constexpr std::string_view names[] = { |
311 | 0 | "exchange", |
312 | 0 | "priority", |
313 | 0 | "type", |
314 | 0 | }; |
315 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
316 | 0 | env->set_mx_record_template(tmpl); |
317 | 0 | } |
318 | |
|
319 | 0 | struct ares_mx_reply* mx_start; |
320 | 0 | int status = ares_parse_mx_reply(buf, len, &mx_start); |
321 | 0 | if (status != ARES_SUCCESS) return Just<int>(status); |
322 | | |
323 | 0 | DeleteFnPtr<void, ares_free_data> free_me(mx_start); |
324 | |
|
325 | 0 | uint32_t offset = ret->Length(); |
326 | 0 | ares_mx_reply* current = mx_start; |
327 | |
|
328 | 0 | MaybeLocal<Value> values[] = { |
329 | 0 | Undefined(env->isolate()), // exchange |
330 | 0 | Undefined(env->isolate()), // priority |
331 | 0 | Undefined(env->isolate()), // type |
332 | 0 | }; |
333 | |
|
334 | 0 | for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { |
335 | 0 | values[0] = OneByteString(env->isolate(), current->host); |
336 | 0 | values[1] = Integer::New(env->isolate(), current->priority); |
337 | 0 | values[2] = env->dns_mx_string(); |
338 | 0 | Local<Value> record; |
339 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&record) || |
340 | 0 | ret->Set(env->context(), i + offset, record).IsNothing()) { |
341 | 0 | return Nothing<int>(); |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | 0 | return Just<int>(ARES_SUCCESS); |
346 | 0 | } |
347 | | |
348 | | Maybe<int> ParseCaaReply(Environment* env, |
349 | | const unsigned char* buf, |
350 | | int len, |
351 | | Local<Array> ret, |
352 | 0 | bool need_type = false) { |
353 | 0 | HandleScope handle_scope(env->isolate()); |
354 | |
|
355 | 0 | auto tmpl = env->caa_record_template(); |
356 | 0 | if (tmpl.IsEmpty()) { |
357 | 0 | static constexpr std::string_view names[] = { |
358 | 0 | "critical", |
359 | 0 | "type", |
360 | 0 | }; |
361 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
362 | 0 | env->set_caa_record_template(tmpl); |
363 | 0 | } |
364 | |
|
365 | 0 | struct ares_caa_reply* caa_start; |
366 | 0 | int status = ares_parse_caa_reply(buf, len, &caa_start); |
367 | 0 | if (status != ARES_SUCCESS) return Just<int>(status); |
368 | 0 | DeleteFnPtr<void, ares_free_data> free_me(caa_start); |
369 | |
|
370 | 0 | MaybeLocal<Value> values[] = { |
371 | 0 | Undefined(env->isolate()), // critical |
372 | 0 | Undefined(env->isolate()), // type |
373 | 0 | }; |
374 | |
|
375 | 0 | uint32_t offset = ret->Length(); |
376 | 0 | ares_caa_reply* current = caa_start; |
377 | 0 | for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { |
378 | 0 | values[0] = Integer::New(env->isolate(), current->critical); |
379 | 0 | values[1] = env->dns_caa_string(); |
380 | 0 | Local<Object> caa_record; |
381 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values) |
382 | 0 | .ToLocal(&caa_record)) { |
383 | 0 | return Nothing<int>(); |
384 | 0 | } |
385 | | |
386 | | // This additional property is not part of the template as it is |
387 | | // variable based on the record. |
388 | 0 | if (caa_record |
389 | 0 | ->Set(env->context(), |
390 | 0 | OneByteString(env->isolate(), current->property), |
391 | 0 | OneByteString(env->isolate(), current->value)) |
392 | 0 | .IsNothing()) { |
393 | 0 | return Nothing<int>(); |
394 | 0 | } |
395 | | |
396 | 0 | if (ret->Set(env->context(), i + offset, caa_record).IsNothing()) { |
397 | 0 | return Nothing<int>(); |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | 0 | return Just<int>(ARES_SUCCESS); |
402 | 0 | } |
403 | | |
404 | | Maybe<int> ParseTlsaReply(Environment* env, |
405 | | unsigned char* buf, |
406 | | int len, |
407 | 0 | Local<Array> ret) { |
408 | 0 | EscapableHandleScope handle_scope(env->isolate()); |
409 | |
|
410 | 0 | auto tmpl = env->tlsa_record_template(); |
411 | 0 | if (tmpl.IsEmpty()) { |
412 | 0 | static constexpr std::string_view names[] = { |
413 | 0 | "certUsage", |
414 | 0 | "selector", |
415 | 0 | "match", |
416 | 0 | "data", |
417 | 0 | }; |
418 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
419 | 0 | env->set_tlsa_record_template(tmpl); |
420 | 0 | } |
421 | |
|
422 | 0 | ares_dns_record_t* dnsrec = nullptr; |
423 | |
|
424 | 0 | int status = ares_dns_parse(buf, len, 0, &dnsrec); |
425 | 0 | if (status != ARES_SUCCESS) { |
426 | 0 | ares_dns_record_destroy(dnsrec); |
427 | 0 | return Just<int>(status); |
428 | 0 | } |
429 | | |
430 | 0 | DeleteFnPtr<ares_dns_record_t, ares_dns_record_destroy> free_me(dnsrec); |
431 | |
|
432 | 0 | uint32_t offset = ret->Length(); |
433 | 0 | size_t rr_count = ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); |
434 | |
|
435 | 0 | MaybeLocal<Value> values[] = { |
436 | 0 | Undefined(env->isolate()), // certUsage |
437 | 0 | Undefined(env->isolate()), // selector |
438 | 0 | Undefined(env->isolate()), // match |
439 | 0 | Undefined(env->isolate()), // data |
440 | 0 | }; |
441 | |
|
442 | 0 | for (size_t i = 0; i < rr_count; i++) { |
443 | 0 | const ares_dns_rr_t* rr = |
444 | 0 | ares_dns_record_rr_get(dnsrec, ARES_SECTION_ANSWER, i); |
445 | |
|
446 | 0 | if (ares_dns_rr_get_type(rr) != ARES_REC_TYPE_TLSA) continue; |
447 | | |
448 | 0 | unsigned char certusage = ares_dns_rr_get_u8(rr, ARES_RR_TLSA_CERT_USAGE); |
449 | 0 | unsigned char selector = ares_dns_rr_get_u8(rr, ARES_RR_TLSA_SELECTOR); |
450 | 0 | unsigned char match = ares_dns_rr_get_u8(rr, ARES_RR_TLSA_MATCH); |
451 | 0 | size_t data_len; |
452 | 0 | const unsigned char* data = |
453 | 0 | ares_dns_rr_get_bin(rr, ARES_RR_TLSA_DATA, &data_len); |
454 | 0 | if (!data || data_len == 0) continue; |
455 | | |
456 | 0 | Local<ArrayBuffer> data_ab = ArrayBuffer::New(env->isolate(), data_len); |
457 | 0 | memcpy(data_ab->Data(), data, data_len); |
458 | |
|
459 | 0 | values[0] = Integer::NewFromUnsigned(env->isolate(), certusage); |
460 | 0 | values[1] = Integer::NewFromUnsigned(env->isolate(), selector); |
461 | 0 | values[2] = Integer::NewFromUnsigned(env->isolate(), match); |
462 | 0 | values[3] = data_ab; |
463 | |
|
464 | 0 | Local<Object> tlsa_rec; |
465 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values) |
466 | 0 | .ToLocal(&tlsa_rec) || |
467 | 0 | ret->Set(env->context(), offset + i, tlsa_rec).IsNothing()) { |
468 | 0 | return Nothing<int>(); |
469 | 0 | } |
470 | 0 | } |
471 | | |
472 | 0 | return Just<int>(ARES_SUCCESS); |
473 | 0 | } |
474 | | |
475 | | Maybe<int> ParseTxtReply(Environment* env, |
476 | | const unsigned char* buf, |
477 | | int len, |
478 | | Local<Array> ret, |
479 | 0 | bool need_type = false) { |
480 | 0 | HandleScope handle_scope(env->isolate()); |
481 | |
|
482 | 0 | auto tmpl = env->txt_record_template(); |
483 | 0 | if (tmpl.IsEmpty()) { |
484 | 0 | static constexpr std::string_view names[] = { |
485 | 0 | "entries", |
486 | 0 | "type", |
487 | 0 | }; |
488 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
489 | 0 | env->set_txt_record_template(tmpl); |
490 | 0 | } |
491 | |
|
492 | 0 | struct ares_txt_ext* txt_out; |
493 | |
|
494 | 0 | int status = ares_parse_txt_reply_ext(buf, len, &txt_out); |
495 | 0 | if (status != ARES_SUCCESS) return Just<int>(status); |
496 | 0 | DeleteFnPtr<void, ares_free_data> free_me(txt_out); |
497 | |
|
498 | 0 | Local<Array> txt_chunk; |
499 | 0 | LocalVector<Value> chunks(env->isolate()); |
500 | |
|
501 | 0 | struct ares_txt_ext* current = txt_out; |
502 | 0 | uint32_t i = 0; |
503 | 0 | uint32_t offset = ret->Length(); |
504 | |
|
505 | 0 | MaybeLocal<Value> values[] = { |
506 | 0 | Undefined(env->isolate()), // entries |
507 | 0 | Undefined(env->isolate()), // type |
508 | 0 | }; |
509 | |
|
510 | 0 | for (; current != nullptr; current = current->next) { |
511 | 0 | Local<String> txt = |
512 | 0 | OneByteString(env->isolate(), current->txt, current->length); |
513 | | |
514 | | // New record found - write out the current chunk |
515 | 0 | if (current->record_start) { |
516 | 0 | if (!chunks.empty()) { |
517 | 0 | auto txt_chunk = |
518 | 0 | Array::New(env->isolate(), chunks.data(), chunks.size()); |
519 | 0 | chunks.clear(); |
520 | 0 | if (need_type) { |
521 | 0 | values[0] = txt_chunk; |
522 | 0 | values[1] = env->dns_txt_string(); |
523 | 0 | Local<Object> elem; |
524 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values) |
525 | 0 | .ToLocal(&elem) || |
526 | 0 | ret->Set(env->context(), offset + i++, elem).IsNothing()) { |
527 | 0 | return Nothing<int>(); |
528 | 0 | } |
529 | 0 | } else if (ret->Set(env->context(), offset + i++, txt_chunk) |
530 | 0 | .IsNothing()) { |
531 | 0 | return Nothing<int>(); |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | 0 | txt_chunk = Array::New(env->isolate()); |
536 | 0 | } |
537 | | |
538 | 0 | chunks.push_back(txt); |
539 | 0 | } |
540 | | |
541 | | // Push last chunk if it isn't empty |
542 | 0 | if (!chunks.empty()) { |
543 | 0 | txt_chunk = Array::New(env->isolate(), chunks.data(), chunks.size()); |
544 | 0 | if (need_type) { |
545 | 0 | values[0] = txt_chunk; |
546 | 0 | values[1] = env->dns_txt_string(); |
547 | 0 | Local<Object> elem; |
548 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&elem) || |
549 | 0 | ret->Set(env->context(), offset + i, elem).IsNothing()) { |
550 | 0 | return Nothing<int>(); |
551 | 0 | } |
552 | 0 | } else if (ret->Set(env->context(), offset + i, txt_chunk).IsNothing()) { |
553 | 0 | return Nothing<int>(); |
554 | 0 | } |
555 | 0 | } |
556 | | |
557 | 0 | return Just<int>(ARES_SUCCESS); |
558 | 0 | } |
559 | | |
560 | | Maybe<int> ParseSrvReply(Environment* env, |
561 | | const unsigned char* buf, |
562 | | int len, |
563 | | Local<Array> ret, |
564 | 0 | bool need_type = false) { |
565 | 0 | HandleScope handle_scope(env->isolate()); |
566 | |
|
567 | 0 | auto tmpl = env->srv_record_template(); |
568 | 0 | if (tmpl.IsEmpty()) { |
569 | 0 | static constexpr std::string_view names[] = { |
570 | 0 | "name", |
571 | 0 | "port", |
572 | 0 | "priority", |
573 | 0 | "weight", |
574 | 0 | "type", |
575 | 0 | }; |
576 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
577 | 0 | env->set_srv_record_template(tmpl); |
578 | 0 | } |
579 | |
|
580 | 0 | struct ares_srv_reply* srv_start; |
581 | 0 | int status = ares_parse_srv_reply(buf, len, &srv_start); |
582 | 0 | if (status != ARES_SUCCESS) return Just<int>(status); |
583 | 0 | DeleteFnPtr<void, ares_free_data> free_me(srv_start); |
584 | |
|
585 | 0 | MaybeLocal<Value> values[] = { |
586 | 0 | Undefined(env->isolate()), // name |
587 | 0 | Undefined(env->isolate()), // port |
588 | 0 | Undefined(env->isolate()), // priority |
589 | 0 | Undefined(env->isolate()), // weight |
590 | 0 | Undefined(env->isolate()), // type |
591 | 0 | }; |
592 | |
|
593 | 0 | ares_srv_reply* current = srv_start; |
594 | 0 | int offset = ret->Length(); |
595 | 0 | for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { |
596 | 0 | values[0] = OneByteString(env->isolate(), current->host); |
597 | 0 | values[1] = Integer::New(env->isolate(), current->port); |
598 | 0 | values[2] = Integer::New(env->isolate(), current->priority); |
599 | 0 | values[3] = Integer::New(env->isolate(), current->weight); |
600 | 0 | values[4] = env->dns_srv_string(); |
601 | |
|
602 | 0 | Local<Object> srv_record; |
603 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values) |
604 | 0 | .ToLocal(&srv_record) || |
605 | 0 | ret->Set(env->context(), i + offset, srv_record).IsNothing()) { |
606 | 0 | return Nothing<int>(); |
607 | 0 | } |
608 | 0 | } |
609 | | |
610 | 0 | return Just<int>(ARES_SUCCESS); |
611 | 0 | } |
612 | | |
613 | | Maybe<int> ParseNaptrReply(Environment* env, |
614 | | const unsigned char* buf, |
615 | | int len, |
616 | | Local<Array> ret, |
617 | 0 | bool need_type = false) { |
618 | 0 | HandleScope handle_scope(env->isolate()); |
619 | |
|
620 | 0 | auto tmpl = env->naptr_record_template(); |
621 | 0 | if (tmpl.IsEmpty()) { |
622 | 0 | static constexpr std::string_view names[] = { |
623 | 0 | "flags", |
624 | 0 | "service", |
625 | 0 | "regexp", |
626 | 0 | "replacement", |
627 | 0 | "order", |
628 | 0 | "preference", |
629 | 0 | "type", |
630 | 0 | }; |
631 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
632 | 0 | env->set_naptr_record_template(tmpl); |
633 | 0 | } |
634 | |
|
635 | 0 | ares_naptr_reply* naptr_start; |
636 | 0 | int status = ares_parse_naptr_reply(buf, len, &naptr_start); |
637 | 0 | if (status != ARES_SUCCESS) return Just<int>(status); |
638 | 0 | DeleteFnPtr<void, ares_free_data> free_me(naptr_start); |
639 | |
|
640 | 0 | MaybeLocal<Value> values[] = { |
641 | 0 | Undefined(env->isolate()), // flags |
642 | 0 | Undefined(env->isolate()), // service |
643 | 0 | Undefined(env->isolate()), // regexp |
644 | 0 | Undefined(env->isolate()), // replacement |
645 | 0 | Undefined(env->isolate()), // order |
646 | 0 | Undefined(env->isolate()), // preference |
647 | 0 | Undefined(env->isolate()), // type |
648 | 0 | }; |
649 | |
|
650 | 0 | ares_naptr_reply* current = naptr_start; |
651 | 0 | int offset = ret->Length(); |
652 | 0 | for (uint32_t i = 0; current != nullptr; ++i, current = current->next) { |
653 | 0 | values[0] = OneByteString(env->isolate(), current->flags); |
654 | 0 | values[1] = OneByteString(env->isolate(), current->service); |
655 | 0 | values[2] = OneByteString(env->isolate(), current->regexp); |
656 | 0 | values[3] = OneByteString(env->isolate(), current->replacement); |
657 | 0 | values[4] = Integer::New(env->isolate(), current->order); |
658 | 0 | values[5] = Integer::New(env->isolate(), current->preference); |
659 | 0 | if (need_type) { |
660 | 0 | values[6] = env->dns_naptr_string(); |
661 | 0 | } |
662 | |
|
663 | 0 | Local<Object> naptr_record; |
664 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values) |
665 | 0 | .ToLocal(&naptr_record) || |
666 | 0 | ret->Set(env->context(), i + offset, naptr_record).IsNothing()) { |
667 | 0 | return Nothing<int>(); |
668 | 0 | } |
669 | 0 | } |
670 | | |
671 | 0 | return Just<int>(ARES_SUCCESS); |
672 | 0 | } |
673 | | |
674 | 0 | Local<DictionaryTemplate> getSoaRecordTemplate(Environment* env) { |
675 | 0 | auto tmpl = env->soa_record_template(); |
676 | 0 | if (tmpl.IsEmpty()) { |
677 | 0 | static constexpr std::string_view names[] = { |
678 | 0 | "nsname", |
679 | 0 | "hostmaster", |
680 | 0 | "serial", |
681 | 0 | "refresh", |
682 | 0 | "retry", |
683 | 0 | "expire", |
684 | 0 | "minttl", |
685 | 0 | "type", |
686 | 0 | }; |
687 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
688 | 0 | env->set_soa_record_template(tmpl); |
689 | 0 | } |
690 | 0 | return tmpl; |
691 | 0 | } |
692 | | |
693 | | Maybe<int> ParseSoaReply(Environment* env, |
694 | | unsigned char* buf, |
695 | | int len, |
696 | 0 | Local<Object>* ret) { |
697 | 0 | EscapableHandleScope handle_scope(env->isolate()); |
698 | |
|
699 | 0 | auto tmpl = getSoaRecordTemplate(env); |
700 | | |
701 | | // Manage memory using standardard smart pointer std::unique_tr |
702 | 0 | struct AresDeleter { |
703 | 0 | void operator()(char* ptr) const noexcept { ares_free_string(ptr); } |
704 | 0 | }; |
705 | 0 | using ares_unique_ptr = std::unique_ptr<char[], AresDeleter>; |
706 | | |
707 | | // Can't use ares_parse_soa_reply() here which can only parse single record |
708 | 0 | const unsigned int ancount = cares_get_16bit(buf + 6); |
709 | 0 | unsigned char* ptr = buf + NS_HFIXEDSZ; |
710 | 0 | char* name_temp = nullptr; |
711 | 0 | long temp_len; // NOLINT(runtime/int) |
712 | 0 | int status = ares_expand_name(ptr, buf, len, &name_temp, &temp_len); |
713 | 0 | if (status != ARES_SUCCESS) { |
714 | | // returns EBADRESP in case of invalid input |
715 | 0 | return Just<int>(status == ARES_EBADNAME ? ARES_EBADRESP : status); |
716 | 0 | } |
717 | | |
718 | 0 | const ares_unique_ptr name(name_temp); |
719 | |
|
720 | 0 | if (ptr + temp_len + NS_QFIXEDSZ > buf + len) { |
721 | 0 | return Just<int>(ARES_EBADRESP); |
722 | 0 | } |
723 | 0 | ptr += temp_len + NS_QFIXEDSZ; |
724 | |
|
725 | 0 | MaybeLocal<Value> values[] = { |
726 | 0 | Undefined(env->isolate()), // nsname |
727 | 0 | Undefined(env->isolate()), // hostmaster |
728 | 0 | Undefined(env->isolate()), // serial |
729 | 0 | Undefined(env->isolate()), // refresh |
730 | 0 | Undefined(env->isolate()), // retry |
731 | 0 | Undefined(env->isolate()), // expire |
732 | 0 | Undefined(env->isolate()), // minttl |
733 | 0 | Undefined(env->isolate()), // type |
734 | 0 | }; |
735 | |
|
736 | 0 | for (unsigned int i = 0; i < ancount; i++) { |
737 | 0 | char* rr_name_temp = nullptr; |
738 | 0 | long rr_temp_len; // NOLINT(runtime/int) |
739 | 0 | int status2 = ares_expand_name(ptr, buf, len, &rr_name_temp, &rr_temp_len); |
740 | |
|
741 | 0 | if (status2 != ARES_SUCCESS) |
742 | 0 | return Just<int>(status2 == ARES_EBADNAME ? ARES_EBADRESP : status2); |
743 | | |
744 | 0 | const ares_unique_ptr rr_name(rr_name_temp); |
745 | |
|
746 | 0 | ptr += rr_temp_len; |
747 | 0 | if (ptr + NS_RRFIXEDSZ > buf + len) { |
748 | 0 | return Just<int>(ARES_EBADRESP); |
749 | 0 | } |
750 | | |
751 | 0 | const int rr_type = cares_get_16bit(ptr); |
752 | 0 | const int rr_len = cares_get_16bit(ptr + 8); |
753 | 0 | ptr += NS_RRFIXEDSZ; |
754 | | |
755 | | // only need SOA |
756 | 0 | if (rr_type == ns_t_soa) { |
757 | 0 | char* nsname_temp = nullptr; |
758 | 0 | long nsname_temp_len; // NOLINT(runtime/int) |
759 | |
|
760 | 0 | int status3 = ares_expand_name(ptr, buf, len, |
761 | 0 | &nsname_temp, |
762 | 0 | &nsname_temp_len); |
763 | 0 | if (status3 != ARES_SUCCESS) { |
764 | 0 | return Just<int>(status3 == ARES_EBADNAME ? ARES_EBADRESP : status3); |
765 | 0 | } |
766 | 0 | const ares_unique_ptr nsname(nsname_temp); |
767 | 0 | ptr += nsname_temp_len; |
768 | |
|
769 | 0 | char* hostmaster_temp = nullptr; |
770 | 0 | long hostmaster_temp_len; // NOLINT(runtime/int) |
771 | 0 | int status4 = ares_expand_name(ptr, buf, len, |
772 | 0 | &hostmaster_temp, |
773 | 0 | &hostmaster_temp_len); |
774 | 0 | if (status4 != ARES_SUCCESS) { |
775 | 0 | return Just<int>(status4 == ARES_EBADNAME ? ARES_EBADRESP : status4); |
776 | 0 | } |
777 | 0 | const ares_unique_ptr hostmaster(hostmaster_temp); |
778 | 0 | ptr += hostmaster_temp_len; |
779 | |
|
780 | 0 | if (ptr + 5 * 4 > buf + len) { |
781 | 0 | return Just<int>(ARES_EBADRESP); |
782 | 0 | } |
783 | | |
784 | 0 | const unsigned int serial = nbytes::ReadUint32BE(ptr + 0 * 4); |
785 | 0 | const unsigned int refresh = nbytes::ReadUint32BE(ptr + 1 * 4); |
786 | 0 | const unsigned int retry = nbytes::ReadUint32BE(ptr + 2 * 4); |
787 | 0 | const unsigned int expire = nbytes::ReadUint32BE(ptr + 3 * 4); |
788 | 0 | const unsigned int minttl = nbytes::ReadUint32BE(ptr + 4 * 4); |
789 | |
|
790 | 0 | values[0] = OneByteString(env->isolate(), nsname.get()); |
791 | 0 | values[1] = OneByteString(env->isolate(), hostmaster.get()); |
792 | 0 | values[2] = Integer::NewFromUnsigned(env->isolate(), serial); |
793 | 0 | values[3] = Integer::New(env->isolate(), refresh); |
794 | 0 | values[4] = Integer::New(env->isolate(), retry); |
795 | 0 | values[5] = Integer::New(env->isolate(), expire); |
796 | 0 | values[6] = Integer::NewFromUnsigned(env->isolate(), minttl); |
797 | 0 | values[7] = env->dns_soa_string(); |
798 | 0 | Local<Object> soa_record; |
799 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values) |
800 | 0 | .ToLocal(&soa_record)) { |
801 | 0 | return Nothing<int>(); |
802 | 0 | } |
803 | | |
804 | 0 | *ret = handle_scope.Escape(soa_record); |
805 | 0 | break; |
806 | 0 | } |
807 | | |
808 | 0 | ptr += rr_len; |
809 | 0 | } |
810 | | |
811 | 0 | return Just<int>(ARES_SUCCESS); |
812 | 0 | } |
813 | | } // anonymous namespace |
814 | | |
815 | | ChannelWrap::ChannelWrap(Environment* env, |
816 | | Local<Object> object, |
817 | | int timeout, |
818 | | int tries, |
819 | | int max_timeout) |
820 | 0 | : AsyncWrap(env, object, PROVIDER_DNSCHANNEL), |
821 | 0 | timeout_(timeout), |
822 | 0 | tries_(tries), |
823 | 0 | max_timeout_(max_timeout) { |
824 | 0 | MakeWeak(); |
825 | |
|
826 | 0 | Setup(); |
827 | 0 | } |
828 | | |
829 | 0 | void ChannelWrap::MemoryInfo(MemoryTracker* tracker) const { |
830 | 0 | if (timer_handle_ != nullptr) |
831 | 0 | tracker->TrackField("timer_handle", *timer_handle_); |
832 | 0 | tracker->TrackField("task_list", task_list_, "NodeAresTask::List"); |
833 | 0 | } |
834 | | |
835 | 0 | void ChannelWrap::New(const FunctionCallbackInfo<Value>& args) { |
836 | 0 | CHECK(args.IsConstructCall()); |
837 | 0 | CHECK_EQ(args.Length(), 3); |
838 | 0 | CHECK(args[0]->IsInt32()); |
839 | 0 | CHECK(args[1]->IsInt32()); |
840 | 0 | CHECK(args[2]->IsInt32()); |
841 | 0 | const int timeout = args[0].As<Int32>()->Value(); |
842 | 0 | const int tries = args[1].As<Int32>()->Value(); |
843 | 0 | const int max_timeout = args[2].As<Int32>()->Value(); |
844 | 0 | Environment* env = Environment::GetCurrent(args); |
845 | 0 | new ChannelWrap(env, args.This(), timeout, tries, max_timeout); |
846 | 0 | } |
847 | | |
848 | | GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env, |
849 | | Local<Object> req_wrap_obj, |
850 | | uint8_t order) |
851 | 0 | : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP), |
852 | 0 | order_(order) {} |
853 | | |
854 | | GetNameInfoReqWrap::GetNameInfoReqWrap( |
855 | | Environment* env, |
856 | | Local<Object> req_wrap_obj) |
857 | 0 | : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) {} |
858 | | |
859 | | /* This is called once per second by loop->timer. It is used to constantly */ |
860 | | /* call back into c-ares for possibly processing timeouts. */ |
861 | 0 | void ChannelWrap::AresTimeout(uv_timer_t* handle) { |
862 | 0 | ChannelWrap* channel = static_cast<ChannelWrap*>(handle->data); |
863 | 0 | CHECK_EQ(channel->timer_handle(), handle); |
864 | 0 | ares_process_fd(channel->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD); |
865 | 0 | } |
866 | | |
867 | | |
868 | 0 | void NodeAresTask::MemoryInfo(MemoryTracker* tracker) const { |
869 | 0 | tracker->TrackField("channel", channel); |
870 | 0 | } |
871 | | |
872 | | /* Allocates and returns a new NodeAresTask */ |
873 | 0 | NodeAresTask* NodeAresTask::Create(ChannelWrap* channel, ares_socket_t sock) { |
874 | 0 | auto task = new NodeAresTask(); |
875 | |
|
876 | 0 | task->channel = channel; |
877 | 0 | task->sock = sock; |
878 | |
|
879 | 0 | if (uv_poll_init_socket(channel->env()->event_loop(), |
880 | 0 | &task->poll_watcher, sock) < 0) { |
881 | | /* This should never happen. */ |
882 | 0 | delete task; |
883 | 0 | return nullptr; |
884 | 0 | } |
885 | | |
886 | 0 | return task; |
887 | 0 | } |
888 | | |
889 | 0 | void ChannelWrap::Setup() { |
890 | 0 | struct ares_options options; |
891 | 0 | memset(&options, 0, sizeof(options)); |
892 | 0 | options.flags = ARES_FLAG_NOCHECKRESP; |
893 | 0 | options.sock_state_cb = ares_sockstate_cb; |
894 | 0 | options.sock_state_cb_data = this; |
895 | 0 | options.timeout = timeout_; |
896 | 0 | options.tries = tries_; |
897 | 0 | options.qcache_max_ttl = 0; |
898 | |
|
899 | 0 | int r; |
900 | 0 | if (!library_inited_) { |
901 | 0 | Mutex::ScopedLock lock(ares_library_mutex); |
902 | | // Multiple calls to ares_library_init() increase a reference counter, |
903 | | // so this is a no-op except for the first call to it. |
904 | 0 | r = ares_library_init(ARES_LIB_INIT_ALL); |
905 | 0 | if (r != ARES_SUCCESS) |
906 | 0 | return env()->ThrowError(ToErrorCodeString(r)); |
907 | 0 | } |
908 | | |
909 | | /* We do the call to ares_init_option for caller. */ |
910 | 0 | int optmask = ARES_OPT_FLAGS | ARES_OPT_TIMEOUTMS | ARES_OPT_SOCK_STATE_CB | |
911 | 0 | ARES_OPT_TRIES | ARES_OPT_QUERY_CACHE; |
912 | |
|
913 | 0 | if (max_timeout_ > 0) { |
914 | 0 | options.maxtimeout = max_timeout_; |
915 | 0 | optmask |= ARES_OPT_MAXTIMEOUTMS; |
916 | 0 | } |
917 | |
|
918 | 0 | r = ares_init_options(&channel_, &options, optmask); |
919 | |
|
920 | 0 | if (r != ARES_SUCCESS) { |
921 | 0 | Mutex::ScopedLock lock(ares_library_mutex); |
922 | 0 | ares_library_cleanup(); |
923 | 0 | return env()->ThrowError(ToErrorCodeString(r)); |
924 | 0 | } |
925 | | |
926 | 0 | library_inited_ = true; |
927 | 0 | } |
928 | | |
929 | 0 | void ChannelWrap::StartTimer() { |
930 | 0 | if (timer_handle_ == nullptr) { |
931 | 0 | timer_handle_ = new uv_timer_t(); |
932 | 0 | timer_handle_->data = static_cast<void*>(this); |
933 | 0 | uv_timer_init(env()->event_loop(), timer_handle_); |
934 | 0 | } else if (uv_is_active(reinterpret_cast<uv_handle_t*>(timer_handle_))) { |
935 | 0 | return; |
936 | 0 | } |
937 | 0 | int timeout = timeout_; |
938 | 0 | if (timeout <= 0 || timeout > 1000) timeout = 1000; |
939 | 0 | uv_timer_start(timer_handle_, AresTimeout, timeout, timeout); |
940 | 0 | } |
941 | | |
942 | 0 | void ChannelWrap::CloseTimer() { |
943 | 0 | if (timer_handle_ == nullptr) |
944 | 0 | return; |
945 | | |
946 | 0 | env()->CloseHandle(timer_handle_, [](uv_timer_t* handle) { delete handle; }); |
947 | 0 | timer_handle_ = nullptr; |
948 | 0 | } |
949 | | |
950 | 0 | ChannelWrap::~ChannelWrap() { |
951 | 0 | ares_destroy(channel_); |
952 | |
|
953 | 0 | if (library_inited_) { |
954 | 0 | Mutex::ScopedLock lock(ares_library_mutex); |
955 | | // This decreases the reference counter increased by ares_library_init(). |
956 | 0 | ares_library_cleanup(); |
957 | 0 | } |
958 | |
|
959 | 0 | CloseTimer(); |
960 | 0 | } |
961 | | |
962 | | |
963 | 0 | void ChannelWrap::ModifyActivityQueryCount(int count) { |
964 | 0 | active_query_count_ += count; |
965 | 0 | CHECK_GE(active_query_count_, 0); |
966 | 0 | } |
967 | | |
968 | | |
969 | | /** |
970 | | * This function is to check whether current servers are fallback servers |
971 | | * when cares initialized. |
972 | | * |
973 | | * The fallback servers of cares is [ "127.0.0.1" ] with no user additional |
974 | | * setting. |
975 | | */ |
976 | 0 | void ChannelWrap::EnsureServers() { |
977 | | /* if last query is OK or servers are set by user self, do not check */ |
978 | 0 | if (query_last_ok_ || !is_servers_default_) { |
979 | 0 | return; |
980 | 0 | } |
981 | | |
982 | 0 | ares_addr_port_node* servers = nullptr; |
983 | |
|
984 | 0 | ares_get_servers_ports(channel_, &servers); |
985 | | |
986 | | /* if no server or multi-servers, ignore */ |
987 | 0 | if (servers == nullptr) return; |
988 | 0 | if (servers->next != nullptr) { |
989 | 0 | ares_free_data(servers); |
990 | 0 | is_servers_default_ = false; |
991 | 0 | return; |
992 | 0 | } |
993 | | |
994 | | /* if the only server is not 127.0.0.1, ignore */ |
995 | 0 | if (servers[0].family != AF_INET || |
996 | 0 | servers[0].addr.addr4.s_addr != htonl(INADDR_LOOPBACK) || |
997 | 0 | servers[0].tcp_port != 0 || |
998 | 0 | servers[0].udp_port != 0) { |
999 | 0 | ares_free_data(servers); |
1000 | 0 | is_servers_default_ = false; |
1001 | 0 | return; |
1002 | 0 | } |
1003 | | |
1004 | 0 | ares_free_data(servers); |
1005 | 0 | servers = nullptr; |
1006 | | |
1007 | | /* destroy channel and reset channel */ |
1008 | 0 | ares_destroy(channel_); |
1009 | |
|
1010 | 0 | CloseTimer(); |
1011 | 0 | Setup(); |
1012 | 0 | } |
1013 | | |
1014 | 0 | int AnyTraits::Send(QueryWrap<AnyTraits>* wrap, const char* name) { |
1015 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_ANY); |
1016 | 0 | return ARES_SUCCESS; |
1017 | 0 | } |
1018 | | |
1019 | 0 | int ATraits::Send(QueryWrap<ATraits>* wrap, const char* name) { |
1020 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_A); |
1021 | 0 | return ARES_SUCCESS; |
1022 | 0 | } |
1023 | | |
1024 | 0 | int AaaaTraits::Send(QueryWrap<AaaaTraits>* wrap, const char* name) { |
1025 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_AAAA); |
1026 | 0 | return ARES_SUCCESS; |
1027 | 0 | } |
1028 | | |
1029 | 0 | int CaaTraits::Send(QueryWrap<CaaTraits>* wrap, const char* name) { |
1030 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_CAA); |
1031 | 0 | return ARES_SUCCESS; |
1032 | 0 | } |
1033 | | |
1034 | 0 | int CnameTraits::Send(QueryWrap<CnameTraits>* wrap, const char* name) { |
1035 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_CNAME); |
1036 | 0 | return ARES_SUCCESS; |
1037 | 0 | } |
1038 | | |
1039 | 0 | int MxTraits::Send(QueryWrap<MxTraits>* wrap, const char* name) { |
1040 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_MX); |
1041 | 0 | return ARES_SUCCESS; |
1042 | 0 | } |
1043 | | |
1044 | 0 | int NsTraits::Send(QueryWrap<NsTraits>* wrap, const char* name) { |
1045 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_NS); |
1046 | 0 | return ARES_SUCCESS; |
1047 | 0 | } |
1048 | | |
1049 | 0 | int TlsaTraits::Send(QueryWrap<TlsaTraits>* wrap, const char* name) { |
1050 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_TLSA); |
1051 | 0 | return ARES_SUCCESS; |
1052 | 0 | } |
1053 | | |
1054 | 0 | int TxtTraits::Send(QueryWrap<TxtTraits>* wrap, const char* name) { |
1055 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_TXT); |
1056 | 0 | return ARES_SUCCESS; |
1057 | 0 | } |
1058 | | |
1059 | 0 | int SrvTraits::Send(QueryWrap<SrvTraits>* wrap, const char* name) { |
1060 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_SRV); |
1061 | 0 | return ARES_SUCCESS; |
1062 | 0 | } |
1063 | | |
1064 | 0 | int PtrTraits::Send(QueryWrap<PtrTraits>* wrap, const char* name) { |
1065 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_PTR); |
1066 | 0 | return ARES_SUCCESS; |
1067 | 0 | } |
1068 | | |
1069 | 0 | int NaptrTraits::Send(QueryWrap<NaptrTraits>* wrap, const char* name) { |
1070 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_NAPTR); |
1071 | 0 | return ARES_SUCCESS; |
1072 | 0 | } |
1073 | | |
1074 | 0 | int SoaTraits::Send(QueryWrap<SoaTraits>* wrap, const char* name) { |
1075 | 0 | wrap->AresQuery(name, ARES_CLASS_IN, ARES_REC_TYPE_SOA); |
1076 | 0 | return ARES_SUCCESS; |
1077 | 0 | } |
1078 | | |
1079 | | Maybe<int> AnyTraits::Parse(QueryAnyWrap* wrap, |
1080 | 0 | const std::unique_ptr<ResponseData>& response) { |
1081 | 0 | if (response->is_host) [[unlikely]] { |
1082 | 0 | return Just<int>(ARES_EBADRESP); |
1083 | 0 | } |
1084 | | |
1085 | 0 | unsigned char* buf = response->buf.data; |
1086 | 0 | int len = response->buf.size; |
1087 | |
|
1088 | 0 | Environment* env = wrap->env(); |
1089 | 0 | HandleScope handle_scope(env->isolate()); |
1090 | 0 | Context::Scope context_scope(env->context()); |
1091 | |
|
1092 | 0 | Local<Array> ret = Array::New(env->isolate()); |
1093 | 0 | int type, status, old_count; |
1094 | | |
1095 | | /* Parse A records or CNAME records */ |
1096 | 0 | ares_addrttl addrttls[256]; |
1097 | 0 | int naddrttls = arraysize(addrttls); |
1098 | |
|
1099 | 0 | type = ns_t_cname_or_a; |
1100 | 0 | if (!ParseGeneralReply(env, buf, len, &type, ret, addrttls, &naddrttls) |
1101 | 0 | .To(&status)) { |
1102 | 0 | return Nothing<int>(); |
1103 | 0 | } |
1104 | 0 | uint32_t a_count = ret->Length(); |
1105 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1106 | 0 | return Just<int>(status); |
1107 | 0 | } |
1108 | | |
1109 | 0 | if (type == ns_t_a) { |
1110 | 0 | CHECK_EQ(static_cast<uint32_t>(naddrttls), a_count); |
1111 | | |
1112 | 0 | auto tmpl = env->a_record_template(); |
1113 | 0 | if (tmpl.IsEmpty()) { |
1114 | 0 | static constexpr std::string_view names[] = { |
1115 | 0 | "address", |
1116 | 0 | "ttl", |
1117 | 0 | "type", |
1118 | 0 | }; |
1119 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
1120 | 0 | env->set_a_record_template(tmpl); |
1121 | 0 | } |
1122 | 0 | MaybeLocal<Value> values[] = { |
1123 | 0 | Undefined(env->isolate()), // address |
1124 | 0 | Undefined(env->isolate()), // ttl |
1125 | 0 | Undefined(env->isolate()), // type |
1126 | 0 | }; |
1127 | |
|
1128 | 0 | for (uint32_t i = 0; i < a_count; i++) { |
1129 | 0 | Local<Value> address; |
1130 | 0 | if (!ret->Get(env->context(), i).ToLocal(&address)) { |
1131 | 0 | return Nothing<int>(); |
1132 | 0 | } |
1133 | 0 | values[0] = address; |
1134 | 0 | values[1] = Integer::NewFromUnsigned(env->isolate(), addrttls[i].ttl); |
1135 | 0 | values[2] = env->dns_a_string(); |
1136 | |
|
1137 | 0 | Local<Object> obj; |
1138 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&obj) || |
1139 | 0 | ret->Set(env->context(), i, obj).IsNothing()) { |
1140 | 0 | return Nothing<int>(); |
1141 | 0 | } |
1142 | 0 | } |
1143 | 0 | } else { |
1144 | 0 | auto tmpl = env->cname_record_template(); |
1145 | 0 | if (tmpl.IsEmpty()) { |
1146 | 0 | static constexpr std::string_view names[] = { |
1147 | 0 | "value", |
1148 | 0 | "type", |
1149 | 0 | }; |
1150 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
1151 | 0 | env->set_cname_record_template(tmpl); |
1152 | 0 | } |
1153 | 0 | MaybeLocal<Value> values[] = { |
1154 | 0 | Undefined(env->isolate()), // value |
1155 | 0 | Undefined(env->isolate()), // type |
1156 | 0 | }; |
1157 | 0 | for (uint32_t i = 0; i < a_count; i++) { |
1158 | 0 | Local<Value> value; |
1159 | 0 | if (!ret->Get(env->context(), i).ToLocal(&value)) { |
1160 | 0 | return Nothing<int>(); |
1161 | 0 | } |
1162 | 0 | values[0] = value; |
1163 | 0 | values[1] = env->dns_cname_string(); |
1164 | 0 | Local<Object> obj; |
1165 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&obj) || |
1166 | 0 | ret->Set(env->context(), i, obj).IsNothing()) { |
1167 | 0 | return Nothing<int>(); |
1168 | 0 | } |
1169 | 0 | } |
1170 | 0 | } |
1171 | | |
1172 | | /* Parse AAAA records */ |
1173 | 0 | ares_addr6ttl addr6ttls[256]; |
1174 | 0 | int naddr6ttls = arraysize(addr6ttls); |
1175 | |
|
1176 | 0 | type = ns_t_aaaa; |
1177 | 0 | if (!ParseGeneralReply(env, buf, len, &type, ret, addr6ttls, &naddr6ttls) |
1178 | 0 | .To(&status)) { |
1179 | 0 | return Nothing<int>(); |
1180 | 0 | } |
1181 | 0 | uint32_t aaaa_count = ret->Length() - a_count; |
1182 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) |
1183 | 0 | return Just<int>(status); |
1184 | | |
1185 | 0 | CHECK_EQ(aaaa_count, static_cast<uint32_t>(naddr6ttls)); |
1186 | 0 | CHECK_EQ(ret->Length(), a_count + aaaa_count); |
1187 | | |
1188 | 0 | auto tmpl = env->aaaa_record_template(); |
1189 | 0 | if (tmpl.IsEmpty()) { |
1190 | 0 | static constexpr std::string_view names[] = { |
1191 | 0 | "address", |
1192 | 0 | "ttl", |
1193 | 0 | "type", |
1194 | 0 | }; |
1195 | 0 | tmpl = DictionaryTemplate::New(env->isolate(), names); |
1196 | 0 | env->set_aaaa_record_template(tmpl); |
1197 | 0 | } |
1198 | |
|
1199 | 0 | MaybeLocal<Value> values[] = { |
1200 | 0 | Undefined(env->isolate()), // address |
1201 | 0 | Undefined(env->isolate()), // ttl |
1202 | 0 | Undefined(env->isolate()), // type |
1203 | 0 | }; |
1204 | |
|
1205 | 0 | for (uint32_t i = a_count; i < ret->Length(); i++) { |
1206 | 0 | Local<Value> address; |
1207 | 0 | if (!ret->Get(env->context(), i).ToLocal(&address)) { |
1208 | 0 | return Nothing<int>(); |
1209 | 0 | } |
1210 | 0 | values[0] = address; |
1211 | 0 | values[1] = |
1212 | 0 | Integer::NewFromUnsigned(env->isolate(), addr6ttls[i - a_count].ttl); |
1213 | 0 | values[2] = env->dns_aaaa_string(); |
1214 | 0 | Local<Object> obj; |
1215 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values).ToLocal(&obj) || |
1216 | 0 | ret->Set(env->context(), i, obj).IsNothing()) { |
1217 | 0 | return Nothing<int>(); |
1218 | 0 | } |
1219 | 0 | } |
1220 | | |
1221 | | /* Parse MX records */ |
1222 | 0 | if (!ParseMxReply(env, buf, len, ret, true).To(&status)) { |
1223 | 0 | return Nothing<int>(); |
1224 | 0 | } |
1225 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1226 | 0 | return Just<int>(status); |
1227 | 0 | } |
1228 | | |
1229 | | /* Parse NS records */ |
1230 | 0 | type = ns_t_ns; |
1231 | 0 | old_count = ret->Length(); |
1232 | 0 | if (!ParseGeneralReply(env, buf, len, &type, ret).To(&status)) { |
1233 | 0 | return Nothing<int>(); |
1234 | 0 | } |
1235 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1236 | 0 | return Just<int>(status); |
1237 | 0 | } |
1238 | | |
1239 | 0 | auto dns_ns_tmpl = env->dns_ns_record_template(); |
1240 | 0 | if (dns_ns_tmpl.IsEmpty()) { |
1241 | 0 | static constexpr std::string_view names[] = { |
1242 | 0 | "value", |
1243 | 0 | "type", |
1244 | 0 | }; |
1245 | 0 | dns_ns_tmpl = DictionaryTemplate::New(env->isolate(), names); |
1246 | 0 | env->set_dns_ns_record_template(dns_ns_tmpl); |
1247 | 0 | } |
1248 | |
|
1249 | 0 | MaybeLocal<Value> values_ns[] = { |
1250 | 0 | Undefined(env->isolate()), // value |
1251 | 0 | Undefined(env->isolate()), // type |
1252 | 0 | }; |
1253 | |
|
1254 | 0 | for (uint32_t i = old_count; i < ret->Length(); i++) { |
1255 | 0 | Local<Value> value; |
1256 | 0 | if (!ret->Get(env->context(), i).ToLocal(&value)) { |
1257 | 0 | return Nothing<int>(); |
1258 | 0 | } |
1259 | 0 | values_ns[0] = value; |
1260 | 0 | values_ns[1] = env->dns_ns_string(); |
1261 | 0 | Local<Object> obj; |
1262 | 0 | if (!NewDictionaryInstance(env->context(), dns_ns_tmpl, values_ns) |
1263 | 0 | .ToLocal(&obj) || |
1264 | 0 | ret->Set(env->context(), i, obj).IsNothing()) { |
1265 | 0 | return Nothing<int>(); |
1266 | 0 | } |
1267 | 0 | } |
1268 | | |
1269 | | /* Parse TXT records */ |
1270 | 0 | if (!ParseTxtReply(env, buf, len, ret, true).To(&status)) { |
1271 | 0 | return Nothing<int>(); |
1272 | 0 | } |
1273 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1274 | 0 | return Just<int>(status); |
1275 | 0 | } |
1276 | | |
1277 | | /* Parse SRV records */ |
1278 | 0 | if (!ParseSrvReply(env, buf, len, ret, true).To(&status)) { |
1279 | 0 | return Nothing<int>(); |
1280 | 0 | } |
1281 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1282 | 0 | return Just<int>(status); |
1283 | 0 | } |
1284 | | |
1285 | | /* Parse PTR records */ |
1286 | 0 | type = ns_t_ptr; |
1287 | 0 | old_count = ret->Length(); |
1288 | 0 | if (!ParseGeneralReply(env, buf, len, &type, ret).To(&status)) { |
1289 | 0 | return Nothing<int>(); |
1290 | 0 | } |
1291 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) |
1292 | 0 | return Just<int>(status); |
1293 | | |
1294 | 0 | for (uint32_t i = old_count; i < ret->Length(); i++) { |
1295 | 0 | Local<Value> value; |
1296 | 0 | if (!ret->Get(env->context(), i).ToLocal(&value)) { |
1297 | 0 | return Nothing<int>(); |
1298 | 0 | } |
1299 | 0 | values_ns[0] = value; |
1300 | 0 | values_ns[1] = env->dns_ptr_string(); |
1301 | 0 | Local<Object> obj; |
1302 | 0 | if (!NewDictionaryInstance(env->context(), dns_ns_tmpl, values_ns) |
1303 | 0 | .ToLocal(&obj) || |
1304 | 0 | ret->Set(env->context(), i, obj).IsNothing()) { |
1305 | 0 | return Nothing<int>(); |
1306 | 0 | } |
1307 | 0 | } |
1308 | | |
1309 | | /* Parse NAPTR records */ |
1310 | 0 | if (!ParseNaptrReply(env, buf, len, ret, true).To(&status)) { |
1311 | 0 | return Nothing<int>(); |
1312 | 0 | } |
1313 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1314 | 0 | return Just<int>(status); |
1315 | 0 | } |
1316 | | |
1317 | | /* Parse SOA records */ |
1318 | 0 | Local<Object> soa_record = Local<Object>(); |
1319 | 0 | if (!ParseSoaReply(env, buf, len, &soa_record).To(&status)) { |
1320 | 0 | return Nothing<int>(); |
1321 | 0 | } |
1322 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1323 | 0 | return Just<int>(status); |
1324 | 0 | } |
1325 | | |
1326 | 0 | if (!soa_record.IsEmpty()) { |
1327 | 0 | if (ret->Set(env->context(), ret->Length(), soa_record).IsNothing()) { |
1328 | 0 | return Just<int>(ARES_ENOMEM); |
1329 | 0 | } |
1330 | 0 | } |
1331 | | |
1332 | | /* Parse TLSA records */ |
1333 | 0 | if (!ParseTlsaReply(env, buf, len, ret).To(&status)) { |
1334 | 0 | return Nothing<int>(); |
1335 | 0 | } |
1336 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1337 | 0 | return Just<int>(status); |
1338 | 0 | } |
1339 | | |
1340 | | /* Parse CAA records */ |
1341 | 0 | if (!ParseCaaReply(env, buf, len, ret, true).To(&status)) { |
1342 | 0 | return Nothing<int>(); |
1343 | 0 | } |
1344 | 0 | if (status != ARES_SUCCESS && status != ARES_ENODATA) { |
1345 | 0 | return Just<int>(status); |
1346 | 0 | } |
1347 | | |
1348 | 0 | wrap->CallOnComplete(ret); |
1349 | 0 | return Just<int>(ARES_SUCCESS); |
1350 | 0 | } |
1351 | | |
1352 | | Maybe<int> ATraits::Parse(QueryAWrap* wrap, |
1353 | 0 | const std::unique_ptr<ResponseData>& response) { |
1354 | 0 | if (response->is_host) [[unlikely]] { |
1355 | 0 | return Just<int>(ARES_EBADRESP); |
1356 | 0 | } |
1357 | | |
1358 | 0 | unsigned char* buf = response->buf.data; |
1359 | 0 | int len = response->buf.size; |
1360 | |
|
1361 | 0 | Environment* env = wrap->env(); |
1362 | 0 | HandleScope handle_scope(env->isolate()); |
1363 | 0 | Context::Scope context_scope(env->context()); |
1364 | |
|
1365 | 0 | ares_addrttl addrttls[256]; |
1366 | 0 | int naddrttls = arraysize(addrttls), status; |
1367 | 0 | Local<Array> ret = Array::New(env->isolate()); |
1368 | |
|
1369 | 0 | int type = ns_t_a; |
1370 | 0 | if (!ParseGeneralReply(env, buf, len, &type, ret, addrttls, &naddrttls) |
1371 | 0 | .To(&status)) { |
1372 | 0 | return Nothing<int>(); |
1373 | 0 | } |
1374 | 0 | if (status != ARES_SUCCESS) { |
1375 | 0 | return Just<int>(status); |
1376 | 0 | } |
1377 | | |
1378 | 0 | Local<Array> ttls = AddrTTLToArray<ares_addrttl>(env, addrttls, naddrttls); |
1379 | |
|
1380 | 0 | wrap->CallOnComplete(ret, ttls); |
1381 | 0 | return Just<int>(ARES_SUCCESS); |
1382 | 0 | } |
1383 | | |
1384 | | Maybe<int> AaaaTraits::Parse(QueryAaaaWrap* wrap, |
1385 | 0 | const std::unique_ptr<ResponseData>& response) { |
1386 | 0 | if (response->is_host) [[unlikely]] { |
1387 | 0 | return Just<int>(ARES_EBADRESP); |
1388 | 0 | } |
1389 | | |
1390 | 0 | unsigned char* buf = response->buf.data; |
1391 | 0 | int len = response->buf.size; |
1392 | |
|
1393 | 0 | Environment* env = wrap->env(); |
1394 | 0 | HandleScope handle_scope(env->isolate()); |
1395 | 0 | Context::Scope context_scope(env->context()); |
1396 | |
|
1397 | 0 | ares_addr6ttl addrttls[256]; |
1398 | 0 | int naddrttls = arraysize(addrttls), status; |
1399 | 0 | Local<Array> ret = Array::New(env->isolate()); |
1400 | |
|
1401 | 0 | int type = ns_t_aaaa; |
1402 | 0 | if (!ParseGeneralReply(env, buf, len, &type, ret, addrttls, &naddrttls) |
1403 | 0 | .To(&status)) { |
1404 | 0 | return Nothing<int>(); |
1405 | 0 | } |
1406 | 0 | if (status != ARES_SUCCESS) { |
1407 | 0 | return Just<int>(status); |
1408 | 0 | } |
1409 | | |
1410 | 0 | Local<Array> ttls = AddrTTLToArray<ares_addr6ttl>(env, addrttls, naddrttls); |
1411 | |
|
1412 | 0 | wrap->CallOnComplete(ret, ttls); |
1413 | 0 | return Just<int>(ARES_SUCCESS); |
1414 | 0 | } |
1415 | | |
1416 | | Maybe<int> CaaTraits::Parse(QueryCaaWrap* wrap, |
1417 | 0 | const std::unique_ptr<ResponseData>& response) { |
1418 | 0 | if (response->is_host) [[unlikely]] { |
1419 | 0 | return Just<int>(ARES_EBADRESP); |
1420 | 0 | } |
1421 | | |
1422 | 0 | unsigned char* buf = response->buf.data; |
1423 | 0 | int len = response->buf.size; |
1424 | |
|
1425 | 0 | Environment* env = wrap->env(); |
1426 | 0 | HandleScope handle_scope(env->isolate()); |
1427 | 0 | Context::Scope context_scope(env->context()); |
1428 | |
|
1429 | 0 | Local<Array> ret = Array::New(env->isolate()); |
1430 | 0 | int status; |
1431 | 0 | if (!ParseCaaReply(env, buf, len, ret).To(&status)) { |
1432 | 0 | return Nothing<int>(); |
1433 | 0 | } |
1434 | 0 | if (status != ARES_SUCCESS) { |
1435 | 0 | return Just<int>(status); |
1436 | 0 | } |
1437 | | |
1438 | 0 | wrap->CallOnComplete(ret); |
1439 | 0 | return Just<int>(ARES_SUCCESS); |
1440 | 0 | } |
1441 | | |
1442 | | Maybe<int> CnameTraits::Parse(QueryCnameWrap* wrap, |
1443 | 0 | const std::unique_ptr<ResponseData>& response) { |
1444 | 0 | if (response->is_host) [[unlikely]] { |
1445 | 0 | return Just<int>(ARES_EBADRESP); |
1446 | 0 | } |
1447 | | |
1448 | 0 | unsigned char* buf = response->buf.data; |
1449 | 0 | int len = response->buf.size; |
1450 | |
|
1451 | 0 | Environment* env = wrap->env(); |
1452 | 0 | HandleScope handle_scope(env->isolate()); |
1453 | 0 | Context::Scope context_scope(env->context()); |
1454 | |
|
1455 | 0 | Local<Array> ret = Array::New(env->isolate()); |
1456 | 0 | int type = ns_t_cname; |
1457 | 0 | int status; |
1458 | 0 | if (!ParseGeneralReply(env, buf, len, &type, ret).To(&status)) { |
1459 | 0 | return Nothing<int>(); |
1460 | 0 | } |
1461 | 0 | if (status != ARES_SUCCESS) { |
1462 | 0 | return Just<int>(status); |
1463 | 0 | } |
1464 | | |
1465 | 0 | wrap->CallOnComplete(ret); |
1466 | 0 | return Just<int>(ARES_SUCCESS); |
1467 | 0 | } |
1468 | | |
1469 | | Maybe<int> MxTraits::Parse(QueryMxWrap* wrap, |
1470 | 0 | const std::unique_ptr<ResponseData>& response) { |
1471 | 0 | if (response->is_host) [[unlikely]] { |
1472 | 0 | return Just<int>(ARES_EBADRESP); |
1473 | 0 | } |
1474 | | |
1475 | 0 | unsigned char* buf = response->buf.data; |
1476 | 0 | int len = response->buf.size; |
1477 | |
|
1478 | 0 | Environment* env = wrap->env(); |
1479 | 0 | HandleScope handle_scope(env->isolate()); |
1480 | 0 | Context::Scope context_scope(env->context()); |
1481 | |
|
1482 | 0 | Local<Array> mx_records = Array::New(env->isolate()); |
1483 | 0 | int status; |
1484 | 0 | if (!ParseMxReply(env, buf, len, mx_records).To(&status)) { |
1485 | 0 | return Nothing<int>(); |
1486 | 0 | } |
1487 | | |
1488 | 0 | if (status != ARES_SUCCESS) { |
1489 | 0 | return Just<int>(status); |
1490 | 0 | } |
1491 | | |
1492 | 0 | wrap->CallOnComplete(mx_records); |
1493 | 0 | return Just<int>(ARES_SUCCESS); |
1494 | 0 | } |
1495 | | |
1496 | | Maybe<int> NsTraits::Parse(QueryNsWrap* wrap, |
1497 | 0 | const std::unique_ptr<ResponseData>& response) { |
1498 | 0 | if (response->is_host) [[unlikely]] { |
1499 | 0 | return Just<int>(ARES_EBADRESP); |
1500 | 0 | } |
1501 | | |
1502 | 0 | unsigned char* buf = response->buf.data; |
1503 | 0 | int len = response->buf.size; |
1504 | |
|
1505 | 0 | Environment* env = wrap->env(); |
1506 | 0 | HandleScope handle_scope(env->isolate()); |
1507 | 0 | Context::Scope context_scope(env->context()); |
1508 | |
|
1509 | 0 | int type = ns_t_ns; |
1510 | 0 | Local<Array> names = Array::New(env->isolate()); |
1511 | 0 | int status; |
1512 | 0 | if (!ParseGeneralReply(env, buf, len, &type, names).To(&status)) { |
1513 | 0 | return Nothing<int>(); |
1514 | 0 | } |
1515 | 0 | if (status != ARES_SUCCESS) { |
1516 | 0 | return Just<int>(status); |
1517 | 0 | } |
1518 | | |
1519 | 0 | wrap->CallOnComplete(names); |
1520 | 0 | return Just<int>(ARES_SUCCESS); |
1521 | 0 | } |
1522 | | |
1523 | | Maybe<int> TlsaTraits::Parse(QueryTlsaWrap* wrap, |
1524 | 0 | const std::unique_ptr<ResponseData>& response) { |
1525 | 0 | if (response->is_host) [[unlikely]] { |
1526 | 0 | return Just<int>(ARES_EBADRESP); |
1527 | 0 | } |
1528 | | |
1529 | 0 | unsigned char* buf = response->buf.data; |
1530 | 0 | int len = response->buf.size; |
1531 | |
|
1532 | 0 | Environment* env = wrap->env(); |
1533 | 0 | HandleScope handle_scope(env->isolate()); |
1534 | 0 | Context::Scope context_scope(env->context()); |
1535 | |
|
1536 | 0 | Local<Array> tlsa_records = Array::New(env->isolate()); |
1537 | 0 | int status; |
1538 | 0 | if (!ParseTlsaReply(env, buf, len, tlsa_records).To(&status)) { |
1539 | 0 | return Nothing<int>(); |
1540 | 0 | } |
1541 | 0 | if (status != ARES_SUCCESS) { |
1542 | 0 | return Just<int>(status); |
1543 | 0 | } |
1544 | | |
1545 | 0 | wrap->CallOnComplete(tlsa_records); |
1546 | 0 | return Just<int>(ARES_SUCCESS); |
1547 | 0 | } |
1548 | | |
1549 | | Maybe<int> TxtTraits::Parse(QueryTxtWrap* wrap, |
1550 | 0 | const std::unique_ptr<ResponseData>& response) { |
1551 | 0 | if (response->is_host) [[unlikely]] { |
1552 | 0 | return Just<int>(ARES_EBADRESP); |
1553 | 0 | } |
1554 | | |
1555 | 0 | unsigned char* buf = response->buf.data; |
1556 | 0 | int len = response->buf.size; |
1557 | |
|
1558 | 0 | Environment* env = wrap->env(); |
1559 | 0 | HandleScope handle_scope(env->isolate()); |
1560 | 0 | Context::Scope context_scope(env->context()); |
1561 | |
|
1562 | 0 | Local<Array> txt_records = Array::New(env->isolate()); |
1563 | 0 | int status; |
1564 | 0 | if (!ParseTxtReply(env, buf, len, txt_records).To(&status)) { |
1565 | 0 | return Nothing<int>(); |
1566 | 0 | } |
1567 | 0 | if (status != ARES_SUCCESS) { |
1568 | 0 | return Just<int>(status); |
1569 | 0 | } |
1570 | | |
1571 | 0 | wrap->CallOnComplete(txt_records); |
1572 | 0 | return Just<int>(ARES_SUCCESS); |
1573 | 0 | } |
1574 | | |
1575 | | Maybe<int> SrvTraits::Parse(QuerySrvWrap* wrap, |
1576 | 0 | const std::unique_ptr<ResponseData>& response) { |
1577 | 0 | if (response->is_host) [[unlikely]] { |
1578 | 0 | return Just<int>(ARES_EBADRESP); |
1579 | 0 | } |
1580 | | |
1581 | 0 | unsigned char* buf = response->buf.data; |
1582 | 0 | int len = response->buf.size; |
1583 | |
|
1584 | 0 | Environment* env = wrap->env(); |
1585 | 0 | HandleScope handle_scope(env->isolate()); |
1586 | 0 | Context::Scope context_scope(env->context()); |
1587 | |
|
1588 | 0 | Local<Array> srv_records = Array::New(env->isolate()); |
1589 | 0 | int status; |
1590 | 0 | if (!ParseSrvReply(env, buf, len, srv_records).To(&status)) { |
1591 | 0 | return Nothing<int>(); |
1592 | 0 | } |
1593 | 0 | if (status != ARES_SUCCESS) return Just<int>(status); |
1594 | | |
1595 | 0 | wrap->CallOnComplete(srv_records); |
1596 | 0 | return Just<int>(ARES_SUCCESS); |
1597 | 0 | } |
1598 | | |
1599 | | Maybe<int> PtrTraits::Parse(QueryPtrWrap* wrap, |
1600 | 0 | const std::unique_ptr<ResponseData>& response) { |
1601 | 0 | if (response->is_host) [[unlikely]] { |
1602 | 0 | return Just<int>(ARES_EBADRESP); |
1603 | 0 | } |
1604 | 0 | unsigned char* buf = response->buf.data; |
1605 | 0 | int len = response->buf.size; |
1606 | |
|
1607 | 0 | Environment* env = wrap->env(); |
1608 | 0 | HandleScope handle_scope(env->isolate()); |
1609 | 0 | Context::Scope context_scope(env->context()); |
1610 | |
|
1611 | 0 | int type = ns_t_ptr; |
1612 | 0 | Local<Array> aliases = Array::New(env->isolate()); |
1613 | |
|
1614 | 0 | int status; |
1615 | 0 | if (!ParseGeneralReply(env, buf, len, &type, aliases).To(&status)) { |
1616 | 0 | return Nothing<int>(); |
1617 | 0 | } |
1618 | 0 | if (status != ARES_SUCCESS) { |
1619 | 0 | return Just<int>(status); |
1620 | 0 | } |
1621 | | |
1622 | 0 | wrap->CallOnComplete(aliases); |
1623 | 0 | return Just<int>(ARES_SUCCESS); |
1624 | 0 | } |
1625 | | |
1626 | | Maybe<int> NaptrTraits::Parse(QueryNaptrWrap* wrap, |
1627 | 0 | const std::unique_ptr<ResponseData>& response) { |
1628 | 0 | if (response->is_host) [[unlikely]] { |
1629 | 0 | return Just<int>(ARES_EBADRESP); |
1630 | 0 | } |
1631 | 0 | unsigned char* buf = response->buf.data; |
1632 | 0 | int len = response->buf.size; |
1633 | |
|
1634 | 0 | Environment* env = wrap->env(); |
1635 | 0 | HandleScope handle_scope(env->isolate()); |
1636 | 0 | Context::Scope context_scope(env->context()); |
1637 | |
|
1638 | 0 | Local<Array> naptr_records = Array::New(env->isolate()); |
1639 | 0 | int status; |
1640 | 0 | if (!ParseNaptrReply(env, buf, len, naptr_records).To(&status)) { |
1641 | 0 | return Nothing<int>(); |
1642 | 0 | } |
1643 | 0 | if (status != ARES_SUCCESS) { |
1644 | 0 | return Just<int>(status); |
1645 | 0 | } |
1646 | | |
1647 | 0 | wrap->CallOnComplete(naptr_records); |
1648 | 0 | return Just<int>(ARES_SUCCESS); |
1649 | 0 | } |
1650 | | |
1651 | | Maybe<int> SoaTraits::Parse(QuerySoaWrap* wrap, |
1652 | 0 | const std::unique_ptr<ResponseData>& response) { |
1653 | 0 | if (response->is_host) [[unlikely]] { |
1654 | 0 | return Just<int>(ARES_EBADRESP); |
1655 | 0 | } |
1656 | 0 | unsigned char* buf = response->buf.data; |
1657 | 0 | int len = response->buf.size; |
1658 | |
|
1659 | 0 | Environment* env = wrap->env(); |
1660 | 0 | HandleScope handle_scope(env->isolate()); |
1661 | 0 | Context::Scope context_scope(env->context()); |
1662 | |
|
1663 | 0 | auto tmpl = getSoaRecordTemplate(env); |
1664 | 0 | MaybeLocal<Value> values[] = { |
1665 | 0 | Undefined(env->isolate()), // nsname |
1666 | 0 | Undefined(env->isolate()), // hostmaster |
1667 | 0 | Undefined(env->isolate()), // serial |
1668 | 0 | Undefined(env->isolate()), // refresh |
1669 | 0 | Undefined(env->isolate()), // retry |
1670 | 0 | Undefined(env->isolate()), // expire |
1671 | 0 | Undefined(env->isolate()), // minttl |
1672 | 0 | Undefined(env->isolate()), // type |
1673 | 0 | }; |
1674 | |
|
1675 | 0 | ares_soa_reply* soa_out; |
1676 | 0 | int status = ares_parse_soa_reply(buf, len, &soa_out); |
1677 | |
|
1678 | 0 | if (status != ARES_SUCCESS) return Just<int>(status); |
1679 | | |
1680 | 0 | auto cleanup = OnScopeLeave([&]() { ares_free_data(soa_out); }); |
1681 | |
|
1682 | 0 | values[0] = OneByteString(env->isolate(), soa_out->nsname); |
1683 | 0 | values[1] = OneByteString(env->isolate(), soa_out->hostmaster); |
1684 | 0 | values[2] = Integer::NewFromUnsigned(env->isolate(), soa_out->serial); |
1685 | 0 | values[3] = Integer::New(env->isolate(), soa_out->refresh); |
1686 | 0 | values[4] = Integer::New(env->isolate(), soa_out->retry); |
1687 | 0 | values[5] = Integer::New(env->isolate(), soa_out->expire); |
1688 | 0 | values[6] = Integer::NewFromUnsigned(env->isolate(), soa_out->minttl); |
1689 | 0 | Local<Object> soa_record; |
1690 | 0 | if (!NewDictionaryInstance(env->context(), tmpl, values) |
1691 | 0 | .ToLocal(&soa_record)) { |
1692 | 0 | return Nothing<int>(); |
1693 | 0 | } |
1694 | | |
1695 | 0 | wrap->CallOnComplete(soa_record); |
1696 | 0 | return Just<int>(ARES_SUCCESS); |
1697 | 0 | } |
1698 | | |
1699 | 0 | int ReverseTraits::Send(QueryReverseWrap* wrap, const char* name) { |
1700 | 0 | permission::PermissionScope scope = permission::PermissionScope::kNet; |
1701 | 0 | Environment* env_holder = wrap->env(); |
1702 | |
|
1703 | 0 | if (!env_holder->permission()->is_granted(env_holder, scope, name)) |
1704 | 0 | [[unlikely]] { |
1705 | 0 | wrap->QueuePermissionModelResponseCallback(name); |
1706 | | // Error will be returned in the callback |
1707 | 0 | return ARES_SUCCESS; |
1708 | 0 | } |
1709 | | |
1710 | 0 | int length, family; |
1711 | 0 | char address_buffer[sizeof(struct in6_addr)]; |
1712 | |
|
1713 | 0 | if (uv_inet_pton(AF_INET, name, &address_buffer) == 0) { |
1714 | 0 | length = sizeof(struct in_addr); |
1715 | 0 | family = AF_INET; |
1716 | 0 | } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 0) { |
1717 | 0 | length = sizeof(struct in6_addr); |
1718 | 0 | family = AF_INET6; |
1719 | 0 | } else { |
1720 | 0 | return UV_EINVAL; // So errnoException() reports a proper error. |
1721 | 0 | } |
1722 | | |
1723 | 0 | TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( |
1724 | 0 | TRACING_CATEGORY_NODE2(dns, native), "reverse", wrap, |
1725 | 0 | "name", TRACE_STR_COPY(name), |
1726 | 0 | "family", family == AF_INET ? "ipv4" : "ipv6"); |
1727 | |
|
1728 | 0 | ares_gethostbyaddr(wrap->channel()->cares_channel(), |
1729 | 0 | address_buffer, |
1730 | 0 | length, |
1731 | 0 | family, |
1732 | 0 | QueryReverseWrap::Callback, |
1733 | 0 | wrap->MakeCallbackPointer()); |
1734 | 0 | return ARES_SUCCESS; |
1735 | 0 | } |
1736 | | |
1737 | | Maybe<int> ReverseTraits::Parse(QueryReverseWrap* wrap, |
1738 | 0 | const std::unique_ptr<ResponseData>& response) { |
1739 | 0 | if (!response->is_host) [[unlikely]] { |
1740 | 0 | return Just<int>(ARES_EBADRESP); |
1741 | 0 | } |
1742 | 0 | struct hostent* host = response->host.get(); |
1743 | |
|
1744 | 0 | Environment* env = wrap->env(); |
1745 | 0 | HandleScope handle_scope(env->isolate()); |
1746 | 0 | Context::Scope context_scope(env->context()); |
1747 | 0 | Local<Array> names; |
1748 | 0 | if (!HostentToNames(env, host).ToLocal(&names)) { |
1749 | 0 | return Nothing<int>(); |
1750 | 0 | } |
1751 | 0 | wrap->CallOnComplete(names); |
1752 | 0 | return Just<int>(ARES_SUCCESS); |
1753 | 0 | } |
1754 | | |
1755 | | namespace { |
1756 | | template <class Wrap> |
1757 | 0 | static void Query(const FunctionCallbackInfo<Value>& args) { |
1758 | 0 | ChannelWrap* channel; |
1759 | 0 | ASSIGN_OR_RETURN_UNWRAP(&channel, args.This()); |
1760 | | |
1761 | 0 | CHECK_EQ(false, args.IsConstructCall()); |
1762 | 0 | CHECK(args[0]->IsObject()); |
1763 | 0 | CHECK(args[1]->IsString()); |
1764 | | |
1765 | 0 | Local<Object> req_wrap_obj = args[0].As<Object>(); |
1766 | 0 | Local<String> string = args[1].As<String>(); |
1767 | 0 | auto wrap = std::make_unique<Wrap>(channel, req_wrap_obj); |
1768 | |
|
1769 | 0 | node::Utf8Value utf8name(args.GetIsolate(), string); |
1770 | 0 | auto plain_name = utf8name.ToStringView(); |
1771 | 0 | std::string name = ada::idna::to_ascii(plain_name); |
1772 | 0 | channel->ModifyActivityQueryCount(1); |
1773 | 0 | int err = wrap->Send(name.c_str()); |
1774 | 0 | if (err) { |
1775 | 0 | channel->ModifyActivityQueryCount(-1); |
1776 | 0 | } else { |
1777 | | // Release ownership of the pointer allowing the ownership to be transferred |
1778 | 0 | USE(wrap.release()); |
1779 | 0 | } |
1780 | |
|
1781 | 0 | args.GetReturnValue().Set(err); |
1782 | 0 | } Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::ReverseTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::ATraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::AnyTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::AaaaTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::CaaTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::CnameTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::MxTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::NaptrTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::NsTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::PtrTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::SrvTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::SoaTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::TlsaTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) Unexecuted instantiation: cares_wrap.cc:void node::cares_wrap::(anonymous namespace)::Query<node::cares_wrap::QueryWrap<node::cares_wrap::TxtTraits> >(v8::FunctionCallbackInfo<v8::Value> const&) |
1783 | | |
1784 | 0 | void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { |
1785 | 0 | auto cleanup = OnScopeLeave([&]() { uv_freeaddrinfo(res); }); |
1786 | 0 | BaseObjectPtr<GetAddrInfoReqWrap> req_wrap{ |
1787 | 0 | static_cast<GetAddrInfoReqWrap*>(req->data)}; |
1788 | 0 | Environment* env = req_wrap->env(); |
1789 | |
|
1790 | 0 | HandleScope handle_scope(env->isolate()); |
1791 | 0 | Context::Scope context_scope(env->context()); |
1792 | |
|
1793 | 0 | Local<Value> argv[] = { |
1794 | 0 | Integer::New(env->isolate(), status), |
1795 | 0 | Null(env->isolate()) |
1796 | 0 | }; |
1797 | |
|
1798 | 0 | uint32_t n = 0; |
1799 | 0 | const uint8_t order = req_wrap->order(); |
1800 | |
|
1801 | 0 | if (status == 0) { |
1802 | 0 | Local<Array> results = Array::New(env->isolate()); |
1803 | |
|
1804 | 0 | auto add = [&](bool want_ipv4, bool want_ipv6) -> Maybe<void> { |
1805 | 0 | for (auto p = res; p != nullptr; p = p->ai_next) { |
1806 | 0 | CHECK_EQ(p->ai_socktype, SOCK_STREAM); |
1807 | | |
1808 | 0 | const char* addr; |
1809 | 0 | if (want_ipv4 && p->ai_family == AF_INET) { |
1810 | 0 | addr = reinterpret_cast<char*>( |
1811 | 0 | &(reinterpret_cast<struct sockaddr_in*>(p->ai_addr)->sin_addr)); |
1812 | 0 | } else if (want_ipv6 && p->ai_family == AF_INET6) { |
1813 | 0 | addr = reinterpret_cast<char*>( |
1814 | 0 | &(reinterpret_cast<struct sockaddr_in6*>(p->ai_addr)->sin6_addr)); |
1815 | 0 | } else { |
1816 | 0 | continue; |
1817 | 0 | } |
1818 | | |
1819 | 0 | char ip[INET6_ADDRSTRLEN]; |
1820 | 0 | if (uv_inet_ntop(p->ai_family, addr, ip, sizeof(ip))) |
1821 | 0 | continue; |
1822 | | |
1823 | 0 | Local<String> s = OneByteString(env->isolate(), ip); |
1824 | 0 | if (results->Set(env->context(), n, s).IsNothing()) |
1825 | 0 | return Nothing<void>(); |
1826 | 0 | n++; |
1827 | 0 | } |
1828 | 0 | return JustVoid(); |
1829 | 0 | }; |
1830 | |
|
1831 | 0 | switch (order) { |
1832 | 0 | case DNS_ORDER_IPV4_FIRST: |
1833 | 0 | if (add(true, false).IsNothing() || add(false, true).IsNothing()) |
1834 | 0 | return; |
1835 | | |
1836 | 0 | break; |
1837 | 0 | case DNS_ORDER_IPV6_FIRST: |
1838 | 0 | if (add(false, true).IsNothing() || add(true, false).IsNothing()) |
1839 | 0 | return; |
1840 | | |
1841 | 0 | break; |
1842 | 0 | default: |
1843 | 0 | if (add(true, true).IsNothing()) return; |
1844 | | |
1845 | 0 | break; |
1846 | 0 | } |
1847 | | |
1848 | | // No responses were found to return |
1849 | 0 | if (n == 0) { |
1850 | 0 | argv[0] = Integer::New(env->isolate(), UV_EAI_NODATA); |
1851 | 0 | } |
1852 | |
|
1853 | 0 | argv[1] = results; |
1854 | 0 | } |
1855 | | |
1856 | 0 | TRACE_EVENT_NESTABLE_ASYNC_END2(TRACING_CATEGORY_NODE2(dns, native), |
1857 | 0 | "lookup", |
1858 | 0 | req_wrap.get(), |
1859 | 0 | "count", |
1860 | 0 | n, |
1861 | 0 | "order", |
1862 | 0 | order); |
1863 | | |
1864 | | // Make the callback into JavaScript |
1865 | 0 | req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); |
1866 | 0 | } |
1867 | | |
1868 | | |
1869 | | void AfterGetNameInfo(uv_getnameinfo_t* req, |
1870 | | int status, |
1871 | | const char* hostname, |
1872 | 0 | const char* service) { |
1873 | 0 | BaseObjectPtr<GetNameInfoReqWrap> req_wrap{ |
1874 | 0 | static_cast<GetNameInfoReqWrap*>(req->data)}; |
1875 | 0 | Environment* env = req_wrap->env(); |
1876 | |
|
1877 | 0 | HandleScope handle_scope(env->isolate()); |
1878 | 0 | Context::Scope context_scope(env->context()); |
1879 | |
|
1880 | 0 | Local<Value> argv[] = { |
1881 | 0 | Integer::New(env->isolate(), status), |
1882 | 0 | Null(env->isolate()), |
1883 | 0 | Null(env->isolate()) |
1884 | 0 | }; |
1885 | |
|
1886 | 0 | if (status == 0) { |
1887 | | // Success |
1888 | 0 | Local<String> js_hostname = OneByteString(env->isolate(), hostname); |
1889 | 0 | Local<String> js_service = OneByteString(env->isolate(), service); |
1890 | 0 | argv[1] = js_hostname; |
1891 | 0 | argv[2] = js_service; |
1892 | 0 | } |
1893 | |
|
1894 | 0 | TRACE_EVENT_NESTABLE_ASYNC_END2( |
1895 | 0 | TRACING_CATEGORY_NODE2(dns, native), "lookupService", req_wrap.get(), |
1896 | 0 | "hostname", TRACE_STR_COPY(hostname), |
1897 | 0 | "service", TRACE_STR_COPY(service)); |
1898 | | |
1899 | | // Make the callback into JavaScript |
1900 | 0 | req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv); |
1901 | 0 | } |
1902 | | |
1903 | 0 | void CanonicalizeIP(const FunctionCallbackInfo<Value>& args) { |
1904 | 0 | Isolate* isolate = args.GetIsolate(); |
1905 | 0 | node::Utf8Value ip(isolate, args[0]); |
1906 | |
|
1907 | 0 | int af; |
1908 | 0 | unsigned char result[sizeof(ares_addr_port_node::addr)]; |
1909 | 0 | if (uv_inet_pton(af = AF_INET, *ip, result) != 0 && |
1910 | 0 | uv_inet_pton(af = AF_INET6, *ip, result) != 0) |
1911 | 0 | return; |
1912 | | |
1913 | 0 | char canonical_ip[INET6_ADDRSTRLEN]{}; |
1914 | 0 | CHECK_EQ(0, uv_inet_ntop(af, result, canonical_ip, sizeof(canonical_ip))); |
1915 | 0 | args.GetReturnValue().Set(OneByteString(isolate, canonical_ip)); |
1916 | 0 | } |
1917 | | |
1918 | 0 | void ConvertIpv6StringToBuffer(const FunctionCallbackInfo<Value>& args) { |
1919 | 0 | Isolate* isolate = args.GetIsolate(); |
1920 | 0 | node::Utf8Value ip(isolate, args[0]); |
1921 | 0 | unsigned char dst[16]; // IPv6 addresses are 128 bits (16 bytes) |
1922 | |
|
1923 | 0 | if (uv_inet_pton(AF_INET6, *ip, dst) != 0) { |
1924 | 0 | isolate->ThrowException(Exception::Error( |
1925 | 0 | FIXED_ONE_BYTE_STRING(isolate, "Invalid IPv6 address"))); |
1926 | 0 | return; |
1927 | 0 | } |
1928 | | |
1929 | 0 | Local<Object> buffer; |
1930 | 0 | if (node::Buffer::Copy( |
1931 | 0 | isolate, reinterpret_cast<const char*>(dst), sizeof(dst)) |
1932 | 0 | .ToLocal(&buffer)) { |
1933 | 0 | args.GetReturnValue().Set(buffer); |
1934 | 0 | } |
1935 | 0 | } |
1936 | | |
1937 | 0 | void GetAddrInfo(const FunctionCallbackInfo<Value>& args) { |
1938 | 0 | Environment* env = Environment::GetCurrent(args); |
1939 | |
|
1940 | 0 | CHECK(args[0]->IsObject()); |
1941 | 0 | CHECK(args[1]->IsString()); |
1942 | 0 | CHECK(args[2]->IsInt32()); |
1943 | 0 | CHECK(args[4]->IsUint32()); |
1944 | 0 | Local<Object> req_wrap_obj = args[0].As<Object>(); |
1945 | 0 | node::Utf8Value hostname(env->isolate(), args[1]); |
1946 | |
|
1947 | 0 | ERR_ACCESS_DENIED_IF_INSUFFICIENT_PERMISSIONS( |
1948 | 0 | env, permission::PermissionScope::kNet, hostname.ToStringView(), args); |
1949 | | |
1950 | 0 | std::string ascii_hostname = ada::idna::to_ascii(hostname.ToStringView()); |
1951 | |
|
1952 | 0 | int32_t flags = 0; |
1953 | 0 | if (args[3]->IsInt32()) { |
1954 | 0 | flags = args[3].As<Int32>()->Value(); |
1955 | 0 | } |
1956 | |
|
1957 | 0 | int family; |
1958 | |
|
1959 | 0 | switch (args[2].As<Int32>()->Value()) { |
1960 | 0 | case 0: |
1961 | 0 | family = AF_UNSPEC; |
1962 | 0 | break; |
1963 | 0 | case 4: |
1964 | 0 | family = AF_INET; |
1965 | 0 | break; |
1966 | 0 | case 6: |
1967 | 0 | family = AF_INET6; |
1968 | 0 | break; |
1969 | 0 | default: |
1970 | 0 | UNREACHABLE("bad address family"); |
1971 | 0 | } |
1972 | | |
1973 | 0 | Local<Uint32> order = args[4].As<Uint32>(); |
1974 | |
|
1975 | 0 | auto req_wrap = |
1976 | 0 | std::make_unique<GetAddrInfoReqWrap>(env, req_wrap_obj, order->Value()); |
1977 | |
|
1978 | 0 | struct addrinfo hints; |
1979 | 0 | memset(&hints, 0, sizeof(hints)); |
1980 | 0 | hints.ai_family = family; |
1981 | 0 | hints.ai_socktype = SOCK_STREAM; |
1982 | 0 | hints.ai_flags = flags; |
1983 | |
|
1984 | 0 | TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(TRACING_CATEGORY_NODE2(dns, native), |
1985 | 0 | "lookup", |
1986 | 0 | req_wrap.get(), |
1987 | 0 | "hostname", |
1988 | 0 | TRACE_STR_COPY(ascii_hostname.data()), |
1989 | 0 | "family", |
1990 | 0 | family == AF_INET ? "ipv4" |
1991 | 0 | : family == AF_INET6 ? "ipv6" |
1992 | 0 | : "unspec"); |
1993 | |
|
1994 | 0 | int err = req_wrap->Dispatch( |
1995 | 0 | uv_getaddrinfo, AfterGetAddrInfo, ascii_hostname.data(), nullptr, &hints); |
1996 | 0 | if (err == 0) |
1997 | | // Release ownership of the pointer allowing the ownership to be transferred |
1998 | 0 | USE(req_wrap.release()); |
1999 | |
|
2000 | 0 | args.GetReturnValue().Set(err); |
2001 | 0 | } |
2002 | | |
2003 | | |
2004 | 0 | void GetNameInfo(const FunctionCallbackInfo<Value>& args) { |
2005 | 0 | Environment* env = Environment::GetCurrent(args); |
2006 | |
|
2007 | 0 | CHECK(args[0]->IsObject()); |
2008 | 0 | CHECK(args[1]->IsString()); |
2009 | 0 | CHECK(args[2]->IsUint32()); |
2010 | 0 | Local<Object> req_wrap_obj = args[0].As<Object>(); |
2011 | 0 | node::Utf8Value ip(env->isolate(), args[1]); |
2012 | 0 | const unsigned port = args[2]->Uint32Value(env->context()).FromJust(); |
2013 | 0 | struct sockaddr_storage addr; |
2014 | |
|
2015 | 0 | CHECK(uv_ip4_addr(*ip, port, reinterpret_cast<sockaddr_in*>(&addr)) == 0 || |
2016 | 0 | uv_ip6_addr(*ip, port, reinterpret_cast<sockaddr_in6*>(&addr)) == 0); |
2017 | | |
2018 | 0 | auto req_wrap = std::make_unique<GetNameInfoReqWrap>(env, req_wrap_obj); |
2019 | |
|
2020 | 0 | TRACE_EVENT_NESTABLE_ASYNC_BEGIN2( |
2021 | 0 | TRACING_CATEGORY_NODE2(dns, native), "lookupService", req_wrap.get(), |
2022 | 0 | "ip", TRACE_STR_COPY(*ip), "port", port); |
2023 | |
|
2024 | 0 | int err = 0; |
2025 | 0 | if (!env->permission()->is_granted( |
2026 | 0 | env, permission::PermissionScope::kNet, ip.ToStringView())) |
2027 | 0 | [[unlikely]] { |
2028 | 0 | req_wrap->InsufficientPermissionError(*ip); |
2029 | 0 | } else { |
2030 | 0 | err = req_wrap->Dispatch(uv_getnameinfo, |
2031 | 0 | AfterGetNameInfo, |
2032 | 0 | reinterpret_cast<struct sockaddr*>(&addr), |
2033 | 0 | NI_NAMEREQD); |
2034 | 0 | } |
2035 | |
|
2036 | 0 | if (err == 0) |
2037 | | // Release ownership of the pointer allowing the ownership to be transferred |
2038 | 0 | USE(req_wrap.release()); |
2039 | |
|
2040 | 0 | args.GetReturnValue().Set(err); |
2041 | 0 | } |
2042 | | |
2043 | | |
2044 | 0 | void GetServers(const FunctionCallbackInfo<Value>& args) { |
2045 | 0 | Environment* env = Environment::GetCurrent(args); |
2046 | 0 | ChannelWrap* channel; |
2047 | 0 | ASSIGN_OR_RETURN_UNWRAP(&channel, args.This()); |
2048 | | |
2049 | 0 | Local<Array> server_array = Array::New(env->isolate()); |
2050 | |
|
2051 | 0 | ares_addr_port_node* servers; |
2052 | |
|
2053 | 0 | int r = ares_get_servers_ports(channel->cares_channel(), &servers); |
2054 | 0 | CHECK_EQ(r, ARES_SUCCESS); |
2055 | 0 | auto cleanup = OnScopeLeave([&]() { ares_free_data(servers); }); |
2056 | |
|
2057 | 0 | ares_addr_port_node* cur = servers; |
2058 | |
|
2059 | 0 | for (uint32_t i = 0; cur != nullptr; ++i, cur = cur->next) { |
2060 | 0 | char ip[INET6_ADDRSTRLEN]; |
2061 | |
|
2062 | 0 | const void* caddr = static_cast<const void*>(&cur->addr); |
2063 | 0 | int err = uv_inet_ntop(cur->family, caddr, ip, sizeof(ip)); |
2064 | 0 | CHECK_EQ(err, 0); |
2065 | | |
2066 | 0 | Local<Value> ret[] = { |
2067 | 0 | OneByteString(env->isolate(), ip), |
2068 | 0 | Integer::New(env->isolate(), cur->udp_port) |
2069 | 0 | }; |
2070 | |
|
2071 | 0 | if (server_array->Set(env->context(), i, |
2072 | 0 | Array::New(env->isolate(), ret, arraysize(ret))) |
2073 | 0 | .IsNothing()) { |
2074 | 0 | return; |
2075 | 0 | } |
2076 | 0 | } |
2077 | | |
2078 | 0 | args.GetReturnValue().Set(server_array); |
2079 | 0 | } |
2080 | | |
2081 | | |
2082 | 0 | void SetServers(const FunctionCallbackInfo<Value>& args) { |
2083 | 0 | Environment* env = Environment::GetCurrent(args); |
2084 | 0 | ChannelWrap* channel; |
2085 | 0 | ASSIGN_OR_RETURN_UNWRAP(&channel, args.This()); |
2086 | | |
2087 | 0 | if (channel->active_query_count()) { |
2088 | 0 | return args.GetReturnValue().Set(DNS_ESETSRVPENDING); |
2089 | 0 | } |
2090 | | |
2091 | 0 | CHECK(args[0]->IsArray()); |
2092 | | |
2093 | 0 | Local<Array> arr = args[0].As<Array>(); |
2094 | |
|
2095 | 0 | uint32_t len = arr->Length(); |
2096 | |
|
2097 | 0 | if (len == 0) { |
2098 | 0 | int rv = ares_set_servers(channel->cares_channel(), nullptr); |
2099 | 0 | return args.GetReturnValue().Set(rv); |
2100 | 0 | } |
2101 | | |
2102 | 0 | std::vector<ares_addr_port_node> servers(len); |
2103 | 0 | ares_addr_port_node* last = nullptr; |
2104 | |
|
2105 | 0 | int err; |
2106 | |
|
2107 | 0 | for (uint32_t i = 0; i < len; i++) { |
2108 | 0 | Local<Value> val; |
2109 | 0 | if (!arr->Get(env->context(), i).ToLocal(&val)) return; |
2110 | 0 | CHECK(val->IsArray()); |
2111 | | |
2112 | 0 | Local<Array> elm = val.As<Array>(); |
2113 | |
|
2114 | 0 | Local<Value> familyValue; |
2115 | 0 | Local<Value> ipValue; |
2116 | 0 | Local<Value> portValue; |
2117 | |
|
2118 | 0 | if (!elm->Get(env->context(), 0).ToLocal(&familyValue)) return; |
2119 | 0 | if (!elm->Get(env->context(), 1).ToLocal(&ipValue)) return; |
2120 | 0 | if (!elm->Get(env->context(), 2).ToLocal(&portValue)) return; |
2121 | | |
2122 | 0 | CHECK(familyValue->Int32Value(env->context()).FromJust()); |
2123 | 0 | CHECK(ipValue->IsString()); |
2124 | 0 | CHECK(portValue->Int32Value(env->context()).FromJust()); |
2125 | | |
2126 | 0 | int fam = familyValue->Int32Value(env->context()).FromJust(); |
2127 | 0 | node::Utf8Value ip(env->isolate(), ipValue); |
2128 | 0 | int port = portValue->Int32Value(env->context()).FromJust(); |
2129 | |
|
2130 | 0 | ares_addr_port_node* cur = &servers[i]; |
2131 | |
|
2132 | 0 | cur->tcp_port = cur->udp_port = port; |
2133 | 0 | switch (fam) { |
2134 | 0 | case 4: |
2135 | 0 | cur->family = AF_INET; |
2136 | 0 | err = uv_inet_pton(AF_INET, *ip, &cur->addr); |
2137 | 0 | break; |
2138 | 0 | case 6: |
2139 | 0 | cur->family = AF_INET6; |
2140 | 0 | err = uv_inet_pton(AF_INET6, *ip, &cur->addr); |
2141 | 0 | break; |
2142 | 0 | default: |
2143 | 0 | UNREACHABLE("Bad address family"); |
2144 | 0 | } |
2145 | | |
2146 | 0 | if (err) |
2147 | 0 | break; |
2148 | | |
2149 | 0 | cur->next = nullptr; |
2150 | |
|
2151 | 0 | if (last != nullptr) |
2152 | 0 | last->next = cur; |
2153 | |
|
2154 | 0 | last = cur; |
2155 | 0 | } |
2156 | | |
2157 | 0 | if (err == 0) |
2158 | 0 | err = ares_set_servers_ports(channel->cares_channel(), servers.data()); |
2159 | 0 | else |
2160 | 0 | err = ARES_EBADSTR; |
2161 | |
|
2162 | 0 | if (err == ARES_SUCCESS) |
2163 | 0 | channel->set_is_servers_default(false); |
2164 | |
|
2165 | 0 | args.GetReturnValue().Set(err); |
2166 | 0 | } |
2167 | | |
2168 | 0 | void SetLocalAddress(const FunctionCallbackInfo<Value>& args) { |
2169 | 0 | Environment* env = Environment::GetCurrent(args); |
2170 | 0 | ChannelWrap* channel; |
2171 | 0 | ASSIGN_OR_RETURN_UNWRAP(&channel, args.This()); |
2172 | | |
2173 | 0 | CHECK_EQ(args.Length(), 2); |
2174 | 0 | CHECK(args[0]->IsString()); |
2175 | | |
2176 | 0 | Isolate* isolate = args.GetIsolate(); |
2177 | 0 | node::Utf8Value ip0(isolate, args[0]); |
2178 | |
|
2179 | 0 | unsigned char addr0[sizeof(struct in6_addr)]; |
2180 | 0 | unsigned char addr1[sizeof(struct in6_addr)]; |
2181 | 0 | int type0 = 0; |
2182 | | |
2183 | | // This function accepts 2 arguments. The first may be either an IPv4 |
2184 | | // address or an IPv6 address. If present, the second argument must be the |
2185 | | // other type of address. Otherwise, the unspecified type of IP is set |
2186 | | // to 0 (any). |
2187 | |
|
2188 | 0 | if (uv_inet_pton(AF_INET, *ip0, &addr0) == 0) { |
2189 | 0 | ares_set_local_ip4(channel->cares_channel(), nbytes::ReadUint32BE(addr0)); |
2190 | 0 | type0 = 4; |
2191 | 0 | } else if (uv_inet_pton(AF_INET6, *ip0, &addr0) == 0) { |
2192 | 0 | ares_set_local_ip6(channel->cares_channel(), addr0); |
2193 | 0 | type0 = 6; |
2194 | 0 | } else { |
2195 | 0 | THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); |
2196 | 0 | return; |
2197 | 0 | } |
2198 | | |
2199 | 0 | if (!args[1]->IsUndefined()) { |
2200 | 0 | CHECK(args[1]->IsString()); |
2201 | 0 | node::Utf8Value ip1(isolate, args[1]); |
2202 | |
|
2203 | 0 | if (uv_inet_pton(AF_INET, *ip1, &addr1) == 0) { |
2204 | 0 | if (type0 == 4) { |
2205 | 0 | THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv4 addresses."); |
2206 | 0 | return; |
2207 | 0 | } else { |
2208 | 0 | ares_set_local_ip4(channel->cares_channel(), |
2209 | 0 | nbytes::ReadUint32BE(addr1)); |
2210 | 0 | } |
2211 | 0 | } else if (uv_inet_pton(AF_INET6, *ip1, &addr1) == 0) { |
2212 | 0 | if (type0 == 6) { |
2213 | 0 | THROW_ERR_INVALID_ARG_VALUE(env, "Cannot specify two IPv6 addresses."); |
2214 | 0 | return; |
2215 | 0 | } else { |
2216 | 0 | ares_set_local_ip6(channel->cares_channel(), addr1); |
2217 | 0 | } |
2218 | 0 | } else { |
2219 | 0 | THROW_ERR_INVALID_ARG_VALUE(env, "Invalid IP address."); |
2220 | 0 | return; |
2221 | 0 | } |
2222 | 0 | } else { |
2223 | | // No second arg specified |
2224 | 0 | if (type0 == 4) { |
2225 | 0 | memset(&addr1, 0, sizeof(addr1)); |
2226 | 0 | ares_set_local_ip6(channel->cares_channel(), addr1); |
2227 | 0 | } else { |
2228 | 0 | ares_set_local_ip4(channel->cares_channel(), 0); |
2229 | 0 | } |
2230 | 0 | } |
2231 | 0 | } |
2232 | | |
2233 | 0 | void Cancel(const FunctionCallbackInfo<Value>& args) { |
2234 | 0 | ChannelWrap* channel; |
2235 | 0 | ASSIGN_OR_RETURN_UNWRAP(&channel, args.This()); |
2236 | | |
2237 | 0 | TRACE_EVENT_INSTANT0(TRACING_CATEGORY_NODE2(dns, native), |
2238 | 0 | "cancel", TRACE_EVENT_SCOPE_THREAD); |
2239 | |
|
2240 | 0 | ares_cancel(channel->cares_channel()); |
2241 | 0 | } |
2242 | | |
2243 | | const char EMSG_ESETSRVPENDING[] = "There are pending queries."; |
2244 | 0 | void StrError(const FunctionCallbackInfo<Value>& args) { |
2245 | 0 | Environment* env = Environment::GetCurrent(args); |
2246 | 0 | int code = args[0]->Int32Value(env->context()).FromJust(); |
2247 | 0 | const char* errmsg = (code == DNS_ESETSRVPENDING) ? |
2248 | 0 | EMSG_ESETSRVPENDING : |
2249 | 0 | ares_strerror(code); |
2250 | 0 | args.GetReturnValue().Set(OneByteString(env->isolate(), errmsg)); |
2251 | 0 | } |
2252 | | |
2253 | | } // namespace |
2254 | | |
2255 | 0 | inline void safe_free_hostent(struct hostent* host) { |
2256 | 0 | int idx; |
2257 | |
|
2258 | 0 | if (host->h_addr_list != nullptr) { |
2259 | 0 | idx = 0; |
2260 | 0 | while (host->h_addr_list[idx]) { |
2261 | 0 | free(host->h_addr_list[idx++]); |
2262 | 0 | } |
2263 | 0 | free(host->h_addr_list); |
2264 | 0 | host->h_addr_list = nullptr; |
2265 | 0 | } |
2266 | |
|
2267 | 0 | if (host->h_aliases != nullptr) { |
2268 | 0 | idx = 0; |
2269 | 0 | while (host->h_aliases[idx]) { |
2270 | 0 | free(host->h_aliases[idx++]); |
2271 | 0 | } |
2272 | 0 | free(host->h_aliases); |
2273 | 0 | host->h_aliases = nullptr; |
2274 | 0 | } |
2275 | |
|
2276 | 0 | free(host->h_name); |
2277 | 0 | free(host); |
2278 | 0 | } |
2279 | | |
2280 | | void Initialize(Local<Object> target, |
2281 | | Local<Value> unused, |
2282 | | Local<Context> context, |
2283 | 0 | void* priv) { |
2284 | 0 | Environment* env = Environment::GetCurrent(context); |
2285 | 0 | Isolate* isolate = env->isolate(); |
2286 | |
|
2287 | 0 | SetMethod(context, target, "getaddrinfo", GetAddrInfo); |
2288 | 0 | SetMethod(context, target, "getnameinfo", GetNameInfo); |
2289 | 0 | SetMethodNoSideEffect(context, target, "canonicalizeIP", CanonicalizeIP); |
2290 | 0 | SetMethodNoSideEffect( |
2291 | 0 | context, target, "convertIpv6StringToBuffer", ConvertIpv6StringToBuffer); |
2292 | |
|
2293 | 0 | SetMethod(context, target, "strerror", StrError); |
2294 | |
|
2295 | 0 | NODE_DEFINE_CONSTANT(target, AF_INET); |
2296 | 0 | NODE_DEFINE_CONSTANT(target, AF_INET6); |
2297 | 0 | NODE_DEFINE_CONSTANT(target, AF_UNSPEC); |
2298 | 0 | NODE_DEFINE_CONSTANT(target, AI_ADDRCONFIG); |
2299 | 0 | NODE_DEFINE_CONSTANT(target, AI_ALL); |
2300 | 0 | NODE_DEFINE_CONSTANT(target, AI_V4MAPPED); |
2301 | 0 | NODE_DEFINE_CONSTANT(target, DNS_ORDER_VERBATIM); |
2302 | 0 | NODE_DEFINE_CONSTANT(target, DNS_ORDER_IPV4_FIRST); |
2303 | 0 | NODE_DEFINE_CONSTANT(target, DNS_ORDER_IPV6_FIRST); |
2304 | |
|
2305 | 0 | Local<FunctionTemplate> aiw = |
2306 | 0 | BaseObject::MakeLazilyInitializedJSTemplate(env); |
2307 | 0 | aiw->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
2308 | 0 | SetConstructorFunction(context, target, "GetAddrInfoReqWrap", aiw); |
2309 | |
|
2310 | 0 | Local<FunctionTemplate> niw = |
2311 | 0 | BaseObject::MakeLazilyInitializedJSTemplate(env); |
2312 | 0 | niw->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
2313 | 0 | SetConstructorFunction(context, target, "GetNameInfoReqWrap", niw); |
2314 | |
|
2315 | 0 | Local<FunctionTemplate> qrw = |
2316 | 0 | BaseObject::MakeLazilyInitializedJSTemplate(env); |
2317 | 0 | qrw->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
2318 | 0 | SetConstructorFunction(context, target, "QueryReqWrap", qrw); |
2319 | |
|
2320 | 0 | Local<FunctionTemplate> channel_wrap = |
2321 | 0 | NewFunctionTemplate(isolate, ChannelWrap::New); |
2322 | 0 | channel_wrap->InstanceTemplate()->SetInternalFieldCount( |
2323 | 0 | ChannelWrap::kInternalFieldCount); |
2324 | 0 | channel_wrap->Inherit(AsyncWrap::GetConstructorTemplate(env)); |
2325 | |
|
2326 | 0 | #define V(Name, _, JS) \ |
2327 | 0 | SetProtoMethod(isolate, channel_wrap, #JS, Query<Query##Name##Wrap>); |
2328 | 0 | QUERY_TYPES(V) |
2329 | 0 | #undef V |
2330 | |
|
2331 | 0 | SetProtoMethodNoSideEffect(isolate, channel_wrap, "getServers", GetServers); |
2332 | 0 | SetProtoMethod(isolate, channel_wrap, "setServers", SetServers); |
2333 | 0 | SetProtoMethod(isolate, channel_wrap, "setLocalAddress", SetLocalAddress); |
2334 | 0 | SetProtoMethod(isolate, channel_wrap, "cancel", Cancel); |
2335 | |
|
2336 | 0 | SetConstructorFunction(context, target, "ChannelWrap", channel_wrap); |
2337 | 0 | } |
2338 | | |
2339 | 0 | void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
2340 | 0 | registry->Register(GetAddrInfo); |
2341 | 0 | registry->Register(GetNameInfo); |
2342 | 0 | registry->Register(CanonicalizeIP); |
2343 | 0 | registry->Register(ConvertIpv6StringToBuffer); |
2344 | 0 | registry->Register(StrError); |
2345 | 0 | registry->Register(ChannelWrap::New); |
2346 | |
|
2347 | 0 | #define V(Name, _, __) registry->Register(Query<Query##Name##Wrap>); |
2348 | 0 | QUERY_TYPES(V) |
2349 | 0 | #undef V |
2350 | |
|
2351 | 0 | registry->Register(GetServers); |
2352 | 0 | registry->Register(SetServers); |
2353 | 0 | registry->Register(SetLocalAddress); |
2354 | 0 | registry->Register(Cancel); |
2355 | 0 | } |
2356 | | |
2357 | | } // namespace cares_wrap |
2358 | | } // namespace node |
2359 | | |
2360 | | NODE_BINDING_CONTEXT_AWARE_INTERNAL(cares_wrap, node::cares_wrap::Initialize) |
2361 | | NODE_BINDING_EXTERNAL_REFERENCE(cares_wrap, |
2362 | | node::cares_wrap::RegisterExternalReferences) |