### Home Classes Methods

### In Files

- lib/cool.io/dns_resolver.rb

### Parent

IOWatcher

### Namespace

- CLASS Coolio::DNSResolver::Timeout

### Methods

- ::hosts
- ::new
- #attach
- #detach
- #on_failure
- #on_readable
- #on_success
- #on_timeout
- #request_message
- #request_question
- #response_address
- #send_request

### Class/Module Index

Quicksearch

- Coolio
- Coolio::AsyncWatcher
- Coolio::Connections
- Coolio::DNSResolver
- Coolio::DNSResolver::Timeout
- Coolio::DSL
- Coolio::DSL::ConnectionBuilder
- Coolio::IO
- Coolio::IO::Watcher
- Coolio::IOWatcher
- Coolio::Listener
- Coolio::Loop
- Coolio::Meta
- Coolio::Server
- Coolio::Socket
- Coolio::Socket::Connector
- Coolio::TCPListener
- Coolio::TCPServer
- Coolio::TCPSocket
- Coolio::TCPSocket::TCPConnectResolver
- Coolio::TCPSocket::TCPConnectSocket
- Coolio::TimerWatcher
- Coolio::UNIXListener
- Coolio::UNIXServer
- Coolio::UNIXSocket
- Cool
- Cool::Coolness
- Cool::IOThunk
- Object
- Thread

No matching classes.

# Coolio::DNSResolver

A non-blocking DNS resolver. It provides interfaces for querying both /etc/hosts and nameserves listed in /etc/resolv.conf, or nameservers of your choosing.

Presently the client only supports UDP requests against your nameservers and cannot resolve anything with records larger than 512-bytes. Also, IPv6 is not presently supported.

DNSResolver objects are one-shot. Once they resolve a domain name they automatically detach themselves from the event loop and cannot be used again.

### Constants

DATAGRAM_SIZE
DNS_PORT
RETRIES
TIMEOUT

### Public Class Methods

hosts(host, hostfile = Resolv::Hosts::DefaultFileName) click to toggle source

so currently total is 12s before it will err due to timeouts if it errs due to inability to reach the DNS server [Errno::EHOSTUNREACH], same Query /etc/hosts (or the specified hostfile) for the given host

    # File lib/cool.io/dns_resolver.rb, line 43
    def self.hosts(host, hostfile = Resolv::Hosts::DefaultFileName)
      hosts = {}
      File.open(hostfile) do |f|
        f.each_line do |host_entry|
          entries = host_entry.gsub(/#.*$/, '').gsub(/\s+/, ' ').split(' ')
          addr = entries.shift
          entries.each { |e| hosts[e] ||= addr }
        end
      end

      hosts[host]
    end

new(hostname, *nameservers) click to toggle source

Create a new Coolio::Watcher descended object to resolve the given hostname. If you so desire you can also specify a list of nameservers to query. By default the resolver will use nameservers listed in /etc/resolv.conf

    # File lib/cool.io/dns_resolver.rb, line 60
    def initialize(hostname, *nameservers)
      if nameservers.empty?
        nameservers = Resolv::DNS::Config.default_config_hash[:nameserver]
        raise RuntimeError, "no nameservers found" if nameservers.empty? # TODO just call resolve_failed, not raise [also handle Errno::ENOENT)]
      end

      @nameservers = nameservers
      @question = request_question hostname

      @socket = UDPSocket.new
      @timer = Timeout.new(self)

      super(@socket)
    end

### Public Instance Methods

attach(evloop) click to toggle source

Attach the DNSResolver to the given event loop

    # File lib/cool.io/dns_resolver.rb, line 76
    def attach(evloop)
      send_request
      @timer.attach(evloop)
      super
    end

detach() click to toggle source

Detach the DNSResolver from the given event loop

    # File lib/cool.io/dns_resolver.rb, line 83
    def detach
      @timer.detach if @timer.attached?
      super
    end

on_failure() click to toggle source

Called when we receive a response indicating the name didn't resolve

    # File lib/cool.io/dns_resolver.rb, line 93
    def on_failure; end

on_success(address) click to toggle source

Called when the name has successfully resolved to an address

    # File lib/cool.io/dns_resolver.rb, line 89
    def on_success(address); end

on_timeout() click to toggle source

Called if we don't receive a response, defaults to calling on_failure

    # File lib/cool.io/dns_resolver.rb, line 97
    def on_timeout
      on_failure
    end

### Protected Instance Methods

on_readable() click to toggle source

Called by the subclass when the DNS response is available

    # File lib/cool.io/dns_resolver.rb, line 117
    def on_readable
      datagram = nil
      begin
        datagram = @socket.recvfrom_nonblock(DATAGRAM_SIZE).first
      rescue Errno::ECONNREFUSED
      end

      address = response_address datagram rescue nil
      address ? on_success(address) : on_failure
      detach
    end

request_message() click to toggle source

    # File lib/cool.io/dns_resolver.rb, line 144
    def request_message
      # Standard query header
      message = [2, 1, 0].pack('nCC')

      # One entry
      qdcount = 1

      # No answer, authority, or additional records
      ancount = nscount = arcount = 0

      message << [qdcount, ancount, nscount, arcount].pack('nnnn')
      message << @question
    end

request_question(hostname) click to toggle source

    # File lib/cool.io/dns_resolver.rb, line 129
    def request_question(hostname)
      raise ArgumentError, "hostname cannot be nil" if hostname.nil?

      # Query name
      message = hostname.split('.').map { |s| [s.size].pack('C') << s }.join + "\00""

      # Host address query
      qtype = 1

      # Internet query
      qclass = 1

      message << [qtype, qclass].pack('nn')
    end

response_address(message) click to toggle source

    # File lib/cool.io/dns_resolver.rb, line 158
    def response_address(message)
      # Confirm the ID field
      id = message[0..1].unpack('n').first.to_i
      return unless id == 2

      # Check the QR value and confirm this message is a response
      qr = message[2..2].unpack('B1').first.to_i
      return unless qr == 1

      # Check the RCODE (lower nibble) and ensure there wasn't an error
      rcode = message[3..3].unpack('B8').first[4..7].to_i(2)
      return unless rcode == 0

      # Extract the question and answer counts
      qdcount, ancount = message[4..7].unpack('nn').map { |n| n.to_i }

      # We only asked one question
      return unless qdcount == 1
      message.slice!(0, 12)

      # Make sure it's the same question
      return unless message[0..(@question.size-1)] == @question
      message.slice!(0, @question.size)

      # Extract the RDLENGTH
      while not message.empty?
        type = message[2..3].unpack('n').first.to_i
        rdlength = message[10..11].unpack('n').first.to_i
        rdata = message[12..(12 + rdlength - 1)]
        message.slice!(0, 12 + rdlength)

        # Only IPv4 supported
        next unless rdlength == 4

        # If we got an Internet address back, return it
        return rdata.unpack('CCCC').join('.') if type == 1
      end

      nil
    end

send_request() click to toggle source

Send a request to the DNS server

    # File lib/cool.io/dns_resolver.rb, line 106
    def send_request
      nameserver = @nameservers.shift
      @nameservers << nameserver # rotate them
      @socket.connect @nameservers.first, DNS_PORT
      begin
        @socket.send request_message, 0
      rescue Errno::EHOSTUNREACH # TODO figure out why it has to be wrapper here, when the other wrapper should be wrapping this one!
      end
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.