Class: Dnsruby::Recursor

### Home Classes Methods

### In Files

- lib/Dnsruby/Recursor.rb

### Parent

Object

### Methods

- ::add_to_hints
- ::clear_caches
- ::new
- ::set_hints
- #hints=
- #prune_rrsets_to_rfc5452
- #query
- #recursion_callback
- #recursion_callback=

### Files

- DNSSEC
- EVENTMACHINE
- EXAMPLES
- README

### Class/Module Index

Quicksearch

- Dnsruby
- Dnsruby::Algorithms
- Dnsruby::Classes
- Dnsruby::CodeMapper
- Dnsruby::Config
- Dnsruby::DNS
- Dnsruby::DecodeError
- Dnsruby::Dnssec
- Dnsruby::Dnssec::ValidationPolicy
- Dnsruby::EncodeError
- Dnsruby::ExtendedRCode
- Dnsruby::FormErr
- Dnsruby::Header
- Dnsruby::Hosts
- Dnsruby::IPv4
- Dnsruby::IPv6
- Dnsruby::Message
- Dnsruby::Message::Section
- Dnsruby::Message::SecurityLevel
- Dnsruby::MetaTypes
- Dnsruby::Modes
- Dnsruby::NXDomain
- Dnsruby::NXRRSet
- Dnsruby::Name
- Dnsruby::Name::Label
- Dnsruby::NotAuth
- Dnsruby::NotImp
- Dnsruby::NotZone
- Dnsruby::Nsec3HashAlgorithms
- Dnsruby::OpCode
- Dnsruby::OtherResolvError
- Dnsruby::QTypes
- Dnsruby::Question
- Dnsruby::RCode
- Dnsruby::RR
- Dnsruby::RR::ANY
- Dnsruby::RR::CERT
- Dnsruby::RR::CERT::CertificateTypes
- Dnsruby::RR::CNAME
- Dnsruby::RR::DHCID
- Dnsruby::RR::DLV
- Dnsruby::RR::DNAME
- Dnsruby::RR::DNSKEY
- Dnsruby::RR::DS
- Dnsruby::RR::DS::DigestTypes
- Dnsruby::RR::DomainName
- Dnsruby::RR::Generic
- Dnsruby::RR::HINFO
- Dnsruby::RR::HIP
- Dnsruby::RR::IN
- Dnsruby::RR::IN::A
- Dnsruby::RR::IN::AAAA
- Dnsruby::RR::IN::AFSDB
- Dnsruby::RR::IN::PX
- Dnsruby::RR::IN::SRV
- Dnsruby::RR::IN::WKS
- Dnsruby::RR::IPSECKEY
- Dnsruby::RR::ISDN
- Dnsruby::RR::KX
- Dnsruby::RR::LOC
- Dnsruby::RR::MB
- Dnsruby::RR::MG
- Dnsruby::RR::MINFO
- Dnsruby::RR::MR
- Dnsruby::RR::MX
- Dnsruby::RR::NAPTR
- Dnsruby::RR::NS
- Dnsruby::RR::NSAP
- Dnsruby::RR::NSEC
- Dnsruby::RR::NSEC3
- Dnsruby::RR::NSEC3PARAM
- Dnsruby::RR::OPT
- Dnsruby::RR::PTR
- Dnsruby::RR::RP
- Dnsruby::RR::RRSIG
- Dnsruby::RR::RT
- Dnsruby::RR::SOA
- Dnsruby::RR::SPF
- Dnsruby::RR::SSHFP
- Dnsruby::RR::SSHFP::Algorithms
- Dnsruby::RR::SSHFP::FpTypes
- Dnsruby::RR::TKEY
- Dnsruby::RR::TSIG
- Dnsruby::RR::TXT
- Dnsruby::RR::X25
- Dnsruby::RRSet
- Dnsruby::Recursor
- Dnsruby::Refused
- Dnsruby::Resolv
- Dnsruby::ResolvError
- Dnsruby::ResolvTimeout
- Dnsruby::Resolver
- Dnsruby::Resolver::EventType
- Dnsruby::SelectThread
- Dnsruby::ServFail
- Dnsruby::SingleResolver
- Dnsruby::TheLog
- Dnsruby::TsigError
- Dnsruby::TsigNotSignedResponseError
- Dnsruby::Types
- Dnsruby::Update
- Dnsruby::VerifyError
- Dnsruby::YXDomain
- Dnsruby::YXRRSet
- Dnsruby::ZoneReader
- Dnsruby::ZoneReader::ParseException
- Dnsruby::ZoneSerialError
- Dnsruby::ZoneTransfer
- Dnsruby::ZoneTransfer::Delta
- Base32

No matching classes.

# Dnsruby::Recursor

Dnsruby::Recursor - Perform recursive dns lookups

    require 'Dnsruby'
    rec = Dnsruby::Recursor.new()
    answer = rec.recurse("rob.com.au")

This module uses a Dnsruby::Resolver to perform recursive queries.

### AUTHOR

Rob Brown, bbb@cpan.org Alex Dalitz, alexd@nominet.org.uk

### SEE ALSO

Dnsruby::Resolver,

### COPYRIGHT

Copyright (c) 2002, Rob Brown. All rights reserved. Portions Copyright (c) 2005, Olaf M Kolkman. Ruby version with caching and validation Copyright (c) 2008, AlexD (Nominet UK)

Example lookup process:

[root@box root]# dig +trace www.rob.com.au.

; <<>> DiG 9.2.0 <<>> +trace www.rob.com.au. ;; global options: printcmd . 507343 IN NS C.ROOT-SERVERS.NET. . 507343 IN NS D.ROOT-SERVERS.NET. . 507343 IN NS E.ROOT-SERVERS.NET. . 507343 IN NS F.ROOT-SERVERS.NET. . 507343 IN NS G.ROOT-SERVERS.NET. . 507343 IN NS H.ROOT-SERVERS.NET. . 507343 IN NS I.ROOT-SERVERS.NET. . 507343 IN NS J.ROOT-SERVERS.NET. . 507343 IN NS K.ROOT-SERVERS.NET. . 507343 IN NS L.ROOT-SERVERS.NET. . 507343 IN NS M.ROOT-SERVERS.NET. . 507343 IN NS A.ROOT-SERVERS.NET. . 507343 IN NS B.ROOT-SERVERS.NET. ;; Received 436 bytes from 127.0.0.1#53(127.0.0.1) in 9 ms

    ;;; But these should be hard coded as the hints

    ;;; Ask H.ROOT-SERVERS.NET gave:

au. 172800 IN NS NS2.BERKELEY.EDU. au. 172800 IN NS NS1.BERKELEY.EDU. au. 172800 IN NS NS.UU.NET. au. 172800 IN NS BOX2.AUNIC.NET. au. 172800 IN NS SEC1.APNIC.NET. au. 172800 IN NS SEC3.APNIC.NET. ;; Received 300 bytes from 128.63.2.53#53(H.ROOT-SERVERS.NET) in 322 ms

    ;;; A little closer than before

    ;;; Ask NS2.BERKELEY.EDU gave:

com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS audns01.syd.optus.net. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. ;; Received 387 bytes from 128.32.206.12#53(NS2.BERKELEY.EDU) in 10312 ms

    ;;; A little closer than before

    ;;; Ask ns4.ausregistry.net gave:

com.au. 259200 IN NS ns1.ausregistry.net. com.au. 259200 IN NS ns2.ausregistry.net. com.au. 259200 IN NS ns3.ausregistry.net. com.au. 259200 IN NS ns4.ausregistry.net. com.au. 259200 IN NS ns3.melbourneit.com. com.au. 259200 IN NS dns1.telstra.net. com.au. 259200 IN NS au2ld.CSIRO.au. com.au. 259200 IN NS ns.ripe.net. com.au. 259200 IN NS audns01.syd.optus.net. ;; Received 259 bytes from 137.39.1.3#53(ns4.ausregistry.net) in 606 ms

    ;;; Uh... yeah... I already knew this
    ;;; from what NS2.BERKELEY.EDU told me.
    ;;; ns4.ausregistry.net must have brain damage

    ;;; Ask ns1.ausregistry.net gave:

rob.com.au. 86400 IN NS sy-dns02.tmns.net.au. rob.com.au. 86400 IN NS sy-dns01.tmns.net.au. ;; Received 87 bytes from 203.18.56.41#53(ns1.ausregistry.net) in 372 ms

    ;;; Ah, much better.  Something more useful.

    ;;; Ask sy-dns02.tmns.net.au gave:

www.rob.com.au. 7200 IN A 139.134.5.123 rob.com.au. 7200 IN NS sy-dns01.tmns.net.au. rob.com.au. 7200 IN NS sy-dns02.tmns.net.au. ;; Received 135 bytes from 139.134.2.18#53(sy-dns02.tmns.net.au) in 525 ms

     ;;; FINALLY, THE ANSWER!
    Now,DNSSEC validation is performed (unless disabled).

### Attributes

callback[RW]
hints[R]
ipv6_ok[RW]
nameservers[RW]
recurse[RW]
resolver[RW]

The resolver to use for the queries

### Public Class Methods

add_to_hints(hints, rr) click to toggle source

    # File lib/Dnsruby/Recursor.rb, line 325
    def Recursor.add_to_hints(hints, rr)
      server = rr.name.to_s.downcase
      server.sub!(/\.$/,"")
      if (server)
        if ( rr.type == Types::A)
          #print ";; ADDITIONAL HELP: $server -> [".$rr->rdatastr."]\n" if $self->{'debug'};
          if (hints[server]!=nil)
            TheLog.debug(";; STORING IP: #{server} IN A "+rr.address.to_s+"\n")
            hints[server].push([rr.address.to_s, rr.ttl])
          end
        end
        if ( rr.type == Types::AAAA)
          #print ";; ADDITIONAL HELP: $server -> [".$rr->rdatastr."]\n" if $self->{'debug'};
          if (hints[server])
            TheLog.debug(";; STORING IP6: #{server} IN AAAA "+rr.address.to_s+"\n")
            hints[server].push([rr.address.to_s, rr.ttl])
          end
        end

      end
    end

clear_caches(resolver = Resolver.new) click to toggle source

    # File lib/Dnsruby/Recursor.rb, line 370
    def Recursor.clear_caches(resolver = Resolver.new)
      Recursor.set_hints(Hash.new, resolver)
      @@zones_cache = Hash.new # key zone_name, values Hash of servers and AddressCaches
      @@zones_cache["."] = @@hints

      @@authority_cache = Hash.new
    end

new(res = nil) click to toggle source

    # File lib/Dnsruby/Recursor.rb, line 167
    def initialize(res = nil)
      if (res)
        @resolver = res
      else
        if (defined?@@nameservers && @@nameservers.length > 0)
          @resolver = Resolver.new({:nameserver => @@nameservers})
        else
          @resolver = Resolver.new
        end
      end
      @ipv6_ok = false
    end

set_hints(hints, resolver) click to toggle source

    # File lib/Dnsruby/Recursor.rb, line 194
    def Recursor.set_hints(hints, resolver)
      TheLog.debug(";; hints(#{hints.inspect})\n")
      @resolver = resolver
      if (resolver.single_resolvers.length == 0)
        resolver = Resolver.new()
      end
      if (hints && hints.length > 0)
        resolver.nameservers=hints
        if (String === hints)
          hints = [hints]
        end
        hints.each {|hint|
          @@hints = Hash.new
          @@hints[hint]=hint
        }
      end
      if (!hints && @@nameservers)
        @@hints=(@@nameservers)
      else
        @@nameservers=(hints)
        @@hints = hints
      end
      TheLog.debug(";; verifying (root) zone...\n")
      # bind always asks one of the hint servers
      # for who it thinks is authoritative for
      # the (root) zone as a sanity check.
      # Nice idea.

      #      if (!@@hints || @@hints.length == 0)
      resolver.recurse=(1)
      packet=resolver.query_no_validation_or_recursion(".", "NS", "IN")
      hints = Hash.new
      if (packet)
        if (ans = packet.answer)
          ans.each do |rr|
            if (rr.name.to_s =~ /^\.?$/ and
                  rr.type == Types::NS)
              # Found root authority
              server = rr.nsdname.to_s.downcase
              server.sub!(/\.$/,"")
              TheLog.debug(";; FOUND HINT: #{server}\n")
              hints[server] = AddressCache.new
            end
          end
          if ((packet.additional.length == 0) ||
                 ((packet.additional.length == 1) && (packet.additional()[0].type == Types.OPT)))
            # Some resolvers (e.g. 8.8.8.8) do not send an additional section -
            # need to make explicit queries for these :(
            # Probably best to limit the number of outstanding queries - extremely bursty behaviour otherwise
            # What happens if we select only name
            q = Queue.new
            hints.keys.each {|server|
              # Query for the server address and add it to hints.
              ['A', 'AAAA'].each {|type|
                msg = Message.new
                msg.do_caching = @do_caching
                msg.header.rd = false
                msg.do_validation = false
                msg.add_question(server, type, 'IN')
                if (@dnssec)
                  msg.header.cd = true # We do our own validation by default
                end
                resolver.send_async(msg, q)
              }
            }
            (hints.length * 2).times {
              id, result, error = q.pop
              if (result)
                result.answer.each {|rr|
                  TheLog.debug(";; NS address: " + rr.inspect+"\n")
                  add_to_hints(hints, rr)
                }
              end
            }
          else
            packet.additional.each do |rr|
              TheLog.debug(";; ADDITIONAL: "+rr.inspect+"\n")
              add_to_hints(hints, rr)

            end
          end
        end
        #                      foreach my $server (keys %hints) {
        hints.keys.each do |server|
          if (!hints[server] || hints[server].length == 0)
            # Wipe the servers without lookups
            hints.delete(server)
          end
        end
        @@hints = hints
      else
        @@hints = {}
      end
      if (@@hints.size > 0)
        TheLog.info(";; USING THE FOLLOWING HINT IPS:\n")
        @@hints.values.each do |ips|
          ips.each do |server|
            TheLog.info(";;  #{server}\n")
          end
        end
      else
        raise ResolvError.new( "Server ["+(@@nameservers)[0].to_s+".] did not give answers")
      end

      # Disable recursion flag.
      resolver.recurse=(0)
      #      end

      #  return $self->nameservers( map { @{ $_ } } values %{ $self->{'hints'} } );
      if (Array === @@hints)
        temp = []
        @@hints.each {|hint|
          temp.push(hint)
        }
        @@hints = Hash.new
        count = 0
        temp.each {|hint|
          print "Adding hint : #{temp[count]}\n"
          @@hints[count] = temp[count]
          count += 1
        }
      end
      if (String === @@hints)
        temp = @@hints
        @@hints = Hash.new
        @@hints[0] = temp
      end
      @@nameservers = @@hints.values
      return @@nameservers
    end

### Public Instance Methods

hints=(hints) click to toggle source

Initialize the hint servers. Recursive queries need a starting name server to work off of. This method takes a list of IP addresses to use as the starting servers. These name servers should be authoritative for the root (.) zone.

    res.hints=(ips)

If no hints are passed, the default nameserver is asked for the hints. Normally these IPs can be obtained from the following location:

    ftp://ftp.internic.net/domain/named.root

    # File lib/Dnsruby/Recursor.rb, line 191
    def hints=(hints)
      Recursor.set_hints(hints, @resolver)
    end

prune_rrsets_to_rfc5452(packet, zone) click to toggle source

    # File lib/Dnsruby/Recursor.rb, line 729
    def prune_rrsets_to_rfc5452(packet, zone)
      # Now prune the response of any unrelated rrsets (RFC5452 section6)
      # "One very simple way to achieve this is to only accept data if it is
      # part of the domain for which the query was intended."
      if (!packet.header.aa)
        return
      end
      if (!packet.question()[0])
        return
      end

      section_rrsets = packet.section_rrsets
      section_rrsets.keys.each {|section|
        section_rrsets[section].each {|rrset|
          n = Name.create(rrset.name)
          n.absolute = true
          if ((n.to_s == zone) || (n.to_s == Name.create(zone).to_s) ||
                (n.subdomain_of?(Name.create(zone))) ||
                (rrset.type == Types::OPT))
            #            # @TODO@ Leave in the response if it is an SOA, NSEC or RRSIGfor the parent zone
            ##          elsif ((query_name.subdomain_of?rrset.name) &&
            #          elsif  ((rrset.type == Types.SOA) || (rrset.type == Types.NSEC) || (rrset.type == Types.NSEC3)) #)
          else
            TheLog.debug"Removing #{rrset.name}, #{rrset.type} from response from server for #{zone}"
            packet.send(section).remove_rrset(rrset.name, rrset.type)
          end
        }
      }
    end

query(name, type=Types.A, klass=Classes.IN, no_validation = false) click to toggle source

This method is much like the normal query() method except it disables the recurse flag in the packet and explicitly performs the recursion.

    packet = res.query( "www.netscape.com.", "A")
    packet = res.query( "www.netscape.com.", "A", "IN", true) # no validation

The Recursor maintains a cache of known nameservers. DNSSEC validation is performed unless true is passed as the fourth parameter.

    # File lib/Dnsruby/Recursor.rb, line 390
    def query(name, type=Types.A, klass=Classes.IN, no_validation = false)
      # @TODO@ PROVIDE AN ASYNCHRONOUS SEND WHICH RETURNS MESSAGE WITH ERROR!!!

      # Make sure the hint servers are initialized.
      @@mutex.synchronize {
        self.hints=(Hash.new) unless @@hints
      }
      @resolver.recurse=(0)
      # Make sure the authority cache is clean.
      # It is only used to store A and AAAA records of
      # the suposedly authoritative name servers.
      # TTLs are respected
      @@mutex.synchronize {
        if (!@@zones_cache)
          Recursor.clear_caches(@resolver)
        end
      }

      # So we have normal hashes, but the array of addresses at the end is now an AddressCache
      # which respects the ttls of the A/AAAA records

      # Now see if we already know the zone in question
      # Otherwise, see if we know any of its parents (will know at least ".")
      known_zone, known_authorities = get_closest_known_zone_authorities_for(name) # ".", @hints if nothing else

      # Seed name servers with the closest known authority
      #      ret =  _dorecursion( name, type, klass, ".", @hints, 0)
      ret =  _dorecursion( name, type, klass, known_zone, known_authorities, 0, no_validation)
      Dnssec.validate(ret) if !no_validation
      #      print "\n\nRESPONSE:\n#{ret}\n"
      return ret
    end

recursion_callback() click to toggle source

    # File lib/Dnsruby/Recursor.rb, line 366
    def recursion_callback
      return @callback
    end

recursion_callback=(sub) click to toggle source

This method takes a code reference, which is then invoked each time a packet is received during the recursive lookup. For example to emulate dig's C<+trace> function:

    res.recursion_callback(Proc.new { |packet|
        print packet.additional.inspect

        print";; Received %d bytes from %s\n\n",
            packetanswersize,
            packet.answerfrom);
    })

    # File lib/Dnsruby/Recursor.rb, line 360
    def recursion_callback=(sub)
      #          if (sub && UNIVERSAL::isa(sub, 'CODE'))
      @callback = sub
      #          end
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.