Class: Async::DNS::Resolver
- Inherits:
-
Object
- Object
- Async::DNS::Resolver
- Defined in:
- lib/async/dns/resolver.rb
Overview
Resolve names to addresses using the DNS protocol.
Constant Summary collapse
- ADDRESS_RESOURCE_CLASSES =
[Resolv::DNS::Resource::IN::A]
Instance Attribute Summary collapse
-
#search ⇒ Object
readonly
The search domains, which are used to generate fully qualified names if required.
Class Method Summary collapse
-
.default(**options) ⇒ Object
The default resolver for the system.
Instance Method Summary collapse
-
#addresses_for(name, resource_classes = ADDRESS_RESOURCE_CLASSES) ⇒ Object
Yields a list of ‘Resolv::IPv4` and `Resolv::IPv6` addresses for the given `name` and `resource_class`.
-
#fully_qualified_names(name) ⇒ Object
Generates a fully qualified name from a given name.
-
#initialize(endpoint, ndots: 1, search: nil, origin: nil, cache: Cache.new, **options) ⇒ Resolver
constructor
Servers are specified in the same manor as options, e.g.
-
#next_id! ⇒ Object
Provides the next sequence identification number which is used to keep track of DNS messages.
-
#query(name, resource_class = Resolv::DNS::Resource::IN::A) ⇒ Object
Query a named resource and return the response.
-
#records_for(name, resource_classes) ⇒ Object
Look up a named resource of the given resource_class.
Constructor Details
#initialize(endpoint, ndots: 1, search: nil, origin: nil, cache: Cache.new, **options) ⇒ Resolver
Servers are specified in the same manor as options, e.g.
[:tcp/:udp, address, port]
In the case of multiple servers, they will be checked in sequence.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/async/dns/resolver.rb', line 37 def initialize(endpoint, ndots: 1, search: nil, origin: nil, cache: Cache.new, **) @endpoint = endpoint @ndots = ndots if search @search = search else @search = [nil] end if origin @search = [origin] + @search end @cache = cache = end |
Instance Attribute Details
#search ⇒ Object (readonly)
The search domains, which are used to generate fully qualified names if required.
56 57 58 |
# File 'lib/async/dns/resolver.rb', line 56 def search @search end |
Class Method Details
.default(**options) ⇒ Object
The default resolver for the system.
30 31 32 |
# File 'lib/async/dns/resolver.rb', line 30 def self.default(**) System.resolver(**) end |
Instance Method Details
#addresses_for(name, resource_classes = ADDRESS_RESOURCE_CLASSES) ⇒ Object
Yields a list of ‘Resolv::IPv4` and `Resolv::IPv6` addresses for the given `name` and `resource_class`. Raises a ResolutionFailure if no severs respond.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/async/dns/resolver.rb', line 131 def addresses_for(name, resource_classes = ADDRESS_RESOURCE_CLASSES) records = self.records_for(name, resource_classes) if records.empty? raise ResolutionFailure.new("Could not find any records for #{name.inspect}!") end addresses = [] if records records.each do |record| if record.respond_to? :address addresses << record.address else # The most common case here is that record.class is IN::CNAME and we need to figure out the address. Usually the upstream DNS server would have replied with this too, and this will be loaded from the response if possible without requesting additional information: addresses += addresses_for(record.name, resource_classes) end end end if addresses.empty? raise ResolutionFailure.new("Could not find any addresses for #{name.inspect}!") end return addresses end |
#fully_qualified_names(name) ⇒ Object
Generates a fully qualified name from a given name.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/async/dns/resolver.rb', line 61 def fully_qualified_names(name) return to_enum(:fully_qualified_names, name) unless block_given? name = Resolv::DNS::Name.create(name) if name.absolute? yield name else if @ndots <= name.length - 1 yield name end @search.each do |domain| yield name.with_origin(domain) end end end |
#next_id! ⇒ Object
Provides the next sequence identification number which is used to keep track of DNS messages.
80 81 82 83 |
# File 'lib/async/dns/resolver.rb', line 80 def next_id! # Using sequential numbers for the query ID is generally a bad thing because over UDP they can be spoofed. 16-bits isn't hard to guess either, but over UDP we also use a random port, so this makes effectively 32-bits of entropy to guess per request. SecureRandom.random_number(2**16) end |
#query(name, resource_class = Resolv::DNS::Resource::IN::A) ⇒ Object
Query a named resource and return the response.
Bypasses the cache and always makes a new request.
90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/async/dns/resolver.rb', line 90 def query(name, resource_class = Resolv::DNS::Resource::IN::A) response = nil self.fully_qualified_names(name) do |fully_qualified_name| response = self.dispatch_query(fully_qualified_name, resource_class) break if response.rcode == Resolv::DNS::RCode::NoError end return response end |
#records_for(name, resource_classes) ⇒ Object
Look up a named resource of the given resource_class.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/async/dns/resolver.rb', line 103 def records_for(name, resource_classes) Console.debug(self) {"Looking up records for #{name.inspect} with #{resource_classes.inspect}."} resource_classes = Array(resource_classes) resources = nil self.fully_qualified_names(name) do |fully_qualified_name| resources = @cache.fetch(fully_qualified_name, resource_classes) do |name, resource_class| if response = self.dispatch_query(name, resource_class) response.answer.each do |name, ttl, record| Console.debug(self) {"Caching record for #{name.inspect} with #{record.class} and TTL #{ttl}."} @cache.store(name, resource_class, record) end end end break if resources.any? end return resources end |