rubydns 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +60 -7
- data/lib/rubydns.rb +1 -3
- data/lib/rubydns/extensions/{resolv-1.8.rb → resolv.rb} +0 -25
- data/lib/rubydns/handler.rb +13 -4
- data/lib/rubydns/message.rb +2 -0
- data/lib/rubydns/server.rb +78 -56
- data/lib/rubydns/transaction.rb +33 -9
- data/lib/rubydns/version.rb +1 -1
- data/test/examples/GeoLiteCountry.dat +0 -0
- data/test/{daemon1.rb → examples/dropping-dns.rb} +10 -10
- data/test/{fortune-dns.rb → examples/fortune-dns.rb} +0 -2
- data/test/examples/geoip-dns.rb +86 -0
- data/test/examples/log/DroppingDaemon.log +107 -0
- data/test/examples/log/FortuneDNS.log +5 -0
- data/test/examples/log/GeoIPDNSDaemon.log +0 -0
- data/test/examples/log/TestDaemon.log +340 -0
- data/test/examples/run/DroppingDaemon.pid +1 -0
- data/test/examples/run/GeoIPDNSDaemon.pid +1 -0
- data/test/{soa_example1.rb → examples/soa-dns.rb} +5 -3
- data/test/{example1.rb → examples/test-dns-1.rb} +4 -3
- data/test/{daemon2.rb → examples/test-dns-2.rb} +4 -4
- data/test/log/BasicTestServer.log +64 -1453
- data/test/log/SlowServer.log +48 -0
- data/test/log/TestPassthroughServer.log +24 -790
- data/test/log/TruncatedServer.log +52 -959
- data/test/test_daemon.rb +5 -3
- data/test/test_passthrough.rb +4 -2
- data/test/test_rules.rb +53 -0
- data/test/test_slow_server.rb +77 -0
- data/test/test_truncation.rb +4 -2
- metadata +39 -31
- data/lib/rubydns/extensions/resolv-1.9.rb +0 -111
- data/test/TruncatedServer/log/daemon.log +0 -445
- data/test/log/FortuneDNS.log +0 -265
data/README.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
RubyDNS
|
2
2
|
=======
|
3
3
|
|
4
|
-
* Author: Samuel G. D. Williams (<http://www.oriontransfer.co.nz>)
|
5
|
-
* Copyright (C) 2009, 2011 Samuel G. D. Williams.
|
6
4
|
* Released under the MIT license.
|
5
|
+
* Copyright (C) 2009, 2011 [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams/).
|
7
6
|
* [](http://travis-ci.org/ioquatix/rubydns)
|
8
7
|
|
9
8
|
RubyDNS is a simple programmatic DSL (domain specific language) for configuring and running a DNS server. RubyDNS provides a daemon that runs a DNS server which can process DNS requests depending on specific policy. Rule selection is based on pattern matching, and results can be hard-coded, computed, fetched from a remote DNS server, fetched from a local cache, etc.
|
@@ -63,8 +62,6 @@ After starting this server you can test it using dig:
|
|
63
62
|
Compatibility
|
64
63
|
-------------
|
65
64
|
|
66
|
-
From RubyDNS version `0.4.0`, the recommended minimum Ruby version is `1.9.3` for complete support. Some features may not work as expected on Ruby version `1.8.x` and it is not tested significantly.
|
67
|
-
|
68
65
|
### Migrating from RubyDNS 0.3.x to 0.4.x ###
|
69
66
|
|
70
67
|
Due to changes in `resolv.rb`, superficial parts of RubyDNS have changed. Rather than using `:A` to specify A-records, one must now use the class name.
|
@@ -76,12 +73,68 @@ becomes
|
|
76
73
|
IN = Resolv::DNS::Resource::IN
|
77
74
|
match(..., IN::A)
|
78
75
|
|
76
|
+
### Migrating from RubyDNS 0.4.x to 0.5.x ###
|
77
|
+
|
78
|
+
The system standard resolver was synchronous, and this could stall the server when making upstream requests to other DNS servers. A new resolver `RubyDNS::Resolver` now provides an asynchronous interface and the `Transaction::passthrough` makes exclusive use of this to provide high performance asynchonous resolution.
|
79
|
+
|
80
|
+
Here is a basic example of how to use the new resolver in full. It is important to provide both `:udp` and `:tcp` connection specifications, so that large requests will be handled correctly:
|
81
|
+
|
82
|
+
resolver = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
|
83
|
+
|
84
|
+
EventMachine::run do
|
85
|
+
resolver.query('google.com', IN::A) do |response|
|
86
|
+
case response
|
87
|
+
when RubyDNS::Message
|
88
|
+
puts "Got response: #{response.answers.first}"
|
89
|
+
else
|
90
|
+
# Response is of class RubyDNS::ResolutionFailure
|
91
|
+
puts "Failed: #{response.message}"
|
92
|
+
end
|
93
|
+
|
94
|
+
EventMachine::stop
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
Existing code that uses `Resolv::DNS` as a resolver will need to be updated:
|
99
|
+
|
100
|
+
# 1/ Add this at the top of your file; Host specific system information:
|
101
|
+
require 'rubydns/system'
|
102
|
+
|
103
|
+
# 2/ Change from R = Resolv::DNS.new to:
|
104
|
+
R = RubyDNS::Resolver.new(RubyDNS::System::nameservers)
|
105
|
+
|
106
|
+
Everything else in the server can remain the same. You can see a complete example in `test/test_resolver.rb`.
|
107
|
+
|
108
|
+
#### Deferred Transactions ####
|
109
|
+
|
110
|
+
The implementation of the above depends on a new feature which was added in 0.5.0:
|
111
|
+
|
112
|
+
transaction.defer!
|
113
|
+
|
114
|
+
Once you call this, the transaction won't complete until you call either `transaction.succeed` or `transaction.fail`.
|
115
|
+
|
116
|
+
RubyDNS::run_server(:listen => SERVER_PORTS) do
|
117
|
+
match(/\.*.com/, IN::A) do |match, transaction|
|
118
|
+
transaction.defer!
|
119
|
+
|
120
|
+
# No domain exists, after 5 seconds:
|
121
|
+
EventMachine::Timer.new(5) do
|
122
|
+
transaction.failure!(:NXDomain)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
otherwise do
|
127
|
+
transaction.failure!(:NXDomain)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
You can see a complete example in `test/test_slow_server.rb`.
|
132
|
+
|
79
133
|
Todo
|
80
134
|
----
|
81
135
|
|
82
|
-
* Support for more features of DNS such as zone transfer
|
83
|
-
* Support reverse records more easily
|
84
|
-
* Better support for deferred requests/concurrency.
|
136
|
+
* Support for more features of DNS such as zone transfer.
|
137
|
+
* Support reverse records more easily.
|
85
138
|
|
86
139
|
License
|
87
140
|
-------
|
data/lib/rubydns.rb
CHANGED
@@ -21,16 +21,14 @@
|
|
21
21
|
require 'rubydns/version'
|
22
22
|
|
23
23
|
if RUBY_VERSION < "1.9"
|
24
|
-
require 'rubydns/extensions/resolv-1.8'
|
25
24
|
require 'rubydns/extensions/string-1.8'
|
26
25
|
elsif RUBY_VERSION < "1.9.3"
|
27
|
-
require 'rubydns/extensions/resolv-1.9'
|
28
26
|
require 'rubydns/extensions/string-1.9.2'
|
29
27
|
else
|
30
|
-
require 'rubydns/extensions/resolv-1.9'
|
31
28
|
require 'rubydns/extensions/string-1.9.3'
|
32
29
|
end
|
33
30
|
|
31
|
+
require 'rubydns/message'
|
34
32
|
require 'rubydns/server'
|
35
33
|
require 'rubydns/resolver'
|
36
34
|
require 'rubydns/handler'
|
@@ -22,31 +22,6 @@ require 'resolv'
|
|
22
22
|
|
23
23
|
class Resolv
|
24
24
|
class DNS
|
25
|
-
# Queries the given DNS server and returns its response in its entirety.
|
26
|
-
# This allows such responses to be passed upstream with little or no
|
27
|
-
# modification/reinterpretation.
|
28
|
-
def query(name, typeclass)
|
29
|
-
lazy_initialize
|
30
|
-
requester = make_requester
|
31
|
-
senders = {}
|
32
|
-
begin
|
33
|
-
@config.resolv(name) {|candidate, tout, nameserver|
|
34
|
-
msg = Message.new
|
35
|
-
msg.rd = 1
|
36
|
-
msg.add_question(candidate, typeclass)
|
37
|
-
unless sender = senders[[candidate, nameserver]]
|
38
|
-
sender = senders[[candidate, nameserver]] =
|
39
|
-
requester.sender(msg, candidate, nameserver)
|
40
|
-
end
|
41
|
-
reply, reply_name = requester.request(sender, tout)
|
42
|
-
|
43
|
-
return reply, reply_name
|
44
|
-
}
|
45
|
-
ensure
|
46
|
-
requester.close
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
25
|
class Message
|
51
26
|
# Merge the given message with this message. A number of heuristics are
|
52
27
|
# applied in order to ensure that the result makes sense. For example,
|
data/lib/rubydns/handler.rb
CHANGED
@@ -21,19 +21,24 @@
|
|
21
21
|
require 'rubydns/message'
|
22
22
|
|
23
23
|
module RubyDNS
|
24
|
+
|
25
|
+
def self.get_peer_details(connection)
|
26
|
+
Socket.unpack_sockaddr_in(connection.get_peername)[1]
|
27
|
+
end
|
28
|
+
|
24
29
|
module UDPHandler
|
25
30
|
def initialize(server)
|
26
31
|
@server = server
|
27
32
|
end
|
28
33
|
|
29
|
-
def self.process(server, data, &block)
|
34
|
+
def self.process(server, data, options = {}, &block)
|
30
35
|
server.logger.debug "Receiving incoming query (#{data.bytesize} bytes)..."
|
31
36
|
query = nil
|
32
37
|
|
33
38
|
begin
|
34
39
|
query = RubyDNS::decode_message(data)
|
35
40
|
|
36
|
-
return server.process_query(query, &block)
|
41
|
+
return server.process_query(query, options, &block)
|
37
42
|
rescue
|
38
43
|
server.logger.error "Error processing request!"
|
39
44
|
server.logger.error "#{$!.class}: #{$!.message}"
|
@@ -56,7 +61,9 @@ module RubyDNS
|
|
56
61
|
end
|
57
62
|
|
58
63
|
def receive_data(data)
|
59
|
-
|
64
|
+
options = {:peer => RubyDNS::get_peer_details(self)}
|
65
|
+
|
66
|
+
UDPHandler.process(@server, data, options) do |answer|
|
60
67
|
data = answer.encode
|
61
68
|
|
62
69
|
@server.logger.debug "Writing response to client (#{data.bytesize} bytes) via UDP..."
|
@@ -107,7 +114,9 @@ module RubyDNS
|
|
107
114
|
if (@buffer.size - @processed) >= @length
|
108
115
|
data = @buffer.string.byteslice(@processed, @length)
|
109
116
|
|
110
|
-
|
117
|
+
options = {:peer => RubyDNS::get_peer_details(self)}
|
118
|
+
|
119
|
+
UDPHandler.process(@server, data, options) do |answer|
|
111
120
|
data = answer.encode
|
112
121
|
|
113
122
|
@server.logger.debug "Writing response to client (#{data.bytesize} bytes) via TCP..."
|
data/lib/rubydns/message.rb
CHANGED
data/lib/rubydns/server.rb
CHANGED
@@ -26,6 +26,61 @@ module RubyDNS
|
|
26
26
|
# are used to match against incoming DNS questions. These rules are used to
|
27
27
|
# generate responses which are either DNS resource records or failures.
|
28
28
|
class Server
|
29
|
+
class Rule
|
30
|
+
def initialize(pattern, callback)
|
31
|
+
@pattern = pattern
|
32
|
+
@callback = callback
|
33
|
+
end
|
34
|
+
|
35
|
+
def match(name, resource_class)
|
36
|
+
# If the pattern doesn't specify any resource classes, we implicitly pass this test:
|
37
|
+
return true if @pattern.size < 2
|
38
|
+
|
39
|
+
# Otherwise, we try to match against some specific resource classes:
|
40
|
+
if Class === @pattern[1]
|
41
|
+
@pattern[1] == resource_class
|
42
|
+
else
|
43
|
+
@pattern[1].include?(resource_class) rescue false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def call(server, name, resource_class, *args)
|
48
|
+
unless match(name, resource_class)
|
49
|
+
server.logger.debug "Resource class #{resource_class} failed to match #{@pattern[1].inspect}!"
|
50
|
+
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Match succeeded against name?
|
55
|
+
case @pattern[0]
|
56
|
+
when Regexp
|
57
|
+
match_data = @pattern[0].match(name)
|
58
|
+
if match_data
|
59
|
+
server.logger.debug "Regexp pattern matched with #{match_data.inspect}."
|
60
|
+
return @callback[match_data, *args]
|
61
|
+
end
|
62
|
+
when String
|
63
|
+
if @pattern[0] == name
|
64
|
+
server.logger.debug "String pattern matched."
|
65
|
+
return @callback[*args]
|
66
|
+
end
|
67
|
+
else
|
68
|
+
if (@pattern[0].call(name, resource_class) rescue false)
|
69
|
+
server.logger.debug "Callable pattern matched."
|
70
|
+
return @callback[*args]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
server.logger.debug "No pattern matched."
|
75
|
+
# We failed to match the pattern.
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
@pattern.inspect
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
29
84
|
# Instantiate a server with a block
|
30
85
|
#
|
31
86
|
# server = Server.new do
|
@@ -58,7 +113,7 @@ module RubyDNS
|
|
58
113
|
# match(/g?mail.(com|org|net)/, [IN::MX, IN::A])
|
59
114
|
#
|
60
115
|
def match(*pattern, &block)
|
61
|
-
@rules <<
|
116
|
+
@rules << Rule.new(pattern, block)
|
62
117
|
end
|
63
118
|
|
64
119
|
# Register a named event which may be invoked later using #fire
|
@@ -66,7 +121,7 @@ module RubyDNS
|
|
66
121
|
# RExec.change_user(RUN_AS)
|
67
122
|
# end
|
68
123
|
def on(event_name, &block)
|
69
|
-
@events[event_name] =
|
124
|
+
@events[event_name] = block
|
70
125
|
end
|
71
126
|
|
72
127
|
# Fire the named event, which must have been registered using on.
|
@@ -87,7 +142,11 @@ module RubyDNS
|
|
87
142
|
# end
|
88
143
|
#
|
89
144
|
def otherwise(&block)
|
90
|
-
@otherwise =
|
145
|
+
@otherwise = block
|
146
|
+
end
|
147
|
+
|
148
|
+
def next!
|
149
|
+
throw :next
|
91
150
|
end
|
92
151
|
|
93
152
|
# Give a name and a record type, try to match a rule and use it for
|
@@ -99,55 +158,11 @@ module RubyDNS
|
|
99
158
|
@logger.debug "Searching for #{name} #{resource_class.name}"
|
100
159
|
|
101
160
|
@rules.each do |rule|
|
102
|
-
@logger.debug "Checking rule #{rule
|
103
|
-
|
104
|
-
pattern = rule[0]
|
105
|
-
|
106
|
-
# Match failed against resource_class?
|
107
|
-
case pattern[1]
|
108
|
-
when Class
|
109
|
-
next unless pattern[1] == resource_class
|
110
|
-
@logger.debug "Resource class #{resource_class.name} matched"
|
111
|
-
when Array
|
112
|
-
next unless pattern[1].include?(resource_class)
|
113
|
-
@logger.debug "Resource class #{resource_class} matched #{pattern[1].inspect}"
|
114
|
-
end
|
161
|
+
@logger.debug "Checking rule #{rule}..."
|
115
162
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
match_data = pattern[0].match(name)
|
120
|
-
if match_data
|
121
|
-
@logger.debug "Query #{name} matched #{pattern[0].to_s} with result #{match_data.inspect}"
|
122
|
-
if rule[1].call(match_data, *args)
|
123
|
-
@logger.debug "Rule returned successfully"
|
124
|
-
return
|
125
|
-
end
|
126
|
-
else
|
127
|
-
@logger.debug "Query #{name} failed to match against #{pattern[0].to_s}"
|
128
|
-
end
|
129
|
-
when String
|
130
|
-
if pattern[0] == name
|
131
|
-
@logger.debug "Query #{name} matched #{pattern[0]}"
|
132
|
-
if rule[1].call(*args)
|
133
|
-
@logger.debug "Rule returned successfully"
|
134
|
-
return
|
135
|
-
end
|
136
|
-
else
|
137
|
-
@logger.debug "Query #{name} failed to match against #{pattern[0]}"
|
138
|
-
end
|
139
|
-
else
|
140
|
-
if pattern[0].respond_to? :call
|
141
|
-
if pattern[0].call(name)
|
142
|
-
@logger.debug "Query #{name} matched #{pattern[0]}"
|
143
|
-
if rule[1].call(*args)
|
144
|
-
@logger.debug "Rule returned successfully"
|
145
|
-
return
|
146
|
-
end
|
147
|
-
else
|
148
|
-
@logger.debug "Query #{name} failed to match against #{pattern[0]}"
|
149
|
-
end
|
150
|
-
end
|
163
|
+
catch (:next) do
|
164
|
+
# If the rule returns true, we assume that it was successful and no further rules need to be evaluated.
|
165
|
+
return true if rule.call(self, name, resource_class, *args)
|
151
166
|
end
|
152
167
|
end
|
153
168
|
|
@@ -160,7 +175,7 @@ module RubyDNS
|
|
160
175
|
|
161
176
|
# Process an incoming DNS message. Returns a serialized message to be
|
162
177
|
# sent back to the client.
|
163
|
-
def process_query(query, &block)
|
178
|
+
def process_query(query, options = {}, &block)
|
164
179
|
# Setup answer
|
165
180
|
answer = Resolv::DNS::Message::new(query.id)
|
166
181
|
answer.qr = 1 # 0 = Query, 1 = Response
|
@@ -186,7 +201,7 @@ module RubyDNS
|
|
186
201
|
chain << lambda do
|
187
202
|
@logger.debug "Processing question #{question} #{resource_class}..."
|
188
203
|
|
189
|
-
transaction = Transaction.new(self, query, question, resource_class, answer)
|
204
|
+
transaction = Transaction.new(self, query, question, resource_class, answer, options)
|
190
205
|
|
191
206
|
# Call the next link in the chain:
|
192
207
|
transaction.callback do
|
@@ -196,9 +211,16 @@ module RubyDNS
|
|
196
211
|
|
197
212
|
# If there was an error, log it and fail:
|
198
213
|
transaction.errback do |response|
|
199
|
-
|
200
|
-
|
201
|
-
|
214
|
+
if Exception === response
|
215
|
+
@logger.error "Exception thrown while processing #{transaction}!"
|
216
|
+
@logger.error "#{response.class}: #{response.message}"
|
217
|
+
if response.backtrace
|
218
|
+
Array(response.backtrace).each { |at| @logger.error at }
|
219
|
+
end
|
220
|
+
else
|
221
|
+
@logger.error "Failure while processing #{transaction}!"
|
222
|
+
@logger.error "#{response.inspect}"
|
223
|
+
end
|
202
224
|
|
203
225
|
answer.rcode = Resolv::DNS::RCode::ServFail
|
204
226
|
|
data/lib/rubydns/transaction.rb
CHANGED
@@ -27,13 +27,15 @@ module RubyDNS
|
|
27
27
|
class Transaction
|
28
28
|
include EventMachine::Deferrable
|
29
29
|
|
30
|
-
def initialize(server, query, question, resource_class, answer)
|
30
|
+
def initialize(server, query, question, resource_class, answer, options = {})
|
31
31
|
@server = server
|
32
32
|
@query = query
|
33
33
|
@question = question
|
34
34
|
@resource_class = resource_class
|
35
35
|
@answer = answer
|
36
36
|
|
37
|
+
@options = options
|
38
|
+
|
37
39
|
@deferred = false
|
38
40
|
@question_appended = false
|
39
41
|
end
|
@@ -51,6 +53,9 @@ module RubyDNS
|
|
51
53
|
# The current full answer to the incoming query.
|
52
54
|
attr :answer
|
53
55
|
|
56
|
+
# Any options or configuration associated with the given transaction.
|
57
|
+
attr :options
|
58
|
+
|
54
59
|
# Return the name of the question, which is typically the requested hostname.
|
55
60
|
def name
|
56
61
|
@question.to_s
|
@@ -63,8 +68,8 @@ module RubyDNS
|
|
63
68
|
|
64
69
|
# Run a new query through the rules with the given name and resource type. The
|
65
70
|
# results of this query are appended to the current transactions <tt>answer</tt>.
|
66
|
-
def append_query!(name, resource_class = nil)
|
67
|
-
Transaction.new(@server, @query, name, resource_class || @resource_class, @answer).process
|
71
|
+
def append_query!(name, resource_class = nil, options = {})
|
72
|
+
Transaction.new(@server, @query, name, resource_class || @resource_class, @answer, options).process
|
68
73
|
end
|
69
74
|
|
70
75
|
def process(&finished)
|
@@ -93,6 +98,8 @@ module RubyDNS
|
|
93
98
|
end
|
94
99
|
|
95
100
|
@answer.merge!(response)
|
101
|
+
|
102
|
+
succeed if @deferred
|
96
103
|
end
|
97
104
|
|
98
105
|
true
|
@@ -117,8 +124,10 @@ module RubyDNS
|
|
117
124
|
case response
|
118
125
|
when RubyDNS::Message
|
119
126
|
yield response
|
120
|
-
|
127
|
+
when RubyDNS::ResolverFailure
|
128
|
+
failure!(:ServFail)
|
121
129
|
else
|
130
|
+
# This shouldn't ever happen, but if it does for some reason we shouldn't hang.
|
122
131
|
fail(response)
|
123
132
|
end
|
124
133
|
end
|
@@ -166,6 +175,8 @@ module RubyDNS
|
|
166
175
|
#
|
167
176
|
# <tt>options[:ttl]</tt>:: Specify the TTL for the resource
|
168
177
|
# <tt>options[:name]</tt>:: Override the name (question) of the response.
|
178
|
+
# <tt>options[:section]</tt>:: Specify whether the response should go in the `:answer`
|
179
|
+
# `:authority` or `:additional` section.
|
169
180
|
#
|
170
181
|
# This function can be used to supply multiple responses to a given question.
|
171
182
|
# For example, each argument is expected to be an instantiated resource from
|
@@ -173,17 +184,27 @@ module RubyDNS
|
|
173
184
|
def append! (*resources)
|
174
185
|
append_question!
|
175
186
|
|
176
|
-
|
187
|
+
if resources.last.kind_of?(Hash)
|
188
|
+
options = resources.pop
|
189
|
+
else
|
190
|
+
options = {}
|
191
|
+
end
|
192
|
+
|
193
|
+
# Use the default options if provided:
|
194
|
+
options = options.merge(@options)
|
195
|
+
|
177
196
|
options[:ttl] ||= 16000
|
178
197
|
options[:name] ||= @question.to_s + "."
|
198
|
+
|
199
|
+
method = ("add_" + (options[:section] || 'answer').to_s).to_sym
|
179
200
|
|
180
201
|
resources.each do |resource|
|
181
|
-
@server.logger.debug "
|
182
|
-
|
202
|
+
@server.logger.debug "#{method}: #{resource.inspect} #{resource.class::TypeValue} #{resource.class::ClassValue}"
|
203
|
+
|
204
|
+
@answer.send(method, options[:name], options[:ttl], resource)
|
183
205
|
end
|
184
206
|
|
185
|
-
|
186
|
-
@answer.encode
|
207
|
+
succeed if @deferred
|
187
208
|
|
188
209
|
true
|
189
210
|
end
|
@@ -214,6 +235,9 @@ module RubyDNS
|
|
214
235
|
@answer.rcode = rcode.to_i
|
215
236
|
end
|
216
237
|
|
238
|
+
# The transaction itself has completed, but contains a failure:
|
239
|
+
succeed(rcode) if @deferred
|
240
|
+
|
217
241
|
true
|
218
242
|
end
|
219
243
|
|