Class: RubyDNS::Resolver

Inherits:
Object
  • Object
show all
Includes:
Celluloid::IO
Defined in:
lib/rubydns/resolver.rb

Defined Under Namespace

Classes: Request

Instance Method Summary collapse

Constructor Details

#initialize(servers, options = {}) ⇒ Resolver

Servers are specified in the same manor as options[:listen], e.g. [:tcp/:udp, address, port] In the case of multiple servers, they will be checked in sequence.



42
43
44
45
46
47
48
# File 'lib/rubydns/resolver.rb', line 42

def initialize(servers, options = {})
	@servers = servers
	
	@options = options
	
	@logger = options[:logger] || Celluloid.logger
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.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/rubydns/resolver.rb', line 66

def addresses_for(name, resource_class = Resolv::DNS::Resource::IN::A, options = {})
	(options[:retries] || 5).times do
		response = query(name, resource_class)
		
		if response
			# Resolv::DNS::Name doesn't retain the trailing dot.
			name = name.sub(/\.$/, '')
			
			return response.answer.select{|record| record[0].to_s == name}.collect{|record| record[2].address}
		end
		
		# Wait 10ms before trying again:
		sleep 0.01
	end
	
	abort ResolutionFailure.new("No server replied.")
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.



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
# File 'lib/rubydns/resolver.rb', line 89

def dispatch_request(message)
	request = Request.new(message, @servers)
	
	request.each do |server|
		@logger.debug "[#{message.id}] Sending request 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(message, response)
				return response
			end
		rescue Task::TimeoutError
			@logger.debug "[#{message.id}] Request timed out!" if @logger
		rescue InvalidResponseError
			@logger.warn "[#{message.id}] Invalid response from network: #{$!}!" if @logger
		rescue DecodeError
			@logger.warn "[#{message.id}] Error while decoding data from network: #{$!}!" if @logger
		rescue IOError
			@logger.warn "[#{message.id}] Error while reading from network: #{$!}!" if @logger
		end
	end
	
	return nil
end

#next_id!Object

Provides the next sequence identification number which is used to keep track of DNS messages.



51
52
53
54
# File 'lib/rubydns/resolver.rb', line 51

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.



57
58
59
60
61
62
63
# File 'lib/rubydns/resolver.rb', line 57

def query(name, resource_class = Resolv::DNS::Resource::IN::A)
	message = Resolv::DNS::Message.new(next_id!)
	message.rd = 1
	message.add_question name, resource_class
	
	dispatch_request(message)
end

#request_timeoutObject



84
85
86
# File 'lib/rubydns/resolver.rb', line 84

def request_timeout
	@options[:timeout] || 1
end