Class: Cares

Inherits:
Object
  • Object
show all
Defined in:
ext/cares.c,
ext/cares.c

Overview

Overview

Ruby/Cares is a C extension to the c-ares library. It performs DNS requests and name resolving asynchronously.

Example

Below follows a simple example. See Cares’ methods documentations for details valid parameters.

require 'cares'
require 'socket'

# Create new Cares instance
cares = Cares.new(:timeout => 3)

#
# Set up three DNS requests.
#

cares.gethostbyname('www.rubyforge.org', Socket::AF_INET) do |name, aliases, family, *addrs|
  puts "[-] Cares#gethostbyname:"
  puts "  #{domain}:"
  puts "    canonical name: #{name}"
  puts "    aliases: #{aliases.join(', ')}"
  puts "    addresses: #{addrs.join(', ')}"
  puts
end

cares.gethostbyaddr('205.234.109.18', Socket::AF_INET) do |name, *rest|
  puts "[-] Cares#gethostbyaddr:"
  puts "  #{addr}:"
  puts "    name: #{name}"
  puts
end

cares.getnameinfo(:addr => '205.234.109.18') do |name, service|
  puts "[-] Cares#getnameinfo:"
  puts "  #{addr}:"
  puts "    name: #{name}"
  puts
end

# Wait for responses and yield the blocks passed to each of the
# methods calls above.
cares.select_loop

Defined Under Namespace

Classes: Init, NameInfo

Instance Method Summary collapse

Constructor Details

#new([options]) ⇒ Object #new([options]) {|socket, read, write| ... } ⇒ Object

Creates a new Cares object. The options hash accepts the following keys:

  • :flags Flags controlling the behaviour of the resolver. See below for a description os the possible flags.

  • :timeout The number of seconds each name server is given to respond to a query on the first try. For further queries, the timeout scales nearly with the provided value. The default is 5 seconds.

  • :tries The number of times the resolver will try to contact each name server before giving up. The default is 4 tries.

  • :ndots The number of dots that must be present in a domain name for it to be queried “as is”, prior to querying with the default domain extensions appended. The default is 1, unless set otherwise in resolv.conf or the RES_OPTIONS environment variable.

  • :udp_port The port to use for UDP queries. The default is 53.

  • :tcp_port The port to use for TCP queries. The default is 53.

  • :servers An array of IP addresses to be used as the servers to be contacted, instead of the ones found in resolv.conf or the local name daemon.

  • :domains An array of domains to be searched, instead of the ones specified in resolv.conf or the machine hostname.

  • :lookups The lookups to perform for host queries. It should be a string of the characters b or f, where b indicates a DNS lookup, and f indicates a hosts file lookup.

The :flags option is a bitwise-or of values from the list below:

  • Cares::Init::USEVC Always use TCP queries. Normally, TCP is only used if a UDP query returns a truncated result.

  • Cares::Init::PRIMARY Only query the first server in the server list.

  • Cares::Init::IGNTC If a truncated response to an UDP query is received, do not fall back to TCP; simply continue with the truncated response.

  • Cares::Init::NORECURSE Do not set the “recursion desired” bit on outgoing queries.

  • Cares::Init::STAYOPEN Do not close the communication sockets when the number of active queries drops to zero.

  • Cares::Init::NOSEARCH Do not use the default search domains; only query hostnames as-is or as aliases.

  • Cares::Init::NOALIASES Do not honor the HOSTALIASES environment variable, which specifies a file of hostname translations.

  • Cares::Init::NOCHECKRESP Do not discard responses with the SERVFAIL, NOTIMP, or REFUSED response codes or responses whose questions don’t match the questions in the request.

If a block is given, it’ll be called when a socket used in name resolving has its state changed. The block takes three arguments. The first one is the Socket object, the the other two are boolean values indicating if the socket should listen for read and/or write events, respectively.

Overloads:

  • #new([options]) {|socket, read, write| ... } ⇒ Object

    Yields:

    • (socket, read, write)


349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'ext/cares.c', line 349

static VALUE
rb_cares_init(int argc, VALUE *argv, VALUE self)
{
	int	status, optmask;
	ares_channel *chp;
	struct ares_options ao;
	VALUE	opts;

	Data_Get_Struct(self, ares_channel, chp);

	rb_scan_args(argc, argv, "01", &opts);
	if (NIL_P(opts) && !rb_block_given_p()) {
		status = ares_init(chp);
		if (status != ARES_SUCCESS)
			raise_error(status);
		return(self);
	}

	optmask = set_init_opts(opts, &ao);

	status = ares_init_options(chp, &ao, optmask);
	if (status != ARES_SUCCESS)
		raise_error(status);

	return(self);
}

Instance Method Details

#gethostbyaddr(addr, family) {|name, aliases, family, *addrs| ... } ⇒ Object

Performs a reverse DNS query on addr. for addresses of family family. The family argument is either Socket::AF_INET or Socket::AF_INET6. The results are passed as arguments to the block:

  • name: addr‘s name.

  • aliases: array of aliases.

  • family: address family.

  • *addrs: array containing name‘s addresses.

Yields:

  • (name, aliases, family, *addrs)


449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
# File 'ext/cares.c', line 449

static VALUE
rb_cares_gethostbyaddr(VALUE self, VALUE addr, VALUE family)
{
	char	*caddr;
	int	 cfamily;
	ares_channel *chp;

	if (!rb_block_given_p())
		rb_raise(rb_eArgError, "gethostbyaddr: block needed");

	Data_Get_Struct(self, ares_channel, chp);
	cfamily = NUM2INT(family);
	caddr = StringValuePtr(addr);

	switch (cfamily) {
	case AF_INET: {
		struct in_addr in;
		if (inet_pton(cfamily, caddr, &in) != 1)
			rb_sys_fail("gethostbyaddr");
		ares_gethostbyaddr(*chp, &in, sizeof(in), cfamily,
				   host_callback, (void *)rb_block_proc());
		break;
	}
	case AF_INET6: {
		struct in6_addr in6;
		if (inet_pton(cfamily, caddr, &in6) != 1)
			rb_sys_fail("gethostbyaddr");
		ares_gethostbyaddr(*chp, &in6, sizeof(in6), cfamily,
				   host_callback, (void *)rb_block_proc());
		break;
	}
	default:
		rb_raise(cNotImpError, "gethostbyaddr: invalid address family");
	}
	return(self);
}

#gethostbyname(name, family) {|cname, aliases, family, *addrs| ... } ⇒ Object

Performs a DNS lookup on name, for addresses of family family. The family argument is either Socket::AF_INET or Socket::AF_INET6. The results are passed as arguments to the block:

  • cname: name‘s canonical name.

  • aliases: array of aliases.

  • family: address family.

  • *addrs: array containing name‘s addresses.

Yields:

  • (cname, aliases, family, *addrs)


421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'ext/cares.c', line 421

static VALUE
rb_cares_gethostbyname(VALUE self, VALUE host, VALUE family)
{
	ares_channel *chp;

	if (!rb_block_given_p())
		rb_raise(rb_eArgError, "gethostbyname: block needed");

	Data_Get_Struct(self, ares_channel, chp);
	ares_gethostbyname(*chp, StringValuePtr(host), NUM2INT(family),
			   host_callback, (void *)rb_block_proc());
	return(self);
}

#getnameinfo(nameservice) {|name, service| ... } ⇒ Object

Performs a protocol-independent reverse lookup on the host and/or service names specified on the nameservice hash. The valid keys are:

  • :addr: IPv4 or IPv6 address.

  • :service: Service name.

The lookup results are passed as parameters to the block.

Yields:

  • (name, service)


517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'ext/cares.c', line 517

static VALUE
rb_cares_getnameinfo(VALUE self, VALUE info)
{
	int	cflags;
	socklen_t sslen;
	struct sockaddr_storage ss;
	ares_channel *chp;
	VALUE	vaddr, vport, vflags;

	Data_Get_Struct(self, ares_channel, chp);

	vflags = rb_hash_aref(info, ID2SYM(rb_intern("flags")));
	if (!NIL_P(vflags))
		cflags = NUM2INT(vflags);
	else
		cflags = 0;

	sslen = 0;
	ss.ss_family = AF_INET;

	vaddr = rb_hash_aref(info, ID2SYM(rb_intern("addr")));
	if (!NIL_P(vaddr)) {
		char	*caddr = StringValuePtr(vaddr);
		struct sockaddr_in *sinp;
		struct sockaddr_in6 *sin6p;

		sinp  = (struct sockaddr_in *)&ss;
		sin6p = (struct sockaddr_in6 *)&ss;

		cflags |= ARES_NI_LOOKUPHOST;

		if (inet_pton(AF_INET, caddr, &sinp->sin_addr) == 1) {
			sslen = sizeof(struct sockaddr_in);
		} else if (inet_pton(AF_INET6, caddr, &sin6p->sin6_addr) == 1) {
			ss.ss_family = AF_INET6;
			sslen = sizeof(struct sockaddr_in6);
		} else {
			rb_raise(cNotImpError,
				 "getnameinfo: invalid address family");
		}
	}

	vport = rb_hash_aref(info, ID2SYM(rb_intern("port")));
	if (!NIL_P(vport)) {
		u_short	cport = htons(NUM2UINT(vport));
		cflags |= ARES_NI_LOOKUPSERVICE;
		switch (ss.ss_family) {
		case AF_INET:
			((struct sockaddr_in *)&ss)->sin_port = cport;
			sslen = sizeof(struct sockaddr_in);
			break;
		case AF_INET6:
			((struct sockaddr_in6 *)&ss)->sin6_port = cport;
			sslen = sizeof(struct sockaddr_in6);
			break;
		}
	}

	ares_getnameinfo(*chp, (struct sockaddr *)&ss, sslen, cflags,
			 nameinfo_callback, (void *)rb_block_proc());
	return(self);
}

#select_loop(timeout = nil) ⇒ nil

Handles the input/output and timeout associated witht the queries made from the name and address lookup methods. The block passed to each of those methods is yielded when the event is processed.

The timeout hash accepts the following keys:

  • :seconds: Timeout in seconds.

  • :useconds: Timeout in microseconds.

Returns:

  • (nil)


593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
# File 'ext/cares.c', line 593

static VALUE
rb_cares_select_loop(int argc, VALUE *argv, VALUE self)
{
	int	nfds;
	fd_set	read, write;
	struct timeval *tvp, tv;
	struct timeval *maxtvp, maxtv;
	ares_channel *chp;
	VALUE	timeout;

	rb_scan_args(argc, argv, "01", &timeout);

	maxtvp = NULL;
	if (!NIL_P(timeout)) {
		VALUE	secs, usecs;

		secs = rb_hash_aref(timeout, ID2SYM(rb_intern("seconds")));
		if (!NIL_P(secs))
			maxtv.tv_sec = NUM2LONG(secs);
		usecs = rb_hash_aref(timeout, ID2SYM(rb_intern("useconds")));
		if (!NIL_P(usecs))
			maxtv.tv_usec = NUM2LONG(usecs);

		if (!NIL_P(secs) || !NIL_P(usecs))
			maxtvp = &maxtv;
	}

	Data_Get_Struct(self, ares_channel, chp);

	for (;;) {
		FD_ZERO(&read);
		FD_ZERO(&write);
		nfds = ares_fds(*chp, &read, &write);
		if (nfds == 0)
			break;
		tvp = ares_timeout(*chp, maxtvp, &tv);
		rb_thread_select(nfds, &read, &write, NULL, tvp);
		ares_process(*chp, &read, &write);
	}
	return(Qnil);
}