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