rapid7/metasploit-framework

View on GitHub
lib/rex/proto/dns/cached_resolver.rb

Summary

Maintainability
A
1 hr
Test Coverage
# -*- coding: binary -*-

require 'net/dns/resolver'

module Rex
module Proto
module DNS

  ##
  # Provides Rex::Sockets compatible version of Net::DNS::Resolver
  # Modified to work with Dnsruby::Messages, their resolvers are too heavy
  ##
  class CachedResolver < Resolver
    include Rex::Proto::DNS::Constants
    attr_accessor :cache

    #
    # Initialize resolver with cache
    #
    # @param config [Hash] Resolver config
    #
    # @return [nil]
    def initialize(config = {})
      dns_cache_no_start = config.delete(:dns_cache_no_start)
      super(config)
      self.cache = Rex::Proto::DNS::Cache.new
      self.cache.start unless dns_cache_no_start
      return
    end

    #
    # Attempt to find answers to query in DNS cache; failing that,
    # send remainder of DNS queries over appropriate transport and
    # cache answers before returning to caller.
    #
    # @param argument [Object] An object holding the DNS message to be processed.
    # @param type [Fixnum] Type of record to look up
    # @param cls [Fixnum] Class of question to look up
    #
    # @return [Dnsruby::Message, nil] DNS response on success, nil on failure.
    def send(argument, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN)
      case argument
      when Dnsruby::Message
        req = argument
      when Net::DNS::Packet, Resolv::DNS::Message
        req = Rex::Proto::DNS::Packet.encode_drb(argument)
      else
        net_packet = make_query_packet(argument,type,cls)
        # This returns a Net::DNS::Packet. Convert to Dnsruby::Message for consistency
        req = Rex::Proto::DNS::Packet.encode_drb(net_packet)
      end
      resolve = req.dup
      # Find cached items, remove request from resolved packet
      req.question.each do |ques|
        cached = self.cache.find(ques.qname, ques.qtype.to_s)
        next if cached.empty?
        req.instance_variable_set(:@answer, (req.answer + cached).uniq)
        resolve.question.delete(ques)
      end
      # Resolve remaining requests, cache responses
      if resolve.question.count > 0
        resolved = super(resolve, type)
        req.instance_variable_set(:@answer, (req.answer + resolved.answer).uniq)
        resolved.answer.each do |ans|
          self.cache.cache_record(ans)
        end
      end
      # Finalize answers in response
      # Check for empty response prior to sending
      req.header.rcode = Dnsruby::RCode::NOERROR if req.answer.size < 1
      req.header.qr = true # Set response bit
      return req
    end
  end
end
end
end