Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 596 lines (471 sloc) 15.139 kB
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
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 <assert.h>
23 #include <node.h>
24 #include <uv.h>
25
13d6a1f @DrPizza Basic VC++ compatibility work.
DrPizza authored
26 #if defined(__OpenBSD__) || defined(__MINGW32__) || defined(_MSC_VER)
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
27 # include <nameser.h>
28 #else
29 # include <arpa/nameser.h>
30 #endif
31
32 // Temporary hack: libuv should provide uv_inet_pton and uv_inet_ntop.
13d6a1f @DrPizza Basic VC++ compatibility work.
DrPizza authored
33 #if defined(__MINGW32__) || defined(_MSC_VER)
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
34 extern "C" {
35 # include <inet_net_pton.h>
36 # include <inet_ntop.h>
37 }
38 # define uv_inet_pton ares_inet_pton
39 # define uv_inet_ntop ares_inet_ntop
40
41 #else // __POSIX__
42 # include <arpa/inet.h>
43 # define uv_inet_pton inet_pton
44 # define uv_inet_ntop inet_ntop
45 #endif
46
47
48 namespace node {
49
50 namespace cares_wrap {
51
52 using v8::Arguments;
53 using v8::Array;
54 using v8::Context;
55 using v8::Function;
56 using v8::Handle;
57 using v8::HandleScope;
58 using v8::Integer;
59 using v8::Local;
60 using v8::Null;
61 using v8::Object;
62 using v8::Persistent;
63 using v8::String;
64 using v8::Value;
65
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
66 static Persistent<String> oncomplete_sym;
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
67
68 static ares_channel ares_channel;
69
70
71 static Local<Array> HostentToAddresses(struct hostent* host) {
72 HandleScope scope;
73 Local<Array> addresses = Array::New();
74
75 char ip[INET6_ADDRSTRLEN];
76 for (int i = 0; host->h_addr_list[i]; ++i) {
77 uv_inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip));
78
79 Local<String> address = String::New(ip);
80 addresses->Set(Integer::New(i), address);
81 }
82
83 return scope.Close(addresses);
84 }
85
86
87 static Local<Array> HostentToNames(struct hostent* host) {
88 HandleScope scope;
89 Local<Array> names = Array::New();
90
91 for (int i = 0; host->h_aliases[i]; ++i) {
92 Local<String> address = String::New(host->h_aliases[i]);
93 names->Set(Integer::New(i), address);
94 }
95
96 return scope.Close(names);
97 }
98
99
100 static const char* AresErrnoString(int errorno) {
101 switch (errorno) {
102 #define ERRNO_CASE(e) case ARES_##e: return #e;
103 ERRNO_CASE(SUCCESS)
104 ERRNO_CASE(ENODATA)
105 ERRNO_CASE(EFORMERR)
106 ERRNO_CASE(ESERVFAIL)
107 ERRNO_CASE(ENOTFOUND)
108 ERRNO_CASE(ENOTIMP)
109 ERRNO_CASE(EREFUSED)
110 ERRNO_CASE(EBADQUERY)
111 ERRNO_CASE(EBADNAME)
112 ERRNO_CASE(EBADFAMILY)
113 ERRNO_CASE(EBADRESP)
114 ERRNO_CASE(ECONNREFUSED)
115 ERRNO_CASE(ETIMEOUT)
116 ERRNO_CASE(EOF)
117 ERRNO_CASE(EFILE)
118 ERRNO_CASE(ENOMEM)
119 ERRNO_CASE(EDESTRUCTION)
120 ERRNO_CASE(EBADSTR)
121 ERRNO_CASE(EBADFLAGS)
122 ERRNO_CASE(ENONAME)
123 ERRNO_CASE(EBADHINTS)
124 ERRNO_CASE(ENOTINITIALIZED)
125 ERRNO_CASE(ELOADIPHLPAPI)
126 ERRNO_CASE(EADDRGETNETWORKPARAMS)
127 ERRNO_CASE(ECANCELLED)
128 #undef ERRNO_CASE
129 default:
130 assert(0 && "Unhandled c-ares error");
131 return "(UNKNOWN)";
132 }
133 }
134
135
136 static void SetAresErrno(int errorno) {
137 HandleScope scope;
138 Handle<Value> key = String::NewSymbol("errno");
139 Handle<Value> value = String::NewSymbol(AresErrnoString(errorno));
140 Context::GetCurrent()->Global()->Set(key, value);
141 }
142
143
144 class QueryWrap {
145 public:
146 QueryWrap() {
147 HandleScope scope;
148
149 object_ = Persistent<Object>::New(Object::New());
150 }
151
152 ~QueryWrap() {
153 assert(!object_.IsEmpty());
154
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
155 object_->Delete(oncomplete_sym);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
156
157 object_.Dispose();
158 object_.Clear();
159 }
160
161 Handle<Object> GetObject() {
162 return object_;
163 }
164
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
165 void SetOnComplete(Handle<Value> oncomplete) {
166 assert(oncomplete->IsFunction());
167 object_->Set(oncomplete_sym, oncomplete);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
168 }
169
170 // Subclasses should implement the appropriate Send method.
171 virtual int Send(const char* name) {
172 assert(0);
13d6a1f @DrPizza Basic VC++ compatibility work.
DrPizza authored
173 return 0;
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
174 }
175
176 virtual int Send(const char* name, int family) {
177 assert(0);
13d6a1f @DrPizza Basic VC++ compatibility work.
DrPizza authored
178 return 0;
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
179 }
180
181 protected:
182 void* GetQueryArg() {
183 return static_cast<void*>(this);
184 }
185
186 static void Callback(void *arg, int status, int timeouts,
187 unsigned char* answer_buf, int answer_len) {
188 QueryWrap* wrap = reinterpret_cast<QueryWrap*>(arg);
189
190 if (status != ARES_SUCCESS) {
191 wrap->ParseError(status);
192 } else {
193 wrap->Parse(answer_buf, answer_len);
194 }
195
196 delete wrap;
197 }
198
199 static void Callback(void *arg, int status, int timeouts,
200 struct hostent* host) {
201 QueryWrap* wrap = reinterpret_cast<QueryWrap*>(arg);
202
203 if (status != ARES_SUCCESS) {
204 wrap->ParseError(status);
205 } else {
206 wrap->Parse(host);
207 }
208
209 delete wrap;
210 }
211
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
212 void CallOnComplete(Local<Value> answer) {
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
213 HandleScope scope;
214 Local<Value> argv[2] = { Integer::New(0), answer };
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
215 MakeCallback(object_, "oncomplete", 2, argv);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
216 }
217
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
218 void CallOnComplete(Local<Value> answer, Local<Value> family) {
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
219 HandleScope scope;
220 Local<Value> argv[3] = { Integer::New(0), answer, family };
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
221 MakeCallback(object_, "oncomplete", 3, argv);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
222 }
223
224 void ParseError(int status) {
225 assert(status != ARES_SUCCESS);
226 SetAresErrno(status);
227
228 HandleScope scope;
229 Local<Value> argv[1] = { Integer::New(-1) };
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
230 MakeCallback(object_, "oncomplete", 1, argv);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
231 }
232
233 // Subclasses should implement the appropriate Parse method.
234 virtual void Parse(unsigned char* buf, int len) {
235 assert(0);
236 };
237
238 virtual void Parse(struct hostent* host) {
239 assert(0);
240 };
241
242 private:
243 Persistent<Object> object_;
244 };
245
246
247 class QueryAWrap: public QueryWrap {
248 public:
249 int Send(const char* name) {
250 ares_query(ares_channel, name, ns_c_in, ns_t_a, Callback, GetQueryArg());
251 return 0;
252 }
253
254 protected:
255 void Parse(unsigned char* buf, int len) {
256 HandleScope scope;
257
258 struct hostent* host;
259
260 int status = ares_parse_a_reply(buf, len, &host, NULL, NULL);
261 if (status != ARES_SUCCESS) {
262 this->ParseError(status);
263 return;
264 }
265
266 Local<Array> addresses = HostentToAddresses(host);
267 ares_free_hostent(host);
268
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
269 this->CallOnComplete(addresses);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
270 }
271 };
272
273
274 class QueryAaaaWrap: public QueryWrap {
275 public:
276 int Send(const char* name) {
277 ares_query(ares_channel,
278 name,
279 ns_c_in,
280 ns_t_aaaa,
281 Callback,
282 GetQueryArg());
283 return 0;
284 }
285
286 protected:
287 void Parse(unsigned char* buf, int len) {
288 HandleScope scope;
289
290 struct hostent* host;
291
292 int status = ares_parse_aaaa_reply(buf, len, &host, NULL, NULL);
293 if (status != ARES_SUCCESS) {
294 this->ParseError(status);
295 return;
296 }
297
298 Local<Array> addresses = HostentToAddresses(host);
299 ares_free_hostent(host);
300
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
301 this->CallOnComplete(addresses);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
302 }
303 };
304
305
306 class QueryCnameWrap: public QueryWrap {
307 public:
308 int Send(const char* name) {
309 ares_query(ares_channel,
310 name,
311 ns_c_in,
312 ns_t_cname,
313 Callback,
314 GetQueryArg());
315 return 0;
316 }
317
318 protected:
319 void Parse(unsigned char* buf, int len) {
320 HandleScope scope;
321
322 struct hostent* host;
323
324 int status = ares_parse_a_reply(buf, len, &host, NULL, NULL);
325 if (status != ARES_SUCCESS) {
326 this->ParseError(status);
327 return;
328 }
329
330 // A cname lookup always returns a single record but we follow the
331 // common API here.
332 Local<Array> result = Array::New(1);
333 result->Set(0, String::New(host->h_name));
334 ares_free_hostent(host);
335
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
336 this->CallOnComplete(result);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
337 }
338 };
339
340
341 class QueryMxWrap: public QueryWrap {
342 public:
343 int Send(const char* name) {
344 ares_query(ares_channel, name, ns_c_in, ns_t_mx, Callback, GetQueryArg());
345 return 0;
346 }
347
348 protected:
349 void Parse(unsigned char* buf, int len) {
350 HandleScope scope;
351
352 struct ares_mx_reply* mx_start;
353 int status = ares_parse_mx_reply(buf, len, &mx_start);
354 if (status != ARES_SUCCESS) {
355 this->ParseError(status);
356 return;
357 }
358
359 Local<Array> mx_records = Array::New();
360 Local<String> exchange_symbol = String::NewSymbol("exchange");
361 Local<String> priority_symbol = String::NewSymbol("priority");
362 int i = 0;
363 for (struct ares_mx_reply* mx_current = mx_start;
364 mx_current;
365 mx_current = mx_current->next) {
366 Local<Object> mx_record = Object::New();
367 mx_record->Set(exchange_symbol, String::New(mx_current->host));
368 mx_record->Set(priority_symbol, Integer::New(mx_current->priority));
369 mx_records->Set(Integer::New(i++), mx_record);
370 }
371
372 ares_free_data(mx_start);
373
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
374 this->CallOnComplete(mx_records);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
375 }
376 };
377
378
379 class QueryNsWrap: public QueryWrap {
380 public:
381 int Send(const char* name) {
382 ares_query(ares_channel, name, ns_c_in, ns_t_ns, Callback, GetQueryArg());
383 return 0;
384 }
385
386 protected:
387 void Parse(unsigned char* buf, int len) {
388 struct hostent* host;
389
390 int status = ares_parse_ns_reply(buf, len, &host);
391 if (status != ARES_SUCCESS) {
392 this->ParseError(status);
393 return;
394 }
395
396 Local<Array> names = HostentToNames(host);
397 ares_free_hostent(host);
398
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
399 this->CallOnComplete(names);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
400 }
401 };
402
403
404 class QuerySrvWrap: public QueryWrap {
405 public:
406 int Send(const char* name) {
407 ares_query(ares_channel,
408 name,
409 ns_c_in,
410 ns_t_srv,
411 Callback,
412 GetQueryArg());
413 return 0;
414 }
415
416 protected:
417 void Parse(unsigned char* buf, int len) {
418 HandleScope scope;
419
420 struct ares_srv_reply* srv_start;
421 int status = ares_parse_srv_reply(buf, len, &srv_start);
422 if (status != ARES_SUCCESS) {
423 this->ParseError(status);
424 return;
425 }
426
427 Local<Array> srv_records = Array::New();
72e18d7 dns_uv: match the old api better, fix tests
Bert Belder authored
428 Local<String> name_symbol = String::NewSymbol("name");
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
429 Local<String> port_symbol = String::NewSymbol("port");
430 Local<String> priority_symbol = String::NewSymbol("priority");
431 Local<String> weight_symbol = String::NewSymbol("weight");
432 int i = 0;
433 for (struct ares_srv_reply* srv_current = srv_start;
434 srv_current;
435 srv_current = srv_current->next) {
436 Local<Object> srv_record = Object::New();
72e18d7 dns_uv: match the old api better, fix tests
Bert Belder authored
437 srv_record->Set(name_symbol, String::New(srv_current->host));
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
438 srv_record->Set(port_symbol, Integer::New(srv_current->port));
439 srv_record->Set(priority_symbol, Integer::New(srv_current->priority));
440 srv_record->Set(weight_symbol, Integer::New(srv_current->weight));
441 srv_records->Set(Integer::New(i++), srv_record);
442 }
443
444 ares_free_data(srv_start);
445
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
446 this->CallOnComplete(srv_records);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
447 }
448 };
449
450
451 class GetHostByAddrWrap: public QueryWrap {
452 public:
453 int Send(const char* name) {
454 int length, family;
455 char address_buffer[sizeof(struct in6_addr)];
456
457 if (uv_inet_pton(AF_INET, name, &address_buffer) == 1) {
458 length = sizeof(struct in_addr);
459 family = AF_INET;
460 } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 1) {
461 length = sizeof(struct in6_addr);
462 family = AF_INET6;
463 } else {
464 return ARES_ENOTIMP;
465 }
466
467 ares_gethostbyaddr(ares_channel,
468 address_buffer,
469 length,
470 family,
471 Callback,
472 GetQueryArg());
473 return 0;
474 }
475
476 protected:
477 void Parse(struct hostent* host) {
478 HandleScope scope;
479
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
480 this->CallOnComplete(HostentToNames(host));
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
481 }
482 };
483
484
485 class GetHostByNameWrap: public QueryWrap {
486 public:
487 int Send(const char* name, int family) {
488 ares_gethostbyname(ares_channel, name, family, Callback, GetQueryArg());
489 return 0;
490 }
491
492 protected:
493 void Parse(struct hostent* host) {
494 HandleScope scope;
495
496 Local<Array> addresses = HostentToAddresses(host);
497 Local<Integer> family = Integer::New(host->h_addrtype);
498
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
499 this->CallOnComplete(addresses, family);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
500 }
501 };
502
503
504 template <class Wrap>
505 static Handle<Value> Query(const Arguments& args) {
506 HandleScope scope;
507
508 assert(!args.IsConstructCall());
509 assert(args.Length() >= 2);
510 assert(args[1]->IsFunction());
511
512 Wrap* wrap = new Wrap();
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
513 wrap->SetOnComplete(args[1]);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
514
515 // We must cache the wrap's js object here, because cares might make the
516 // callback from the wrap->Send stack. This will destroy the wrap's internal
517 // object reference, causing wrap->GetObject() to return undefined.
518 Local<Object> object = Local<Object>::New(wrap->GetObject());
519
520 String::Utf8Value name(args[0]->ToString());
521
522 int r = wrap->Send(*name);
523 if (r) {
524 SetAresErrno(r);
525 delete wrap;
526 return scope.Close(v8::Null());
527 } else {
528 return scope.Close(object);
529 }
530 }
531
532
533 template <class Wrap>
534 static Handle<Value> QueryWithFamily(const Arguments& args) {
535 HandleScope scope;
536
537 assert(!args.IsConstructCall());
538 assert(args.Length() >= 3);
539 assert(args[2]->IsFunction());
540
541 Wrap* wrap = new Wrap();
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
542 wrap->SetOnComplete(args[2]);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
543
544 // We must cache the wrap's js object here, because cares might make the
545 // callback from the wrap->Send stack. This will destroy the wrap's internal
546 // object reference, causing wrap->GetObject() to return undefined.
547 Local<Object> object = Local<Object>::New(wrap->GetObject());
548
549 String::Utf8Value name(args[0]->ToString());
550 int family = args[1]->Int32Value();
551
552 int r = wrap->Send(*name, family);
553 if (r) {
554 SetAresErrno(r);
555 delete wrap;
556 return scope.Close(v8::Null());
557 } else {
558 return scope.Close(object);
559 }
560 }
561
562
563 static void Initialize(Handle<Object> target) {
564 HandleScope scope;
565 int r;
566
567 r = ares_library_init(ARES_LIB_INIT_ALL);
568 assert(r == ARES_SUCCESS);
569
570 struct ares_options options;
21cc4c4 @ry Upgrade libuv to ea4271f
ry authored
571 uv_ares_init_options(uv_default_loop(), &ares_channel, &options, 0);
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
572 assert(r == 0);
573
574 NODE_SET_METHOD(target, "queryA", Query<QueryAWrap>);
575 NODE_SET_METHOD(target, "queryAaaa", Query<QueryAaaaWrap>);
576 NODE_SET_METHOD(target, "queryCname", Query<QueryCnameWrap>);
577 NODE_SET_METHOD(target, "queryMx", Query<QueryMxWrap>);
578 NODE_SET_METHOD(target, "queryNs", Query<QueryNsWrap>);
579 NODE_SET_METHOD(target, "querySrv", Query<QuerySrvWrap>);
580 NODE_SET_METHOD(target, "getHostByAddr", Query<GetHostByAddrWrap>);
581 NODE_SET_METHOD(target, "getHostByName", QueryWithFamily<GetHostByNameWrap>);
582
583 target->Set(String::NewSymbol("AF_INET"), Integer::New(AF_INET));
584 target->Set(String::NewSymbol("AF_INET6"), Integer::New(AF_INET6));
585 target->Set(String::NewSymbol("AF_UNSPEC"), Integer::New(AF_UNSPEC));
586
12798c6 @ry dns callbacks should go through MakeCallback
ry authored
587 oncomplete_sym = Persistent<String>::New(String::NewSymbol("oncomplete"));
858f230 @piscisaureus Bindings for libuv-integrated c-ares
piscisaureus authored
588 }
589
590
591 } // namespace cares_wrap
592
593 } // namespace node
594
595 NODE_MODULE(node_cares_wrap, node::cares_wrap::Initialize);
Something went wrong with that request. Please try again.