ruby-cares 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. data/examples/example.rb +32 -0
  2. data/ext/cares.c +653 -0
  3. data/ext/extconf.rb +5 -0
  4. metadata +48 -0
@@ -0,0 +1,32 @@
1
+ require 'socket'
2
+ require 'cares'
3
+
4
+ domain = 'www.rubyforge.org'
5
+ addr = '205.234.109.18'
6
+ port = 25
7
+
8
+ cares = Cares.new
9
+
10
+ cares.gethostbyname(domain, Socket::AF_INET) do |name, aliases, family, *addrs|
11
+ puts "[-] Cares#gethostbyname:"
12
+ puts " #{domain}:"
13
+ puts " canonical name: #{name}"
14
+ puts " aliases: #{aliases.join(', ')}"
15
+ puts " addresses: #{addrs.join(', ')}"
16
+ puts
17
+ end
18
+
19
+ cares.gethostbyaddr(addr, Socket::AF_INET) do |name, *rest|
20
+ puts "[-] Cares#gethostbyaddr:"
21
+ puts " #{addr}:"
22
+ puts " name: #{name}"
23
+ puts
24
+ end
25
+
26
+ cares.getnameinfo(:addr => addr, :port => port) do |name, service|
27
+ puts "[-] Cares#getnameinfo:"
28
+ puts " IP #{addr} is #{name}"
29
+ puts " Port #{port} is #{service}"
30
+ end
31
+
32
+ cares.select_loop
@@ -0,0 +1,653 @@
1
+ /*
2
+ *--
3
+ * Copyright (c) 2006 Andre Nathan <andre@digirati.com.br>
4
+ *
5
+ * Permission to use, copy, modify, and distribute this software for any
6
+ * purpose with or without fee is hereby granted, provided that the above
7
+ * copyright notice and this permission notice appear in all copies.
8
+ *
9
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+ *
17
+ * $Id$
18
+ *++
19
+ */
20
+ /*
21
+ * Document-class: Cares
22
+ *
23
+ * == Overview
24
+ *
25
+ * Ruby/Cares is a C extension to the
26
+ * c-ares[http://daniel.haxx.se/projects/c-ares/] library. It performs DNS
27
+ * requests and name resolving asynchronously.
28
+ *
29
+ * = Example
30
+ *
31
+ * Below follows a simple example. See Cares' methods documentations for
32
+ * details valid parameters.
33
+ *
34
+ * require 'cares'
35
+ * require 'socket'
36
+ *
37
+ * # Create new Cares instance
38
+ * cares = Cares.new(:timeout => 3)
39
+ *
40
+ * #
41
+ * # Set up three DNS requests.
42
+ * #
43
+ *
44
+ * cares.gethostbyname('www.rubyforge.org', Socket::AF_INET) do |name, aliases, family, *addrs|
45
+ * puts "[-] Cares#gethostbyname:"
46
+ * puts " #{domain}:"
47
+ * puts " canonical name: #{name}"
48
+ * puts " aliases: #{aliases.join(', ')}"
49
+ * puts " addresses: #{addrs.join(', ')}"
50
+ * puts
51
+ * end
52
+ *
53
+ * cares.gethostbyaddr('205.234.109.18', Socket::AF_INET) do |name, *rest|
54
+ * puts "[-] Cares#gethostbyaddr:"
55
+ * puts " #{addr}:"
56
+ * puts " name: #{name}"
57
+ * puts
58
+ * end
59
+ *
60
+ * cares.getnameinfo(:addr => '205.234.109.18') do |name, service|
61
+ * puts "[-] Cares#getnameinfo:"
62
+ * puts " #{addr}:"
63
+ * puts " name: #{name}"
64
+ * puts
65
+ * end
66
+ *
67
+ * # Wait for responses and yield the blocks passed to each of the
68
+ * # methods calls above.
69
+ * cares.select_loop
70
+ */
71
+ #include <ruby.h>
72
+ #include <ares.h>
73
+
74
+ #include <sys/types.h>
75
+ #include <sys/socket.h>
76
+
77
+ #include <arpa/inet.h>
78
+ #include <netdb.h>
79
+
80
+ #define BUFLEN 46
81
+
82
+ static VALUE cInit;
83
+ static VALUE cNameInfo;
84
+ static VALUE cNotImpError;
85
+ static VALUE cBadNameError;
86
+ static VALUE cNotFoundError;
87
+ static VALUE cNoMemError;
88
+ static VALUE cDestrError;
89
+ static VALUE cFlagsError;
90
+
91
+ static void
92
+ define_cares_exceptions(VALUE cares)
93
+ {
94
+ cNotImpError =
95
+ rb_define_class_under(cares, "NotImplementedError", rb_eException);
96
+ cBadNameError =
97
+ rb_define_class_under(cares, "BadNameError", rb_eException);
98
+ cNotFoundError =
99
+ rb_define_class_under(cares, "AddressNotFoundError", rb_eException);
100
+ cNoMemError =
101
+ rb_define_class_under(cares, "NoMemoryError", rb_eException);
102
+ cDestrError =
103
+ rb_define_class_under(cares, "DestructionError", rb_eException);
104
+ cFlagsError =
105
+ rb_define_class_under(cares, "BadFlagsError", rb_eException);
106
+ }
107
+
108
+ static void
109
+ define_init_flags(VALUE init)
110
+ {
111
+ rb_define_const(init, "USEVC", INT2NUM(ARES_FLAG_USEVC));
112
+ rb_define_const(init, "PRIMARY", INT2NUM(ARES_FLAG_PRIMARY));
113
+ rb_define_const(init, "IGNTC", INT2NUM(ARES_FLAG_IGNTC));
114
+ rb_define_const(init, "NORECURSE", INT2NUM(ARES_FLAG_NORECURSE));
115
+ rb_define_const(init, "STAYOPEN", INT2NUM(ARES_FLAG_STAYOPEN));
116
+ rb_define_const(init, "NOSEARCH", INT2NUM(ARES_FLAG_NOSEARCH));
117
+ rb_define_const(init, "NOALIASES", INT2NUM(ARES_FLAG_NOALIASES));
118
+ rb_define_const(init, "NOCHECKRESP", INT2NUM(ARES_FLAG_NOCHECKRESP));
119
+ }
120
+
121
+ static void
122
+ define_nameinfo_flags(VALUE ni)
123
+ {
124
+ rb_define_const(ni, "NOFQDN", INT2NUM(ARES_NI_NOFQDN));
125
+ rb_define_const(ni, "NUMERICHOST", INT2NUM(ARES_NI_NUMERICHOST));
126
+ rb_define_const(ni, "NAMEREQD", INT2NUM(ARES_NI_NAMEREQD));
127
+ rb_define_const(ni, "NUMERICSERV", INT2NUM(ARES_NI_NUMERICSERV));
128
+ rb_define_const(ni, "TCP", INT2NUM(ARES_NI_TCP));
129
+ rb_define_const(ni, "UDP", INT2NUM(ARES_NI_UDP));
130
+ rb_define_const(ni, "SCTP", INT2NUM(ARES_NI_SCTP));
131
+ rb_define_const(ni, "DCCP", INT2NUM(ARES_NI_DCCP));
132
+ rb_define_const(ni, "NUMERICSCOPE", INT2NUM(ARES_NI_NUMERICSCOPE));
133
+ }
134
+
135
+ static void
136
+ raise_error(int error)
137
+ {
138
+ switch (error) {
139
+ case ARES_ENOTIMP:
140
+ rb_raise(cNotImpError, "%s", ares_strerror(error));
141
+ break;
142
+ case ARES_ENOTFOUND:
143
+ rb_raise(cNotFoundError, "%s", ares_strerror(error));
144
+ break;
145
+ case ARES_ENOMEM:
146
+ rb_raise(cNoMemError, "%s", ares_strerror(error));
147
+ break;
148
+ case ARES_EDESTRUCTION:
149
+ rb_raise(cDestrError, "%s", ares_strerror(error));
150
+ break;
151
+ case ARES_EBADFLAGS:
152
+ rb_raise(cFlagsError, "%s", ares_strerror(error));
153
+ break;
154
+ }
155
+ }
156
+
157
+ static VALUE
158
+ rb_cares_destroy(void *chp)
159
+ {
160
+ ares_destroy(*(ares_channel *)chp);
161
+ return(Qnil);
162
+ }
163
+
164
+ static VALUE
165
+ rb_cares_alloc(VALUE klass)
166
+ {
167
+ ares_channel *chp;
168
+ return(Data_Make_Struct(klass, ares_channel, 0, rb_cares_destroy, chp));
169
+ }
170
+
171
+ static void
172
+ init_callback(void *arg, int s, int read, int write)
173
+ {
174
+ VALUE socket, rb_cSock;
175
+ VALUE block = (VALUE)arg;
176
+
177
+ rb_cSock = rb_const_get(rb_cObject, rb_intern("Socket"));
178
+ socket = rb_funcall(rb_cSock, rb_intern("for_fd"), 1, INT2NUM(s));
179
+
180
+ rb_funcall(block, rb_intern("call"), 3, socket,
181
+ read ? Qtrue : Qfalse, write ? Qtrue : Qfalse);
182
+ }
183
+
184
+ static int
185
+ set_init_opts(VALUE opts, struct ares_options *aop)
186
+ {
187
+ int optmask = 0;
188
+ VALUE vflags, vtimeout, vtries, vndots, vudp_port, vtcp_port;
189
+ VALUE vservers, vdomains, vlookups;
190
+
191
+ if (!NIL_P(opts)) {
192
+ vflags = rb_hash_aref(opts, ID2SYM(rb_intern("flags")));
193
+ if (!NIL_P(vflags)) {
194
+ aop->flags = NUM2INT(vflags);
195
+ optmask |= ARES_OPT_FLAGS;
196
+ }
197
+ vtimeout = rb_hash_aref(opts, ID2SYM(rb_intern("timeout")));
198
+ if (!NIL_P(vtimeout)) {
199
+ aop->timeout = NUM2INT(vtimeout);
200
+ optmask |= ARES_OPT_TIMEOUT;
201
+ }
202
+ vtries = rb_hash_aref(opts, ID2SYM(rb_intern("tries")));
203
+ if (!NIL_P(vtries)) {
204
+ aop->timeout = NUM2INT(vtries);
205
+ optmask |= ARES_OPT_TRIES;
206
+ }
207
+ vndots = rb_hash_aref(opts, ID2SYM(rb_intern("ndots")));
208
+ if (!NIL_P(vndots)) {
209
+ aop->timeout = NUM2INT(vtries);
210
+ optmask |= ARES_OPT_NDOTS;
211
+ }
212
+ vudp_port = rb_hash_aref(opts, ID2SYM(rb_intern("udp_port")));
213
+ if (!NIL_P(vudp_port)) {
214
+ aop->udp_port = NUM2UINT(vudp_port);
215
+ optmask |= ARES_OPT_UDP_PORT;
216
+ }
217
+ vtcp_port = rb_hash_aref(opts, ID2SYM(rb_intern("tcp_port")));
218
+ if (!NIL_P(vtcp_port)) {
219
+ aop->tcp_port = NUM2UINT(vtcp_port);
220
+ optmask |= ARES_OPT_TCP_PORT;
221
+ }
222
+ vservers = rb_hash_aref(opts, ID2SYM(rb_intern("servers")));
223
+ if (!NIL_P(vservers)) {
224
+ long i, n;
225
+ struct in_addr in, *servers;
226
+
227
+ n = RARRAY(vservers)->len;
228
+ servers = ALLOCA_N(struct in_addr, n);
229
+ for (i = 0; i < n; i++) {
230
+ char *caddr;
231
+ VALUE vaddr;
232
+ vaddr = rb_ary_entry(vservers, i);
233
+ caddr = StringValuePtr(vaddr);
234
+ if (inet_pton(AF_INET, caddr, &in) != 1)
235
+ rb_sys_fail("initialize");
236
+ MEMCPY(servers+i, &in, struct in_addr, 1);
237
+ }
238
+ aop->servers = servers;
239
+ aop->nservers = n;
240
+ optmask |= ARES_OPT_SERVERS;
241
+ }
242
+ vdomains = rb_hash_aref(opts, ID2SYM(rb_intern("domains")));
243
+ if (!NIL_P(vdomains)) {
244
+ char *domains;
245
+ long i, n;
246
+
247
+ n = RARRAY(vdomains)->len;
248
+ domains = ALLOC_N(char, n);
249
+ for (i = 0; i < n; i++) {
250
+ char *cdomain;
251
+ VALUE vdomain;
252
+ vdomain = rb_ary_entry(vdomains, i);
253
+ cdomain = StringValuePtr(vdomain);
254
+ MEMCPY(domains+i, cdomain, strlen(cdomain), 1);
255
+ }
256
+ aop->domains = &domains;
257
+ aop->ndomains = n;
258
+ optmask |= ARES_OPT_DOMAINS;
259
+ }
260
+ vlookups = rb_hash_aref(opts, ID2SYM(rb_intern("lookups")));
261
+ if (!NIL_P(vlookups)) {
262
+ ID id = rb_to_id(vlookups);
263
+ if (id == rb_intern("dns"))
264
+ aop->lookups = "b";
265
+ else if (id == rb_intern("hosts"))
266
+ aop->lookups = "f";
267
+ else {
268
+ /*
269
+ * XXX Let c-ares handle it (why does rb_raise
270
+ * segfault here?)
271
+ * rb_raise(rb_eArgError, "initialize: "
272
+ * "invalid value for :lookups");
273
+ */
274
+ aop->lookups = "";
275
+ }
276
+ optmask |= ARES_OPT_LOOKUPS;
277
+ }
278
+ }
279
+ if (rb_block_given_p()) {
280
+ aop->sock_state_cb = init_callback;
281
+ aop->sock_state_cb_data = (void *)rb_block_proc();
282
+ optmask |= ARES_OPT_SOCK_STATE_CB;
283
+ }
284
+
285
+ return(optmask);
286
+ }
287
+
288
+ /*
289
+ * call-seq:
290
+ * Cares.new([options]) => cares_obj
291
+ * Cares.new([options]) { |socket, read, write| block } => cares_obj
292
+ *
293
+ * Creates a new <code>Cares</code> object. The <code>options</code> hash
294
+ * accepts the following keys:
295
+ *
296
+ * * <code>:flags</code> Flags controlling the behaviour of the resolver.
297
+ * See below for a description os the possible flags.
298
+ * * <code>:timeout</code> The number of seconds each name server is given
299
+ * to respond to a query on the first try. For further queries, the timeout
300
+ * scales nearly with the provided value. The default is 5 seconds.
301
+ * * <code>:tries</code> The number of times the resolver will try to
302
+ * contact each name server before giving up. The default is 4 tries.
303
+ * * <code>:ndots</code> The number of dots that must be present in a
304
+ * domain name for it to be queried "as is", prior to querying with the
305
+ * default domain extensions appended. The default is 1, unless set
306
+ * otherwise in resolv.conf or the <code>RES_OPTIONS</code> environment
307
+ * variable.
308
+ * * <code>:udp_port</code> The port to use for UDP queries. The default is 53.
309
+ * * <code>:tcp_port</code> The port to use for TCP queries. The default is 53.
310
+ * * <code>:servers</code> An array of IP addresses to be used as the servers
311
+ * to be contacted, instead of the ones found in resolv.conf or the local
312
+ * name daemon.
313
+ * * <code>:domains</code> An array of domains to be searched, instead of
314
+ * the ones specified in resolv.conf or the machine hostname.
315
+ * * <code>:lookups</code> The lookups to perform for host queries. It
316
+ * should be a string of the characters <i>b</i> or <i>f</i>, where <i>b</i>
317
+ * indicates a DNS lookup, and <i>f</i> indicates a hosts file lookup.
318
+ *
319
+ * The <code>:flags</code> option is a bitwise-or of values from the list
320
+ * below:
321
+ *
322
+ * * <code>Cares::Init::USEVC</code> Always use TCP queries. Normally, TCP
323
+ * is only used if a UDP query returns a truncated result.
324
+ * * <code>Cares::Init::PRIMARY</code> Only query the first server in the
325
+ * server list.
326
+ * * <code>Cares::Init::IGNTC</code> If a truncated response to an UDP query
327
+ * is received, do not fall back to TCP; simply continue with the truncated
328
+ * response.
329
+ * * <code>Cares::Init::NORECURSE</code> Do not set the "recursion desired"
330
+ * bit on outgoing queries.
331
+ * * <code>Cares::Init::STAYOPEN</code> Do not close the communication
332
+ * sockets when the number of active queries drops to zero.
333
+ * * <code>Cares::Init::NOSEARCH</code> Do not use the default search
334
+ * domains; only query hostnames as-is or as aliases.
335
+ * * <code>Cares::Init::NOALIASES</code> Do not honor the
336
+ * <code>HOSTALIASES</code> environment variable, which specifies a
337
+ * file of hostname translations.
338
+ * * <code>Cares::Init::NOCHECKRESP</code> Do not discard responses with the
339
+ * <code>SERVFAIL</code>, <code>NOTIMP</code>, or <code>REFUSED</code>
340
+ * response codes or responses whose questions don't match the
341
+ * questions in the request.
342
+ *
343
+ * If a block is given, it'll be called when a socket used in name resolving
344
+ * has its state changed. The block takes three arguments. The first one is
345
+ * the <code>Socket</code> object, the the other two are boolean values
346
+ * indicating if the socket should listen for read and/or write events,
347
+ * respectively.
348
+ */
349
+ static VALUE
350
+ rb_cares_init(int argc, VALUE *argv, VALUE self)
351
+ {
352
+ int status, optmask;
353
+ ares_channel *chp;
354
+ struct ares_options ao;
355
+ VALUE opts;
356
+
357
+ Data_Get_Struct(self, ares_channel, chp);
358
+
359
+ rb_scan_args(argc, argv, "01", &opts);
360
+ if (NIL_P(opts) && !rb_block_given_p()) {
361
+ status = ares_init(chp);
362
+ if (status != ARES_SUCCESS)
363
+ raise_error(status);
364
+ return(self);
365
+ }
366
+
367
+ optmask = set_init_opts(opts, &ao);
368
+
369
+ status = ares_init_options(chp, &ao, optmask);
370
+ if (status != ARES_SUCCESS)
371
+ raise_error(status);
372
+
373
+ return(self);
374
+ }
375
+
376
+ static void
377
+ host_callback(void *arg, int status, struct hostent *hp)
378
+ {
379
+ char buf[BUFLEN], **p;
380
+ VALUE block, info, aliases;
381
+
382
+ if (status != ARES_SUCCESS)
383
+ raise_error(status);
384
+
385
+ block = (VALUE)arg;
386
+
387
+ info = rb_ary_new();
388
+ rb_ary_push(info, rb_str_new2(hp->h_name));
389
+
390
+ aliases = rb_ary_new();
391
+ rb_ary_push(info, aliases);
392
+ if (hp->h_aliases != NULL) {
393
+ for (p = hp->h_aliases; *p; p++)
394
+ rb_ary_push(aliases, rb_str_new2(*p));
395
+ }
396
+
397
+ rb_ary_push(info, INT2NUM(hp->h_addrtype));
398
+
399
+ for (p = hp->h_addr_list; *p != NULL; p++) {
400
+ inet_ntop(hp->h_addrtype, *p, buf, BUFLEN);
401
+ rb_ary_push(info, rb_str_new2(buf));
402
+ }
403
+
404
+ rb_funcall(block, rb_intern("call"), 1, info);
405
+ }
406
+
407
+ /*
408
+ * call-seq:
409
+ * cares.gethostbyname(name, family) { |cname, aliases, family, *addrs| block } => cares
410
+ *
411
+ * Performs a DNS lookup on <code>name</code>, for addresses of family
412
+ * <code>family</code>. The <code>family</code> argument is either
413
+ * <code>Socket::AF_INET</code> or <code>Socket::AF_INET6</code>. The results
414
+ * are passed as arguments to the block:
415
+ *
416
+ * * <code>cname</code>: <code>name</code>'s canonical name.
417
+ * * <code>aliases</code>: array of aliases.
418
+ * * <code>family</code>: address family.
419
+ * * <code>*addrs</code>: array containing <code>name</code>'s addresses.
420
+ */
421
+ static VALUE
422
+ rb_cares_gethostbyname(VALUE self, VALUE host, VALUE family)
423
+ {
424
+ ares_channel *chp;
425
+
426
+ if (!rb_block_given_p())
427
+ rb_raise(rb_eArgError, "gethostbyname: block needed");
428
+
429
+ Data_Get_Struct(self, ares_channel, chp);
430
+ ares_gethostbyname(*chp, StringValuePtr(host), NUM2INT(family),
431
+ host_callback, (void *)rb_block_proc());
432
+ return(self);
433
+ }
434
+
435
+ /*
436
+ * call-seq:
437
+ * cares.gethostbyaddr(addr, family) { |name, aliases, family, *addrs| block } => cares
438
+ *
439
+ * Performs a reverse DNS query on <code>addr</code>. for addresses of family
440
+ * <code>family</code>. The <code>family</code> argument is either
441
+ * <code>Socket::AF_INET</code> or <code>Socket::AF_INET6</code>. The results
442
+ * are passed as arguments to the block:
443
+ *
444
+ * * <code>name</code>: <code>addr</code>'s name.
445
+ * * <code>aliases</code>: array of aliases.
446
+ * * <code>family</code>: address family.
447
+ * * <code>*addrs</code>: array containing <code>name</code>'s addresses.
448
+ */
449
+ static VALUE
450
+ rb_cares_gethostbyaddr(VALUE self, VALUE addr, VALUE family)
451
+ {
452
+ char *caddr;
453
+ int cfamily;
454
+ ares_channel *chp;
455
+
456
+ if (!rb_block_given_p())
457
+ rb_raise(rb_eArgError, "gethostbyaddr: block needed");
458
+
459
+ Data_Get_Struct(self, ares_channel, chp);
460
+ cfamily = NUM2INT(family);
461
+ caddr = StringValuePtr(addr);
462
+
463
+ switch (cfamily) {
464
+ case AF_INET: {
465
+ struct in_addr in;
466
+ if (inet_pton(cfamily, caddr, &in) != 1)
467
+ rb_sys_fail("gethostbyaddr");
468
+ ares_gethostbyaddr(*chp, &in, sizeof(in), cfamily,
469
+ host_callback, (void *)rb_block_proc());
470
+ break;
471
+ }
472
+ case AF_INET6: {
473
+ struct in6_addr in6;
474
+ if (inet_pton(cfamily, caddr, &in6) != 1)
475
+ rb_sys_fail("gethostbyaddr");
476
+ ares_gethostbyaddr(*chp, &in6, sizeof(in6), cfamily,
477
+ host_callback, (void *)rb_block_proc());
478
+ break;
479
+ }
480
+ default:
481
+ rb_raise(cNotImpError, "gethostbyaddr: invalid address family");
482
+ }
483
+ return(self);
484
+ }
485
+
486
+ static void
487
+ nameinfo_callback(void *arg, int status, char *node, char *service)
488
+ {
489
+ VALUE block, info;
490
+
491
+ if (status != ARES_SUCCESS)
492
+ raise_error(status);
493
+
494
+ block = (VALUE)arg;
495
+ info = rb_ary_new();
496
+
497
+ if (node != NULL)
498
+ rb_ary_push(info, rb_str_new2(node));
499
+ if (service != NULL)
500
+ rb_ary_push(info, rb_str_new2(service));
501
+
502
+ rb_funcall(block, rb_intern("call"), 1, info);
503
+ }
504
+
505
+ /*
506
+ * call-seq:
507
+ * cares.getnameinfo(nameservice) { |name, service| block } => cares
508
+ *
509
+ * Performs a protocol-independent reverse lookup on the host and/or service
510
+ * names specified on the <code>nameservice</code> hash. The valid keys are:
511
+ *
512
+ * * <code>:addr</code>: IPv4 or IPv6 address.
513
+ * * <code>:service</code>: Service name.
514
+ *
515
+ * The lookup results are passed as parameters to the block.
516
+ */
517
+ static VALUE
518
+ rb_cares_getnameinfo(VALUE self, VALUE info)
519
+ {
520
+ int cflags;
521
+ socklen_t sslen;
522
+ struct sockaddr_storage ss;
523
+ ares_channel *chp;
524
+ VALUE vaddr, vport, vflags;
525
+
526
+ Data_Get_Struct(self, ares_channel, chp);
527
+
528
+ vflags = rb_hash_aref(info, ID2SYM(rb_intern("flags")));
529
+ if (!NIL_P(vflags))
530
+ cflags = NUM2INT(vflags);
531
+ else
532
+ cflags = 0;
533
+
534
+ sslen = 0;
535
+ ss.ss_family = AF_INET;
536
+
537
+ vaddr = rb_hash_aref(info, ID2SYM(rb_intern("addr")));
538
+ if (!NIL_P(vaddr)) {
539
+ char *caddr = StringValuePtr(vaddr);
540
+ struct sockaddr_in *sinp;
541
+ struct sockaddr_in6 *sin6p;
542
+
543
+ sinp = (struct sockaddr_in *)&ss;
544
+ sin6p = (struct sockaddr_in6 *)&ss;
545
+
546
+ cflags |= ARES_NI_LOOKUPHOST;
547
+
548
+ if (inet_pton(AF_INET, caddr, &sinp->sin_addr) == 1) {
549
+ sslen = sizeof(struct sockaddr_in);
550
+ } else if (inet_pton(AF_INET6, caddr, &sin6p->sin6_addr) == 1) {
551
+ ss.ss_family = AF_INET6;
552
+ sslen = sizeof(struct sockaddr_in6);
553
+ } else {
554
+ rb_raise(cNotImpError,
555
+ "getnameinfo: invalid address family");
556
+ }
557
+ }
558
+
559
+ vport = rb_hash_aref(info, ID2SYM(rb_intern("port")));
560
+ if (!NIL_P(vport)) {
561
+ u_short cport = htons(NUM2UINT(vport));
562
+ cflags |= ARES_NI_LOOKUPSERVICE;
563
+ switch (ss.ss_family) {
564
+ case AF_INET:
565
+ ((struct sockaddr_in *)&ss)->sin_port = cport;
566
+ sslen = sizeof(struct sockaddr_in);
567
+ break;
568
+ case AF_INET6:
569
+ ((struct sockaddr_in6 *)&ss)->sin6_port = cport;
570
+ sslen = sizeof(struct sockaddr_in6);
571
+ break;
572
+ }
573
+ }
574
+
575
+ ares_getnameinfo(*chp, (struct sockaddr *)&ss, sslen, cflags,
576
+ nameinfo_callback, (void *)rb_block_proc());
577
+ return(self);
578
+ }
579
+
580
+ /*
581
+ * call-seq:
582
+ * cares.select_loop(timeout=nil) => nil
583
+ *
584
+ * Handles the input/output and timeout associated witht the queries made
585
+ * from the name and address lookup methods. The block passed to each of
586
+ * those methods is yielded when the event is processed.
587
+ *
588
+ * The <code>timeout</code> hash accepts the following keys:
589
+ *
590
+ * * <code>:seconds</code>: Timeout in seconds.
591
+ * * <code>:useconds</code>: Timeout in microseconds.
592
+ */
593
+ static VALUE
594
+ rb_cares_select_loop(int argc, VALUE *argv, VALUE self)
595
+ {
596
+ int nfds;
597
+ fd_set read, write;
598
+ struct timeval *tvp, tv;
599
+ struct timeval *maxtvp, maxtv;
600
+ ares_channel *chp;
601
+ VALUE timeout;
602
+
603
+ rb_scan_args(argc, argv, "01", &timeout);
604
+
605
+ maxtvp = NULL;
606
+ if (!NIL_P(timeout)) {
607
+ VALUE secs, usecs;
608
+
609
+ secs = rb_hash_aref(timeout, ID2SYM(rb_intern("seconds")));
610
+ if (!NIL_P(secs))
611
+ maxtv.tv_sec = NUM2LONG(secs);
612
+ usecs = rb_hash_aref(timeout, ID2SYM(rb_intern("useconds")));
613
+ if (!NIL_P(usecs))
614
+ maxtv.tv_usec = NUM2LONG(usecs);
615
+
616
+ if (!NIL_P(secs) || !NIL_P(usecs))
617
+ maxtvp = &maxtv;
618
+ }
619
+
620
+ Data_Get_Struct(self, ares_channel, chp);
621
+
622
+ for (;;) {
623
+ FD_ZERO(&read);
624
+ FD_ZERO(&write);
625
+ nfds = ares_fds(*chp, &read, &write);
626
+ if (nfds == 0)
627
+ break;
628
+ tvp = ares_timeout(*chp, maxtvp, &tv);
629
+ rb_thread_select(nfds, &read, &write, NULL, tvp);
630
+ ares_process(*chp, &read, &write);
631
+ }
632
+ return(Qnil);
633
+ }
634
+
635
+ void
636
+ Init_cares(void)
637
+ {
638
+ VALUE cCares = rb_define_class("Cares", rb_cObject);
639
+ define_cares_exceptions(cCares);
640
+
641
+ cInit = rb_define_class_under(cCares, "Init", rb_cObject);
642
+ define_init_flags(cInit);
643
+
644
+ cNameInfo = rb_define_class_under(cCares, "NameInfo", rb_cObject);
645
+ define_nameinfo_flags(cNameInfo);
646
+
647
+ rb_define_alloc_func(cCares, rb_cares_alloc);
648
+ rb_define_method(cCares, "initialize", rb_cares_init, -1);
649
+ rb_define_method(cCares, "gethostbyname", rb_cares_gethostbyname, 2);
650
+ rb_define_method(cCares, "gethostbyaddr", rb_cares_gethostbyaddr, 2);
651
+ rb_define_method(cCares, "getnameinfo", rb_cares_getnameinfo, 1);
652
+ rb_define_method(cCares, "select_loop", rb_cares_select_loop, -1);
653
+ }
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+
3
+ if find_library('cares', 'ares_init') and have_header('ares.h')
4
+ create_makefile('cares')
5
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: ruby-cares
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.0
7
+ date: 2006-09-06 00:00:00 -03:00
8
+ summary: A Ruby extension for c-ares
9
+ require_paths:
10
+ - lib
11
+ email: andre@digirati.com.br
12
+ homepage: http://cares.rubyforge.org
13
+ rubyforge_project: cares
14
+ description: Ruby/Cares is a C extension for the c-ares library, Providing an asynchronous DNS resolver to be used with ruby scripts.
15
+ autorequire: cares
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Andre Nathan
31
+ files:
32
+ - ext/extconf.rb
33
+ - ext/cares.c
34
+ - examples/example.rb
35
+ test_files: []
36
+
37
+ rdoc_options: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ executables: []
42
+
43
+ extensions:
44
+ - ext/extconf.rb
45
+ requirements:
46
+ - c-ares
47
+ dependencies: []
48
+