Class Dnsruby::Resolver
In: lib/Dnsruby/Resolver.rb
Parent: Object

Description

Dnsruby::Resolver is a DNS stub resolver. This class uses a set of SingleResolvers to perform queries with retries across multiple nameservers.

The retry policy is a combination of the Net::DNS and dnsjava approach, and has the option of :

  • A total timeout for the query (defaults to 0, meaning "no total timeout")
  • A retransmission system that targets the namervers concurrently once the first query round is complete, but in which the total time per query round is split between the number of nameservers targetted for the first round. and total time for query round is doubled for each query round
 Note that, if a total timeout is specified, then that will apply regardless of the retry policy

(i.e. it may cut retries short).

 Note also that these timeouts are distinct from the SingleResolver's packet_timeout

Methods

Synchronous

These methods raise an exception or return a response message with rcode==NOERROR

Asynchronous

These methods use a response queue to return the response and the error

Event Loop

Dnsruby runs a pure Ruby event loop to handle I/O in a single thread. It is also possible to configure Dnsruby to use EventMachine instead. See the Dnsruby::Resolver::use_eventmachine method for details.

Note that, if using Dnsruby from an EventMachine loop, you will need to tell Dnsruby not to start the event loop itself :

   Dnsruby::Resolver::use_eventmachine(true)
   Dnsruby::Resolver::start_eventmachine_loop(false)

Methods

Constants

DefaultQueryTimeout = 0
DefaultPacketTimeout = 10
DefaultRetryTimes = 4
DefaultRetryDelay = 5
DefaultPort = 53
DefaultUDPSize = 512

Attributes

config  [R]  The current Config
ignore_truncation  [R]  Should truncation be ignored? i.e. the TC bit is ignored and thus the resolver will not requery over TCP if TC is set
packet_timeout  [R]  The timeout for any individual packet. This is the timeout used by SingleResolver
persistent_tcp  [R]  Should TCP queries be sent on a persistent socket?
persistent_udp  [R]  Should UDP queries be sent on a persistent socket?
port  [R]  The port to send queries to on the resolver
query_timeout  [RW]  Note that this timeout represents the total time a query may run for - multiple packets can be sent to multiple nameservers in this time. This is distinct from the SingleResolver per-packet timeout The query_timeout is not required - it will default to 0, which means "do not use query_timeout". If this is the case then the timeout will be dictated by the retry_times and retry_delay attributes
recurse  [R]  Should the Recursion Desired bit be set?
retry_delay  [RW]  The query will be tried across nameservers retry_times times, with a delay of retry_delay seconds between each retry. The first time round, retry_delay will be divided by the number of nameservers being targetted, and a new nameserver will be queried with the resultant delay.
retry_times  [RW]  The query will be tried across nameservers retry_times times, with a delay of retry_delay seconds between each retry. The first time round, retry_delay will be divided by the number of nameservers being targetted, and a new nameserver will be queried with the resultant delay.
single_resolvers  [R]  The array of SingleResolvers used for sending query messages
src_address  [R]  The source address to send queries from
src_port  [R]  The source port to send queries from
tsig  [R] 
udp_size  [R]  The maximum UDP size to be used
use_tcp  [R]  Should TCP be used as a transport rather than UDP?

Public Class methods

Check whether EventMachine will be used by Dnsruby

Create a new Resolver object. If no parameters are passed in, then the default system configuration will be used. Otherwise, a Hash may be passed in with the following optional elements :

  • :port
  • :use_tcp
  • :tsig
  • :ignore_truncation
  • :src_address
  • :src_port
  • :persistent_tcp
  • :persistent_udp
  • :recurse
  • :udp_size
  • :config_info - see Config
  • :nameserver - can be either a String or an array of Strings
  • :packet_timeout
  • :query_timeout
  • :retry_times
  • :retry_delay

If EventMachine is being used, then this method tells Dnsruby whether or not to start the EventMachine loop. If you want to use Dnsruby client code as is, but using EventMachine for I/O, then Dnsruby must start the EventMachine loop for you. This is the default behaviour. If you want to use EventMachine-style code, where everything is wrapped up in an EventMachine::run{} call, then this method should be called with false as the parameter.

Takes a bool argument to say whether or not to start the event loop when required.

Checks whether Dnsruby will start the EventMachine loop when required.

Tell Dnsruby to use EventMachine for I/O.

If EventMachine is not used, then the pure Ruby event loop in Dnsruby will be used instead.

If EventMachine is not available on the platform, then a RuntimeError will be raised.

Takes a bool to say whether or not to use EventMachine.

Public Instance methods

Add a new SingleResolver to the list of resolvers this Resolver object will query.

Close the Resolver. Unfinished queries are terminated with OtherResolvError.

Query for a n. If a valid Message is received, then it is returned to the caller. Otherwise an exception (a Dnsruby::ResolvError or Dnsruby::ResolvTimeout) is raised.

  require 'Dnsruby'
  res = Dnsruby::Resolver.new
  response = res.query("example.com") # defaults to Types.A, Classes.IN
  response = res.query("example.com", Types.MX)
  response = res.query("208.77.188.166") # IPv4 address so PTR query will be made
  response = res.query("208.77.188.166", Types.PTR)

Asynchronously send a Message to the server. The send can be done using just Dnsruby, or using EventMachine.

Dnsruby pure Ruby event loop :

A client_queue is supplied by the client, along with an optional client_query_id to identify the response. The client_query_id is generated, if not supplied, and returned to the client. When the response is known, a tuple of (query_id, response_message, exception) will be added to the client_queue.

The query is sent synchronously in the caller‘s thread. The select thread is then used to listen for and process the response (up to pushing it to the client_queue). The client thread is then used to retrieve the response and deal with it.

Takes :

  • msg - the message to send
  • client_queue - a Queue to push the response to, when it arrives
  • client_query_id - an optional ID to identify the query to the client
  • use_tcp - whether to use TCP (defaults to SingleResolver.use_tcp)

Returns :

  • client_query_id - to identify the query response to the client. This ID is

generated if it is not passed in by the client

Example invocations :

    id = res.send_async(msg, queue)
    NOT SUPPORTED : id = res.send_async(msg, queue, use_tcp)
    id = res.send_async(msg, queue, id)
    id = res.send_async(msg, queue, id, use_tcp)

Example code :

   require 'Dnsruby'
   res = Dnsruby::Resolver.new
   query_id = 10 # can be any object you like
   query_queue = Queue.new
   res.send_async(Message.new("example.com", Types.MX),  query_queue, query_id)
   query_id_2 = res.send_async(Message.new("example.com", Types.A), query_queue)
   # ...do a load of other stuff here...
   2.times do
     response_id, response, exception = query_queue.pop
     # You can check the ID to see which query has been answered
     if (exception == nil)
         # deal with good response
     else
         # deal with problem
     end
   end

If EventMachine is being used :

If EventMachine is being used (see Dnsruby::Resolver::use_eventmachine, then this method returns an EM::Deferrable object. When the response is known, then the Deferrable will complete. If a queue (and ID) is passed in, then the response will also be pushed to the Queue. Note that an ID is not automatically generated by this version.

Example invocations :

    deferrable = res.send_async(msg)
    deferrable = res.send_async(msg, use_tcp)
    deferrable = res.send_async(msg, q, id, use_tcp)

Example code

  • Here is an example of using the code in an EventMachine style :
      require 'Dnsruby'
      require 'eventmachine'
      res = Dnsruby::Resolver.new
      Dnsruby::Resolver.use_eventmachine
      Dnsruby::Resolver.start_eventmachine_loop(false)
      EventMachine::run {
        df = res.send_async(Dnsruby::Message.new("example.com"))
        df.callback {|msg|
           puts "Response : #{msg}"
           EM.stop}
        df.errback {|msg, err|
           puts "Response : #{msg}"
           puts "Error: #{err}"
           EM.stop}
      }
    
  • And an example in a normal Dnsruby style :
      require 'Dnsruby'
      res = Dnsruby::Resolver.new
      Dnsruby::Resolver.use_eventmachine
      Dnsruby::Resolver.start_eventmachine_loop(true) # default
      q = Queue.new
      id = res.send_async(Dnsruby::Message.new("example.com"),q)
      id, response, error = q.pop
    

Send a message, and wait for the response. If a valid Message is received, then it is returned to the caller. Otherwise an exception (a Dnsruby::ResolvError or Dnsruby::ResolvTimeout) is raised.

send_async is called internally.

example :

  require 'Dnsruby'
  res = Dnsruby::Resolver.new
  begin
  response = res.send_message(Message.new("example.com", Types.MX))
  rescue ResolvError
    # ...
  rescue ResolvTimeout
    # ...
  end

Sets the TSIG to sign outgoing messages with. Pass in either a Dnsruby::RR::TSIG, or a key_name and key (or just a key) Pass in nil to stop tsig signing. It is possible for client code to sign packets prior to sending - see Dnsruby::RR::TSIG#apply and Dnsruby::Message#sign Note that pre-signed packets will not be signed by SingleResolver.

  • res.tsig=(tsig_rr)
  • res.tsig=(key_name, key)
  • res.tsig=nil # Stop the resolver from signing

[Validate]