Skip to content
This repository
Newer
Older
100644 602 lines (476 sloc) 15.278 kb
858f2309 »
2011-07-05 Bindings for libuv-integrated c-ares
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
26 #if defined(__OpenBSD__) || defined(__MINGW32__)
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.
33 #ifdef __MINGW32__
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
66 static Persistent<String> onanswer_sym;
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
155 object_->DeleteHiddenValue(onanswer_sym);
156
157 object_.Dispose();
158 object_.Clear();
159 }
160
161 Handle<Object> GetObject() {
162 return object_;
163 }
164
165 void SetOnAnswer(Handle<Value> onanswer) {
166 assert(onanswer->IsFunction());
167 object_->SetHiddenValue(onanswer_sym, onanswer);
168 }
169
170 // Subclasses should implement the appropriate Send method.
171 virtual int Send(const char* name) {
172 assert(0);
173 }
174
175 virtual int Send(const char* name, int family) {
176 assert(0);
177 }
178
179 protected:
180 void* GetQueryArg() {
181 return static_cast<void*>(this);
182 }
183
184 static void Callback(void *arg, int status, int timeouts,
185 unsigned char* answer_buf, int answer_len) {
186 QueryWrap* wrap = reinterpret_cast<QueryWrap*>(arg);
187
188 if (status != ARES_SUCCESS) {
189 wrap->ParseError(status);
190 } else {
191 wrap->Parse(answer_buf, answer_len);
192 }
193
194 delete wrap;
195 }
196
197 static void Callback(void *arg, int status, int timeouts,
198 struct hostent* host) {
199 QueryWrap* wrap = reinterpret_cast<QueryWrap*>(arg);
200
201 if (status != ARES_SUCCESS) {
202 wrap->ParseError(status);
203 } else {
204 wrap->Parse(host);
205 }
206
207 delete wrap;
208 }
209
210 Handle<Function> GetOnAnswer() {
211 HandleScope scope;
212 assert(!object_.IsEmpty());
213 Handle<Value> onanswer = object_->GetHiddenValue(onanswer_sym);
214 assert(onanswer->IsFunction());
215 return scope.Close(Handle<Function>::Cast(onanswer));
216 }
217
218 void CallOnAnswer(Local<Value> answer) {
219 HandleScope scope;
220 Local<Value> argv[2] = { Integer::New(0), answer };
221 GetOnAnswer()->Call(this->object_, 2, argv);
222 }
223
224 void CallOnAnswer(Local<Value> answer, Local<Value> family) {
225 HandleScope scope;
226 Local<Value> argv[3] = { Integer::New(0), answer, family };
227 GetOnAnswer()->Call(this->object_, 3, argv);
228 }
229
230 void ParseError(int status) {
231 assert(status != ARES_SUCCESS);
232 SetAresErrno(status);
233
234 HandleScope scope;
235 Local<Value> argv[1] = { Integer::New(-1) };
236 GetOnAnswer()->Call(this->object_, 1, argv);
237 }
238
239 // Subclasses should implement the appropriate Parse method.
240 virtual void Parse(unsigned char* buf, int len) {
241 assert(0);
242 };
243
244 virtual void Parse(struct hostent* host) {
245 assert(0);
246 };
247
248 private:
249 Persistent<Object> object_;
250 };
251
252
253 class QueryAWrap: public QueryWrap {
254 public:
255 int Send(const char* name) {
256 ares_query(ares_channel, name, ns_c_in, ns_t_a, Callback, GetQueryArg());
257 return 0;
258 }
259
260 protected:
261 void Parse(unsigned char* buf, int len) {
262 HandleScope scope;
263
264 struct hostent* host;
265
266 int status = ares_parse_a_reply(buf, len, &host, NULL, NULL);
267 if (status != ARES_SUCCESS) {
268 this->ParseError(status);
269 return;
270 }
271
272 Local<Array> addresses = HostentToAddresses(host);
273 ares_free_hostent(host);
274
275 this->CallOnAnswer(addresses);
276 }
277 };
278
279
280 class QueryAaaaWrap: public QueryWrap {
281 public:
282 int Send(const char* name) {
283 ares_query(ares_channel,
284 name,
285 ns_c_in,
286 ns_t_aaaa,
287 Callback,
288 GetQueryArg());
289 return 0;
290 }
291
292 protected:
293 void Parse(unsigned char* buf, int len) {
294 HandleScope scope;
295
296 struct hostent* host;
297
298 int status = ares_parse_aaaa_reply(buf, len, &host, NULL, NULL);
299 if (status != ARES_SUCCESS) {
300 this->ParseError(status);
301 return;
302 }
303
304 Local<Array> addresses = HostentToAddresses(host);
305 ares_free_hostent(host);
306
307 this->CallOnAnswer(addresses);
308 }
309 };
310
311
312 class QueryCnameWrap: public QueryWrap {
313 public:
314 int Send(const char* name) {
315 ares_query(ares_channel,
316 name,
317 ns_c_in,
318 ns_t_cname,
319 Callback,
320 GetQueryArg());
321 return 0;
322 }
323
324 protected:
325 void Parse(unsigned char* buf, int len) {
326 HandleScope scope;
327
328 struct hostent* host;
329
330 int status = ares_parse_a_reply(buf, len, &host, NULL, NULL);
331 if (status != ARES_SUCCESS) {
332 this->ParseError(status);
333 return;
334 }
335
336 // A cname lookup always returns a single record but we follow the
337 // common API here.
338 Local<Array> result = Array::New(1);
339 result->Set(0, String::New(host->h_name));
340 ares_free_hostent(host);
341
342 this->CallOnAnswer(result);
343 }
344 };
345
346
347 class QueryMxWrap: public QueryWrap {
348 public:
349 int Send(const char* name) {
350 ares_query(ares_channel, name, ns_c_in, ns_t_mx, Callback, GetQueryArg());
351 return 0;
352 }
353
354 protected:
355 void Parse(unsigned char* buf, int len) {
356 HandleScope scope;
357
358 struct ares_mx_reply* mx_start;
359 int status = ares_parse_mx_reply(buf, len, &mx_start);
360 if (status != ARES_SUCCESS) {
361 this->ParseError(status);
362 return;
363 }
364
365 Local<Array> mx_records = Array::New();
366 Local<String> exchange_symbol = String::NewSymbol("exchange");
367 Local<String> priority_symbol = String::NewSymbol("priority");
368 int i = 0;
369 for (struct ares_mx_reply* mx_current = mx_start;
370 mx_current;
371 mx_current = mx_current->next) {
372 Local<Object> mx_record = Object::New();
373 mx_record->Set(exchange_symbol, String::New(mx_current->host));
374 mx_record->Set(priority_symbol, Integer::New(mx_current->priority));
375 mx_records->Set(Integer::New(i++), mx_record);
376 }
377
378 ares_free_data(mx_start);
379
380 this->CallOnAnswer(mx_records);
381 }
382 };
383
384
385 class QueryNsWrap: public QueryWrap {
386 public:
387 int Send(const char* name) {
388 ares_query(ares_channel, name, ns_c_in, ns_t_ns, Callback, GetQueryArg());
389 return 0;
390 }
391
392 protected:
393 void Parse(unsigned char* buf, int len) {
394 struct hostent* host;
395
396 int status = ares_parse_ns_reply(buf, len, &host);
397 if (status != ARES_SUCCESS) {
398 this->ParseError(status);
399 return;
400 }
401
402 Local<Array> names = HostentToNames(host);
403 ares_free_hostent(host);
404
405 this->CallOnAnswer(names);
406 }
407 };
408
409
410 class QuerySrvWrap: public QueryWrap {
411 public:
412 int Send(const char* name) {
413 ares_query(ares_channel,
414 name,
415 ns_c_in,
416 ns_t_srv,
417 Callback,
418 GetQueryArg());
419 return 0;
420 }
421
422 protected:
423 void Parse(unsigned char* buf, int len) {
424 HandleScope scope;
425
426 struct ares_srv_reply* srv_start;
427 int status = ares_parse_srv_reply(buf, len, &srv_start);
428 if (status != ARES_SUCCESS) {
429 this->ParseError(status);
430 return;
431 }
432
433 Local<Array> srv_records = Array::New();
434 Local<String> host_symbol = String::NewSymbol("host");
435 Local<String> port_symbol = String::NewSymbol("port");
436 Local<String> priority_symbol = String::NewSymbol("priority");
437 Local<String> weight_symbol = String::NewSymbol("weight");
438 int i = 0;
439 for (struct ares_srv_reply* srv_current = srv_start;
440 srv_current;
441 srv_current = srv_current->next) {
442 Local<Object> srv_record = Object::New();
443 srv_record->Set(host_symbol, String::New(srv_current->host));
444 srv_record->Set(port_symbol, Integer::New(srv_current->port));
445 srv_record->Set(priority_symbol, Integer::New(srv_current->priority));
446 srv_record->Set(weight_symbol, Integer::New(srv_current->weight));
447 srv_records->Set(Integer::New(i++), srv_record);
448 }
449
450 ares_free_data(srv_start);
451
452 this->CallOnAnswer(srv_records);
453 }
454 };
455
456
457 class GetHostByAddrWrap: public QueryWrap {
458 public:
459 int Send(const char* name) {
460 int length, family;
461 char address_buffer[sizeof(struct in6_addr)];
462
463 if (uv_inet_pton(AF_INET, name, &address_buffer) == 1) {
464 length = sizeof(struct in_addr);
465 family = AF_INET;
466 } else if (uv_inet_pton(AF_INET6, name, &address_buffer) == 1) {
467 length = sizeof(struct in6_addr);
468 family = AF_INET6;
469 } else {
470 return ARES_ENOTIMP;
471 }
472
473 ares_gethostbyaddr(ares_channel,
474 address_buffer,
475 length,
476 family,
477 Callback,
478 GetQueryArg());
479 return 0;
480 }
481
482 protected:
483 void Parse(struct hostent* host) {
484 HandleScope scope;
485
486 this->CallOnAnswer(HostentToNames(host));
487 }
488 };
489
490
491 class GetHostByNameWrap: public QueryWrap {
492 public:
493 int Send(const char* name, int family) {
494 ares_gethostbyname(ares_channel, name, family, Callback, GetQueryArg());
495 return 0;
496 }
497
498 protected:
499 void Parse(struct hostent* host) {
500 HandleScope scope;
501
502 Local<Array> addresses = HostentToAddresses(host);
503 Local<Integer> family = Integer::New(host->h_addrtype);
504
505 this->CallOnAnswer(addresses, family);
506 }
507 };
508
509
510 template <class Wrap>
511 static Handle<Value> Query(const Arguments& args) {
512 HandleScope scope;
513
514 assert(!args.IsConstructCall());
515 assert(args.Length() >= 2);
516 assert(args[1]->IsFunction());
517
518 Wrap* wrap = new Wrap();
519 wrap->SetOnAnswer(args[1]);
520
521 // We must cache the wrap's js object here, because cares might make the
522 // callback from the wrap->Send stack. This will destroy the wrap's internal
523 // object reference, causing wrap->GetObject() to return undefined.
524 Local<Object> object = Local<Object>::New(wrap->GetObject());
525
526 String::Utf8Value name(args[0]->ToString());
527
528 int r = wrap->Send(*name);
529 if (r) {
530 SetAresErrno(r);
531 delete wrap;
532 return scope.Close(v8::Null());
533 } else {
534 return scope.Close(object);
535 }
536 }
537
538
539 template <class Wrap>
540 static Handle<Value> QueryWithFamily(const Arguments& args) {
541 HandleScope scope;
542
543 assert(!args.IsConstructCall());
544 assert(args.Length() >= 3);
545 assert(args[2]->IsFunction());
546
547 Wrap* wrap = new Wrap();
548 wrap->SetOnAnswer(args[2]);
549
550 // We must cache the wrap's js object here, because cares might make the
551 // callback from the wrap->Send stack. This will destroy the wrap's internal
552 // object reference, causing wrap->GetObject() to return undefined.
553 Local<Object> object = Local<Object>::New(wrap->GetObject());
554
555 String::Utf8Value name(args[0]->ToString());
556 int family = args[1]->Int32Value();
557
558 int r = wrap->Send(*name, family);
559 if (r) {
560 SetAresErrno(r);
561 delete wrap;
562 return scope.Close(v8::Null());
563 } else {
564 return scope.Close(object);
565 }
566 }
567
568
569 static void Initialize(Handle<Object> target) {
570 HandleScope scope;
571 int r;
572
573 r = ares_library_init(ARES_LIB_INIT_ALL);
574 assert(r == ARES_SUCCESS);
575
576 struct ares_options options;
577 uv_ares_init_options(&ares_channel, &options, 0);
578 assert(r == 0);
579
580 NODE_SET_METHOD(target, "queryA", Query<QueryAWrap>);
581 NODE_SET_METHOD(target, "queryAaaa", Query<QueryAaaaWrap>);
582 NODE_SET_METHOD(target, "queryCname", Query<QueryCnameWrap>);
583 NODE_SET_METHOD(target, "queryMx", Query<QueryMxWrap>);
584 NODE_SET_METHOD(target, "queryNs", Query<QueryNsWrap>);
585 NODE_SET_METHOD(target, "querySrv", Query<QuerySrvWrap>);
586 NODE_SET_METHOD(target, "getHostByAddr", Query<GetHostByAddrWrap>);
587 NODE_SET_METHOD(target, "getHostByName", QueryWithFamily<GetHostByNameWrap>);
588
589 target->Set(String::NewSymbol("AF_INET"), Integer::New(AF_INET));
590 target->Set(String::NewSymbol("AF_INET6"), Integer::New(AF_INET6));
591 target->Set(String::NewSymbol("AF_UNSPEC"), Integer::New(AF_UNSPEC));
592
593 onanswer_sym = Persistent<String>::New(String::NewSymbol("onanswer"));
594 }
595
596
597 } // namespace cares_wrap
598
599 } // namespace node
600
601 NODE_MODULE(node_cares_wrap, node::cares_wrap::Initialize);
Something went wrong with that request. Please try again.