Async::DNS SourceAsyncDNSTransaction

class Transaction

This class provides all details of a single DNS question and response. This is used by the DSL to provide DNS related functionality.

The main functions to complete the transaction are: #append! (evaluate a new query and append the results), #passthrough! (pass the query to an upstream server), #respond! (compute a specific response) and #fail! (fail with an error code).

Definitions

DEFAULT_TTL = 86400

The default time used for responses (24 hours).

def initialize(server, query, question, resource_class, response, **options)

Create a new transaction with the given server, query, question, resource class, response, and options.

Signature

parameter server Server

The server to use for processing.

parameter query Resolv::DNS::Message

The incoming query.

parameter question Resolv::DNS::Name

The question to answer.

parameter resource_class Class(Resolv::DNS::Resource)

The resource class to use for responses.

parameter response Resolv::DNS::Message

The response to the query.

parameter options Hash

Additional options to pass to the transaction.

Implementation

def initialize(server, query, question, resource_class, response, **options)
	@server = server
	@query = query
	@question = question
	@resource_class = resource_class
	@response = response

	@options = options
end

attr :resource_class

Signature

attribute Class(Resolv::DNS::Resource)

The resource class to use for responses. This is typically used to generate a response.

attr :query

Signature

attribute Resolv::DNS::Message

The incoming query.

attr :question

Signature

attribute Resolv::DNS::Name

The question to answer.

attr :response

Signature

attribute Resolv::DNS::Message

The response to the query.

attr :options

Signature

attribute Hash

Additional options associated with the transaction.

def [] key

Access the options hash.

Signature

parameter key Object

The key to lookup.

Implementation

def [] key
	@options[key]
end

def name

The name of the question, which is typically the requested hostname.

Implementation

def name
	@question.to_s
end

def to_s

Shows the question name and resource class. Suitable for debugging purposes.

Implementation

def to_s
	"#{name} #{@resource_class.name}"
end

def append!(name, resource_class = nil, options = {})

Run a new query through the rules with the given name and resource type. The results of this query are appended to the current transaction's response.

Implementation

def append!(name, resource_class = nil, options = {})
	Transaction.new(@server, @query, name, resource_class || @resource_class, @response, **options).process
end

def passthrough!(resolver, force: false, **options, &block)

Use the given resolver to respond to the question. Uses passthrough to do the lookup and merges the result.

If a block is supplied, this function yields with the response message if successful. This could be used, for example, to update a cache or modify the reply.

If recursion is not requested, the result is fail!(:Refused). This check is ignored if an explicit options[:name] or options[:force] is given.

If the resolver can't reach upstream servers, fail!(:ServFail) is invoked.

Implementation

def passthrough!(resolver, force: false, **options, &block)
	if @query.rd || force || options[:name]
		response = passthrough(resolver, **options)
		
		if response
			yield response if block_given?
			
			# Recursion is available and is being used:
			# See issue #26 for more details.
			@response.ra = 1
			@response.merge!(response)
		else
			fail!(:ServFail)
		end
	else
		fail!(:Refused)
	end
end

def passthrough(resolver, name: self.name, resource_class: self.resource_class)

Use the given resolver to respond to the question.

A block must be supplied, and provided a valid response is received from the upstream server, this function yields with the reply and reply_name.

If options[:name] is provided, this overrides the default query name sent to the upstream server. The same logic applies to options[:resource_class].

Implementation

def passthrough(resolver, name: self.name, resource_class: self.resource_class)
	resolver.query(name, resource_class)
end

def respond!(*args)

The last argument can optionally be a hash of options. If options[:resource_class] is provided, it overrides the default resource class of transaction. Additional options are passed to #append!.

See Resolv::DNS::Resource for more information about the various resource_classes available (http://www.ruby-doc.org/stdlib/libdoc/resolv/rdoc/index.html).

Implementation

def respond!(*args)
	append_question!
	
	options = args.last.kind_of?(Hash) ? args.pop : {}
	resource_class = options[:resource_class] || @resource_class
	
	if resource_class == nil
		raise ArgumentError.new("Could not instantiate resource #{resource_class}!")
	end
	
	resource = resource_class.new(*args)
	
	add([resource], options)
end

def add(resources, options = {})

Append a list of resources.

By default resources are appended to the answers section, but this can be changed by setting options[:section] to either :authority or :additional.

The time-to-live (TTL) of the resources can be specified using options[:ttl] and defaults to DEFAULT_TTL.

Implementation

def add(resources, options = {})
	# Use the default options if provided:
	options = options.merge(@options)
	
	ttl = options[:ttl] || DEFAULT_TTL
	name = options[:name] || @question.to_s + "."
	
	section = (options[:section] || 'answer').to_sym
	method = "add_#{section}".to_sym
	
	resources.each do |resource|
		@server.logger.debug {"#{method}: #{resource.inspect} #{resource.class::TypeValue} #{resource.class::ClassValue}"}
		
		@response.send(method, name, ttl, resource)
	end
end

def fail!(rcode)

This function indicates that there was a failure to resolve the given question. The single argument must be an integer error code, typically given by the constants in Resolv::DNS::RCode.

The easiest way to use this function it to simply supply a symbol. Here is a list of the most commonly used ones:

  • :NoError: No error occurred.
  • :FormErr: The incoming data was not formatted correctly.
  • :ServFail: The operation caused a server failure (internal error, etc).
  • :NXDomain: Non-eXistant Domain (domain record does not exist).
  • :NotImp: The operation requested is not implemented.
  • :Refused: The operation was refused by the server.
  • :NotAuth: The server is not authoritive for the zone.

See RFC2929 for more information about DNS error codes (specifically, page 3).

This function will complete deferred transactions.

Implementation

def fail!(rcode)
	append_question!
	
	if rcode.kind_of? Symbol
		@response.rcode = Resolv::DNS::RCode.const_get(rcode)
	else
		@response.rcode = rcode.to_i
	end
end

def failure!(*args)

  • deprecated

Signature

deprecated

Implementation

def failure!(*args)
	@server.logger.warn "failure! is deprecated, use fail! instead"
	
	fail!(*args)
end

def process

A helper method to process the transaction on the given server. Unless the transaction is deferred, it will #succeed on completion.

Implementation

def process
	@server.process(name, @resource_class, self)
end

def append_question!

A typical response to a DNS request includes both the question and response. This helper appends the question unless it looks like the user is already managing that aspect of the response.

Implementation

def append_question!
	if @response.question.size == 0
		@response.add_question(@question, @resource_class)
	end
end