Class: Celluloid::DNS::Resolver
- Inherits:
-
Object
- Object
- Celluloid::DNS::Resolver
- Includes:
- IO
- Defined in:
- lib/celluloid/dns/resolver.rb
Defined Under Namespace
Classes: Request
Constant Summary collapse
- DEFAULT_TIMEOUT =
Wait for up to 5 seconds for a response. Override with ‘options`
5.0
- DEFAULT_DELAY =
10ms wait between making requests. Override with ‘options`
0.01
- DEFAULT_RETRIES =
Try a given request 10 times before failing. Override with ‘options`.
10
Instance Attribute Summary collapse
-
#origin ⇒ Object
Returns the value of attribute origin.
Instance Method Summary collapse
-
#addresses_for(name, resource_class = Resolv::DNS::Resource::IN::A, options = {}) ⇒ Object
Yields a list of ‘Resolv::IPv4` and `Resolv::IPv6` addresses for the given `name` and `resource_class`.
-
#dispatch_request(message) ⇒ Object
Send the message to available servers.
- #fully_qualified_name(name) ⇒ Object
-
#initialize(servers, 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
Look up a named resource of the given resource_class.
- #request_timeout ⇒ Object
Constructor Details
#initialize(servers, 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.
51 52 53 54 55 56 57 58 59 |
# File 'lib/celluloid/dns/resolver.rb', line 51 def initialize(servers, = {}) @servers = servers @options = @origin = [:origin] || nil @logger = [:logger] || Celluloid.logger end |
Instance Attribute Details
#origin ⇒ Object
Returns the value of attribute origin.
61 62 63 |
# File 'lib/celluloid/dns/resolver.rb', line 61 def origin @origin end |
Instance Method Details
#addresses_for(name, resource_class = Resolv::DNS::Resource::IN::A, options = {}) ⇒ 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.
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/celluloid/dns/resolver.rb', line 97 def addresses_for(name, resource_class = Resolv::DNS::Resource::IN::A, = {}) name = fully_qualified_name(name) cache = .fetch(:cache, {}) retries = .fetch(:retries, DEFAULT_RETRIES) delay = .fetch(:delay, DEFAULT_DELAY) records = lookup(name, resource_class, cache) do |name, resource_class| response = nil retries.times do |i| # Wait 10ms before trying again: sleep delay if delay and i > 0 response = query(name, resource_class) break if response end response or abort ResolutionFailure.new("Could not resolve #{name} after #{retries} attempt(s).") 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, record.class, .merge(cache: cache)) end end end if addresses.size > 0 return addresses else abort ResolutionFailure.new("Could not find any addresses for #{name}.") end end |
#dispatch_request(message) ⇒ Object
Send the message to available servers. If no servers respond correctly, nil is returned. This result indicates a failure of the resolver to correctly contact any server and get a valid response.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/celluloid/dns/resolver.rb', line 144 def dispatch_request() request = Request.new(, @servers) request.each do |server| @logger.debug "[#{.id}] Sending request #{.question.inspect} to server #{server.inspect}" if @logger begin response = nil # This may be causing a problem, perhaps try: # after(timeout) { socket.close } # https://github.com/celluloid/celluloid-io/issues/121 timeout(request_timeout) do response = try_server(request, server) end if valid_response(, response) return response end rescue TaskTimeout @logger.debug "[#{.id}] Request timed out!" if @logger rescue InvalidResponseError @logger.warn "[#{.id}] Invalid response from network: #{$!}!" if @logger rescue DecodeError @logger.warn "[#{.id}] Error while decoding data from network: #{$!}!" if @logger rescue IOError @logger.warn "[#{.id}] Error while reading from network: #{$!}!" if @logger end end return nil end |
#fully_qualified_name(name) ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/celluloid/dns/resolver.rb', line 63 def fully_qualified_name(name) # If we are passed an existing deconstructed name: if Resolv::DNS::Name === name if name.absolute? return name else return name.with_origin(@origin) end end # ..else if we have a string, we need to do some basic processing: if name.end_with? '.' return Resolv::DNS::Name.create(name) else return Resolv::DNS::Name.create(name).with_origin(@origin) end end |
#next_id! ⇒ Object
Provides the next sequence identification number which is used to keep track of DNS messages.
82 83 84 85 |
# File 'lib/celluloid/dns/resolver.rb', line 82 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
Look up a named resource of the given resource_class.
88 89 90 91 92 93 94 |
# File 'lib/celluloid/dns/resolver.rb', line 88 def query(name, resource_class = Resolv::DNS::Resource::IN::A) = Resolv::DNS::Message.new(next_id!) .rd = 1 .add_question fully_qualified_name(name), resource_class dispatch_request() end |
#request_timeout ⇒ Object
139 140 141 |
# File 'lib/celluloid/dns/resolver.rb', line 139 def request_timeout @options[:timeout] || DEFAULT_TIMEOUT end |