| Class | Cares |
| In: |
cares.c
|
| Parent: | Object |
Ruby/Cares is a C extension to the c-ares library. It performs DNS requests and name resolving asynchronously.
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
Creates a new Cares object. The options hash accepts the following keys:
The :flags option is a bitwise-or of values from the list below:
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.
/*
* call-seq:
* Cares.new([options]) => cares_obj
* Cares.new([options]) { |socket, read, write| block } => cares_obj
*
* Creates a new <code>Cares</code> object. The <code>options</code> hash
* accepts the following keys:
*
* * <code>:flags</code> Flags controlling the behaviour of the resolver.
* See below for a description os the possible flags.
* * <code>:timeout</code> 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.
* * <code>:tries</code> The number of times the resolver will try to
* contact each name server before giving up. The default is 4 tries.
* * <code>:ndots</code> 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 <code>RES_OPTIONS</code> environment
* variable.
* * <code>:udp_port</code> The port to use for UDP queries. The default is 53.
* * <code>:tcp_port</code> The port to use for TCP queries. The default is 53.
* * <code>:servers</code> 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.
* * <code>:domains</code> An array of domains to be searched, instead of
* the ones specified in resolv.conf or the machine hostname.
* * <code>:lookups</code> The lookups to perform for host queries. It
* should be a string of the characters <i>b</i> or <i>f</i>, where <i>b</i>
* indicates a DNS lookup, and <i>f</i> indicates a hosts file lookup.
*
* The <code>:flags</code> option is a bitwise-or of values from the list
* below:
*
* * <code>Cares::Init::USEVC</code> Always use TCP queries. Normally, TCP
* is only used if a UDP query returns a truncated result.
* * <code>Cares::Init::PRIMARY</code> Only query the first server in the
* server list.
* * <code>Cares::Init::IGNTC</code> If a truncated response to an UDP query
* is received, do not fall back to TCP; simply continue with the truncated
* response.
* * <code>Cares::Init::NORECURSE</code> Do not set the "recursion desired"
* bit on outgoing queries.
* * <code>Cares::Init::STAYOPEN</code> Do not close the communication
* sockets when the number of active queries drops to zero.
* * <code>Cares::Init::NOSEARCH</code> Do not use the default search
* domains; only query hostnames as-is or as aliases.
* * <code>Cares::Init::NOALIASES</code> Do not honor the
* <code>HOSTALIASES</code> environment variable, which specifies a
* file of hostname translations.
* * <code>Cares::Init::NOCHECKRESP</code> Do not discard responses with the
* <code>SERVFAIL</code>, <code>NOTIMP</code>, or <code>REFUSED</code>
* 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 <code>Socket</code> object, the the other two are boolean values
* indicating if the socket should listen for read and/or write events,
* respectively.
*/
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);
}
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:
/*
* call-seq:
* cares.gethostbyaddr(addr, family) { |name, aliases, family, *addrs| block } => cares
*
* Performs a reverse DNS query on <code>addr</code>. for addresses of family
* <code>family</code>. The <code>family</code> argument is either
* <code>Socket::AF_INET</code> or <code>Socket::AF_INET6</code>. The results
* are passed as arguments to the block:
*
* * <code>name</code>: <code>addr</code>'s name.
* * <code>aliases</code>: array of aliases.
* * <code>family</code>: address family.
* * <code>*addrs</code>: array containing <code>name</code>'s addresses.
*/
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);
}
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:
/*
* call-seq:
* cares.gethostbyname(name, family) { |cname, aliases, family, *addrs| block } => cares
*
* Performs a DNS lookup on <code>name</code>, for addresses of family
* <code>family</code>. The <code>family</code> argument is either
* <code>Socket::AF_INET</code> or <code>Socket::AF_INET6</code>. The results
* are passed as arguments to the block:
*
* * <code>cname</code>: <code>name</code>'s canonical name.
* * <code>aliases</code>: array of aliases.
* * <code>family</code>: address family.
* * <code>*addrs</code>: array containing <code>name</code>'s addresses.
*/
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);
}
Performs a protocol-independent reverse lookup on the host and/or service names specified on the nameservice hash. The valid keys are:
The lookup results are passed as parameters to the block.
/*
* call-seq:
* cares.getnameinfo(nameservice) { |name, service| block } => cares
*
* Performs a protocol-independent reverse lookup on the host and/or service
* names specified on the <code>nameservice</code> hash. The valid keys are:
*
* * <code>:addr</code>: IPv4 or IPv6 address.
* * <code>:service</code>: Port number.
*
* The lookup results are passed as parameters to the block.
*/
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);
}
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:
/*
* call-seq:
* cares.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 <code>timeout</code> hash accepts the following keys:
*
* * <code>:seconds</code>: Timeout in seconds.
* * <code>:useconds</code>: Timeout in microseconds.
*/
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);
}