EM-Udns

An async DNS resolver for EventMachine based on udns C library

Download as .zip Download as .tar.gz View on GitHub

Overview

EM-Udns is an async DNS resolver for EventMachine based on udns C library. Having most of the code written in C, EM-Udns becomes very fast. It can resolve DNS A, AAAA, PTR, MX, TXT, SRV and NAPTR records, and can handle every kind of errors (domain/record not found, request timeout, malformed response...).

C udns is a stub resolver, so also EM-Udns. This means that it must rely on a recursive name server, usually co-located in local host or local network. A very good choice is Unbound, a validating, recursive and caching DNS resolver.

Usage Example

require "em-udns"

EM.run do
  # Set the nameserver rather than using /etc/resolv.conf.
  EM::Udns.nameservers = "127.0.0.1"

  resolver = EM::Udns::Resolver.new
  EM::Udns.run resolver

  query = resolver.submit_A "google.com"

  query.callback do |result|
    puts "result => #{result.inspect}"
  end

  query.errback do |error|
    puts "error => #{error.inspect}"
  end
end

It would produce following output:

result => ["209.85.227.105", "209.85.227.103", "209.85.227.104", "209.85.227.106", "209.85.227.99", "209.85.227.147"]

Setting the Nameservers

EM::Udns.nameservers = nameservers

This class method set the nameservers list to use for all the EM::Udns::Resolver instances. If not used, nameservers are taken from /etc/resolv.conf (default behavior). nameserver parameter can be:

IMPORTANT: This class method must be used before initializing any EM::Udns::Resolver instance.

NOTE: Nameservers must be IPv4 addresses since udns does not listen in IPv6.

Example 1:

EM::Udns.nameservers = "127.0.0.1"

Example 2:

EM::Udns.nameservers = ["192.168.100.1", "192.168.100.2"]

Initializing a Resolver

resolver = EM::Udns::Resolver.new

Returns a EM::Udns::Resolver instance. If there is an error an exception EM::Udns::UdnsError is raised.

Runnig a Resolver

EM::Udns.run resolver

Attaches the UDP socket of the resolver to EventMachine. This method must be called after EventMachine is running.

Async DNS Queries

resolver.submit_XXX(parameters)

DNS queries are performed by invoking EM::Udns::Resolver#submit_XXX(parameters) methods on the resolver. The complete list of submit_XXX methods are shown below. These methods return a EM::Udns::Query instance. Callback and errback can then be assigned to the Query object via the callback and errback methods which accept a code block as single argument.

In case of success, the callback code block is invoked on the EM::Udns::Query object passing the DNS result object as single argument. Definition of those objects are shown below.

In case of error, the errback code block is invoked with the exact error as single argument, which is a Ruby Symbol:

Type Specific Queries

A Record

resolver.submit_A(domain)

In case of success the callback is invoked passing as argument an array of String objects. Each String represents an IPv4 address.

Example:

resolver.submit_A "google.com"

Callback is called with argument:

["209.85.227.105",
 "209.85.227.103",
 "209.85.227.104",
 "209.85.227.106",
 "209.85.227.99",
 "209.85.227.147"]

AAAA Record

resolver.submit_AAAA(domain)

In case of success the callback is invoked passing as argument an array of String objects. Each String represents an IPv6 address.

Example:

resolver.submit_AAAA "sixxs.net"

Callback is called with argument:

["2001:838:2:1::30:67",
 "2001:838:2:1:2a0:24ff:feab:3b53",
 "2001:960:800::2",
 "2001:1af8:4050::2"]

MX Record

resolver.submit_MX(domain)

In case of success the callback is invoked passing as argument an array of EM::Udns::RR_MX objects. Such object contains the following attribute readers:

Example:

resolver.submit_MX "gmail.com"

Callback is called with argument:

[#<EventMachine::Udns::RR_MX:0x00000002289090 @domain="alt1.gmail-smtp-in.l.google.com", @priority=10>,
 #<EventMachine::Udns::RR_MX:0x00000002288e60 @domain="alt3.gmail-smtp-in.l.google.com", @priority=30>,
 #<EventMachine::Udns::RR_MX:0x000000022886e0 @domain="gmail-smtp-in.l.google.com", @priority=5>,
 #<EventMachine::Udns::RR_MX:0x00000002288618 @domain="alt2.gmail-smtp-in.l.google.com", @priority=20>,
 #<EventMachine::Udns::RR_MX:0x000000022883c0 @domain="alt4.gmail-smtp-in.l.google.com", @priority=40>]

PTR Record

resolver.submit_PTR(ip)

Argument ip must be a String representing a IPv4 or IPv6. In case of success the callback is invoked passing as argument an array of String objects. Each String represents a domain.

Example 1:

resolver.submit_PTR "8.8.8.8"

Callback is called with argument:

["google-public-dns-a.google.com"]

Example 2:

resolver.submit_PTR "2001:838:2:1:2a0:24ff:feab:3b53"

Callback is called with argument:

["tunnelserver.concepts-ict.net"]

TXT Record

resolver.submit_TXT(domain)

In case of success the callback is invoked passing as argument an array of String objects. Each String represents a text entry in the TXT result.

Example:

resolver.submit_TXT "gmail.com"

Callback is called with argument:

["v=spf1 redirect=_spf.google.com"]

SRV Record

resolver.submit_SRV(domain)
resolver.submit_SRV(domain, service, protocol)

There are two ways to perform a SRV query:

In case of success the callback is invoked passing as argument an array of EM::Udns::RR_SRV objects. Such object contains the following attribute readers:

For more information about these fields check RFC 2782.

Example:

resolver.submit_SRV "_sip._tcp.oversip.net"

or:

resolver.submit_SRV "oversip.net", "sip", "tcp"

Callback is called with argument:

[#<EventMachine::Udns::RR_SRV:0x00000001ea4970 @domain="sip1.oversip.net", @priority=1, @weight=50, @port=5062>,
 #<EventMachine::Udns::RR_SRV:0x00000001ea4b50 @domain="sip2.oversip.net", @priority=2, @weight=50, @port=5060>]

NAPTR Record

resolver.submit_NAPTR(domain)

In case of success the callback is invoked passing as argument an array of EM::Udns::RR_NAPTR objects. Such object contains the following attribute readers:

For more information about these fields check RFC 2915.

Example:

resolver.submit_NAPTR "oversip.net"

Callback is called with argument:

[#<EventMachine::Udns::RR_NAPTR:0x00000002472aa0 @order=30, @preference=50, @flags="S", @service="SIPS+D2T", @regexp=nil, @replacement="_sips._tcp.oversip.net">,
 #<EventMachine::Udns::RR_NAPTR:0x00000002472848 @order=40, @preference=50, @flags="S", @service="SIP+D2S", @regexp=nil, @replacement="_sip._sctp.oversip.net">,
 #<EventMachine::Udns::RR_NAPTR:0x000000024723e8 @order=10, @preference=50, @flags="S", @service="SIP+D2T", @regexp=nil, @replacement="_sip._tcp.oversip.net">,
 #<EventMachine::Udns::RR_NAPTR:0x00000002471d80 @order=20, @preference=50, @flags="S", @service="SIP+D2U", @regexp=nil, @replacement="_sip._udp.oversip.net">]

Other Features

Number of Active Queries

resolver.active

EM::Udns::Resolver#active returns the number of pending queries of resolver as a Fixnum.

Cancelling a Pending Query

resolver.cancel query

EM::Udns::Resolver#cancel(query) cancels the EM::Udns::Query given as argument so no callback/errback would be called upon query completion.

Installation

EM-Udns is provided as a Ruby Gem:

~$ gem install em-udns

Supported Platforms

EM-Udns is tested under the following platforms:

See also the extconf.rb file which compiles udns C library according to current platform.

TODO

Acknowledgement

Many thanks to Michael Tokarev (the author of udns C library) for all the help provided in udns mailing list.