Class: Rex::Proto::DNS::CachedResolver

Inherits:
Resolver
  • Object
show all
Includes:
Constants
Defined in:
lib/rex/proto/dns/cached_resolver.rb

Overview

Provides Rex::Sockets compatible version of Net::DNS::Resolver Modified to work with Dnsruby::Messages, their resolvers are too heavy

Constant Summary

Constants included from Constants

Rex::Proto::DNS::Constants::MATCH_FQDN, Rex::Proto::DNS::Constants::MATCH_HOSTNAME

Constants inherited from Resolver

Resolver::Defaults

Instance Attribute Summary collapse

Attributes inherited from Resolver

#comm, #context

Instance Method Summary collapse

Methods inherited from Resolver

#proxies, #proxies=, #query, #search, #send_tcp, #send_udp

Constructor Details

#initialize(config = {}) ⇒ nil

Initialize resolver with cache

Parameters:

  • config (Hash) (defaults to: {})

    Resolver config



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/rex/proto/dns/cached_resolver.rb', line 23

def initialize(config = {})
  super(config)
  self.cache = Rex::Proto::DNS::Cache.new
  # Read hostsfile into cache
  hf = Rex::Compat.is_windows ? '%WINDIR%/system32/drivers/etc/hosts' : '/etc/hosts'
  entries = begin
    File.read(hf).lines.map(&:strip).select do |entry|
      Rex::Socket.is_ip_addr?(entry.gsub(/\s+/,' ').split(' ').first) and
      not entry.match(/::.*ip6-/) # Ignore Debian/Ubuntu-specific notation for IPv6 hosts
    end.map do |entry|
      entry.gsub(/\s+/,' ').split(' ')
    end
  rescue => e
    @logger.error(e)
    []
  end
  entries.each do |ent|
    next if ent.first =~ /^127\./
    # Deal with multiple hostnames per address
    while ent.length > 2
      hostname = ent.pop
      next unless MATCH_HOSTNAME.match hostname
      begin
        if Rex::Socket.is_ipv4?(ent.first)
          self.cache.add_static(hostname, ent.first, Dnsruby::Types::A)
        elsif Rex::Socket.is_ipv6?(ent.first)
          self.cache.add_static(hostname, ent.first, Dnsruby::Types::AAAA)
        else
          raise "Unknown IP address format #{ent.first} in hosts file!"
        end
      rescue => e
        # Deal with edge-cases in users' hostsfile
        @logger.error(e)
      end
    end
    hostname = ent.pop
    begin
      if MATCH_HOSTNAME.match hostname
        if Rex::Socket.is_ipv4?(ent.first)
          self.cache.add_static(hostname, ent.first, Dnsruby::Types::A)
        elsif Rex::Socket.is_ipv6?(ent.first)
          self.cache.add_static(hostname, ent.first, Dnsruby::Types::AAAA)
        else
          raise "Unknown IP address format #{ent.first} in hosts file!"
        end
      end
    rescue => e
      # Deal with edge-cases in users' hostsfile
      @logger.error(e)
    end
  end
  # TODO: inotify or similar on hostsfile for live updates? Easy-button functionality
  self.cache.start unless config[:dns_cache_no_start]
  return
end

Instance Attribute Details

#cacheObject

Returns the value of attribute cache.



15
16
17
# File 'lib/rex/proto/dns/cached_resolver.rb', line 15

def cache
  @cache
end

Instance Method Details

#send(argument, type = Dnsruby::Types::A, cls = Dnsruby::Classes::IN) ⇒ Dnsruby::Message?

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.

Parameters:

  • argument (Object)

    An object holding the DNS message to be processed.

  • type (Fixnum) (defaults to: Dnsruby::Types::A)

    Type of record to look up

  • cls (Fixnum) (defaults to: Dnsruby::Classes::IN)

    Class of question to look up

Returns:

  • (Dnsruby::Message, nil)

    DNS response on success, nil on failure.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/rex/proto/dns/cached_resolver.rb', line 89

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