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
serverServer The server to use for processing.
-
parameter
queryResolv::DNS::Message The incoming query.
-
parameter
questionResolv::DNS::Name The question to answer.
-
parameter
resource_classClass(Resolv::DNS::Resource) The resource class to use for responses.
-
parameter
responseResolv::DNS::Message The response to the query.
-
parameter
optionsHash 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
keyObject 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