rubydns 0.6.0 → 2.0.0
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.
- checksums.yaml +6 -14
- data/.gitignore +23 -14
- data/.rspec +4 -0
- data/.simplecov +15 -0
- data/.travis.yml +9 -5
- data/.yardopts +1 -0
- data/Gemfile +6 -2
- data/README.md +82 -92
- data/Rakefile +2 -5
- data/bin/rubydns-check +374 -0
- data/examples/Gemfile +7 -0
- data/examples/README.md +137 -0
- data/examples/basic-dns.rb +24 -0
- data/examples/cname.rb +25 -0
- data/{test/examples/dropping-dns.rb → examples/flakey-dns.rb} +28 -31
- data/{test/examples → examples}/fortune-dns.rb +42 -46
- data/examples/geoip-dns.rb +115 -0
- data/examples/simple.rb +25 -0
- data/{test/examples → examples}/soa-dns.rb +27 -27
- data/{test/examples → examples}/test-dns-1.rb +26 -20
- data/{test/examples → examples}/test-dns-2.rb +17 -19
- data/examples/wikipedia-dns.rb +107 -0
- data/lib/rubydns/rule_based_server.rb +180 -0
- data/lib/rubydns/version.rb +1 -1
- data/lib/rubydns.rb +13 -63
- data/rubydns.gemspec +29 -23
- data/spec/rubydns/daemon_spec.rb +114 -0
- data/{test/test_system.rb → spec/rubydns/injected_supervisor_spec.rb} +32 -25
- data/spec/rubydns/passthrough_spec.rb +85 -0
- data/spec/rubydns/rules_spec.rb +74 -0
- data/spec/spec_helper.rb +30 -0
- metadata +101 -78
- data/bin/rd-dns-check +0 -374
- data/bin/rd-resolve-test +0 -160
- data/lib/rubydns/chunked.rb +0 -34
- data/lib/rubydns/extensions/hexdump.rb +0 -38
- data/lib/rubydns/extensions/logger.rb +0 -30
- data/lib/rubydns/extensions/resolv.rb +0 -53
- data/lib/rubydns/extensions/string-1.8.rb +0 -35
- data/lib/rubydns/extensions/string-1.9.2.rb +0 -29
- data/lib/rubydns/extensions/string-1.9.3.rb +0 -31
- data/lib/rubydns/extensions/string.rb +0 -27
- data/lib/rubydns/handler.rb +0 -140
- data/lib/rubydns/message.rb +0 -41
- data/lib/rubydns/resolver.rb +0 -239
- data/lib/rubydns/server.rb +0 -241
- data/lib/rubydns/system.rb +0 -146
- data/lib/rubydns/transaction.rb +0 -250
- data/test/examples/geoip-dns.rb +0 -86
- data/test/helper.rb +0 -9
- data/test/test_daemon.rb +0 -100
- data/test/test_domains.txt +0 -185
- data/test/test_passthrough.rb +0 -80
- data/test/test_resolver.rb +0 -105
- data/test/test_rules.rb +0 -74
- data/test/test_slow_server.rb +0 -98
- data/test/test_truncation.rb +0 -78
- /data/{test → spec/rubydns}/hosts.txt +0 -0
data/lib/rubydns/resolver.rb
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
-
#
|
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
# furnished to do so, subject to the following conditions:
|
|
9
|
-
#
|
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
|
11
|
-
# all copies or substantial portions of the Software.
|
|
12
|
-
#
|
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
-
# THE SOFTWARE.
|
|
20
|
-
|
|
21
|
-
require 'rubydns/message'
|
|
22
|
-
|
|
23
|
-
module RubyDNS
|
|
24
|
-
class InvalidProtocolError < StandardError
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
class ResolutionFailure < StandardError
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
class Resolver
|
|
31
|
-
# Servers are specified in the same manor as options[:listen], e.g.
|
|
32
|
-
# [:tcp/:udp, address, port]
|
|
33
|
-
# In the case of multiple servers, they will be checked in sequence.
|
|
34
|
-
def initialize(servers, options = {})
|
|
35
|
-
@servers = servers
|
|
36
|
-
@sequence = 0
|
|
37
|
-
|
|
38
|
-
@options = options
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Provides the next sequence identification number which is used to keep track of DNS messages.
|
|
42
|
-
def next_id!
|
|
43
|
-
return (@sequence += 1)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Look up a named resource of the given resource_class.
|
|
47
|
-
def query(name, resource_class = Resolv::DNS::Resource::IN::A, &block)
|
|
48
|
-
message = Resolv::DNS::Message.new(next_id!)
|
|
49
|
-
message.rd = 1
|
|
50
|
-
message.add_question name, resource_class
|
|
51
|
-
|
|
52
|
-
Request.fetch(message, @servers, @options, &block)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Yields a list of `Resolv::IPv4` and `Resolv::IPv6` addresses for the given `name` and `resource_class`.
|
|
56
|
-
def addresses_for(name, resource_class = Resolv::DNS::Resource::IN::A, &block)
|
|
57
|
-
query(name, resource_class) do |response|
|
|
58
|
-
# Resolv::DNS::Name doesn't retain the trailing dot.
|
|
59
|
-
name = name.sub(/\.$/, '')
|
|
60
|
-
|
|
61
|
-
case response
|
|
62
|
-
when Message
|
|
63
|
-
yield response.answer.select{|record| record[0].to_s == name}.collect{|record| record[2].address}
|
|
64
|
-
else
|
|
65
|
-
yield []
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Manages a single DNS question message across one or more servers.
|
|
71
|
-
class Request
|
|
72
|
-
include EventMachine::Deferrable
|
|
73
|
-
|
|
74
|
-
def self.fetch(*args)
|
|
75
|
-
request = self.new(*args)
|
|
76
|
-
|
|
77
|
-
request.callback do |message|
|
|
78
|
-
yield message
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
request.errback do |error|
|
|
82
|
-
yield error
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
request.run!
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def initialize(message, servers, options = {}, &block)
|
|
89
|
-
@message = message
|
|
90
|
-
@packet = message.encode
|
|
91
|
-
|
|
92
|
-
@servers = servers.dup
|
|
93
|
-
|
|
94
|
-
# We select the protocol based on the size of the data:
|
|
95
|
-
if @packet.bytesize > UDP_TRUNCATION_SIZE
|
|
96
|
-
@servers.delete_if{|server| server[0] == :udp}
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Measured in seconds:
|
|
100
|
-
@timeout = options[:timeout] || 5
|
|
101
|
-
|
|
102
|
-
@logger = options[:logger]
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
attr :message
|
|
106
|
-
attr :packet
|
|
107
|
-
attr :logger
|
|
108
|
-
|
|
109
|
-
def run!
|
|
110
|
-
try_next_server!
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def process_response!(response)
|
|
114
|
-
if Exception === response
|
|
115
|
-
@logger.warn "[#{@message.id}] Failure while processing response #{exception}!" if @logger
|
|
116
|
-
RubyDNS.log_exception(@logger, response) if @logger
|
|
117
|
-
|
|
118
|
-
try_next_server!
|
|
119
|
-
elsif response.tc != 0
|
|
120
|
-
@logger.warn "[#{@message.id}] Received truncated response!" if @logger
|
|
121
|
-
|
|
122
|
-
try_next_server!
|
|
123
|
-
elsif response.id != @message.id
|
|
124
|
-
@logger.warn "[#{@message.id}] Received response with incorrect message id: #{response.id}" if @logger
|
|
125
|
-
|
|
126
|
-
try_next_server!
|
|
127
|
-
else
|
|
128
|
-
@logger.warn "[#{@message.id}] Received valid response #{response.inspect}" if @logger
|
|
129
|
-
|
|
130
|
-
succeed response
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
private
|
|
135
|
-
|
|
136
|
-
def try_next_server!
|
|
137
|
-
if @request
|
|
138
|
-
@request.close_connection
|
|
139
|
-
@request = nil
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
if @servers.size > 0
|
|
143
|
-
@server = @servers.shift
|
|
144
|
-
|
|
145
|
-
@logger.debug "[#{@message.id}] Sending request to server #{@server.inspect}" if @logger
|
|
146
|
-
|
|
147
|
-
# We make requests one at a time to the given server, naturally the servers are ordered in terms of priority.
|
|
148
|
-
case @server[0]
|
|
149
|
-
when :udp
|
|
150
|
-
@request = UDPRequestHandler.open(@server[1], @server[2], self)
|
|
151
|
-
when :tcp
|
|
152
|
-
@request = TCPRequestHandler.open(@server[1], @server[2], self)
|
|
153
|
-
else
|
|
154
|
-
raise InvalidProtocolError.new(@server)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
# Setting up the timeout...
|
|
158
|
-
EventMachine::Timer.new(@timeout) do
|
|
159
|
-
@logger.debug "[#{@message.id}] Request timed out!" if @logger
|
|
160
|
-
|
|
161
|
-
try_next_server!
|
|
162
|
-
end
|
|
163
|
-
else
|
|
164
|
-
fail ResolutionFailure.new("No available servers responded to the request.")
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
module UDPRequestHandler
|
|
169
|
-
def self.open(host, port, request)
|
|
170
|
-
# Open a datagram socket... EventMachine doesn't support connected datagram sockets, so we have to cheat a bit:
|
|
171
|
-
EventMachine::open_datagram_socket('', 0, self, request, host, port)
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def initialize(request, host, port)
|
|
175
|
-
@request = request
|
|
176
|
-
@host = host
|
|
177
|
-
@port = port
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
def post_init
|
|
181
|
-
# Sending question to remote DNS server...
|
|
182
|
-
send_datagram(@request.packet, @host, @port)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def receive_data(data)
|
|
186
|
-
# Receiving response from remote DNS server...
|
|
187
|
-
message = RubyDNS::decode_message(data)
|
|
188
|
-
|
|
189
|
-
# The message id must match, and it can't be truncated:
|
|
190
|
-
@request.process_response!(message)
|
|
191
|
-
rescue Resolv::DNS::DecodeError => error
|
|
192
|
-
@request.process_response!(error)
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
module TCPRequestHandler
|
|
197
|
-
def self.open(host, port, request)
|
|
198
|
-
EventMachine::connect(host, port, TCPRequestHandler, request)
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def initialize(request)
|
|
202
|
-
@request = request
|
|
203
|
-
@buffer = nil
|
|
204
|
-
@length = nil
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
def post_init
|
|
208
|
-
data = @request.packet
|
|
209
|
-
|
|
210
|
-
send_data([data.bytesize].pack('n'))
|
|
211
|
-
send_data data
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
def receive_data(data)
|
|
215
|
-
# We buffer data until we've received the entire packet:
|
|
216
|
-
@buffer ||= BinaryStringIO.new
|
|
217
|
-
@buffer.write(data)
|
|
218
|
-
|
|
219
|
-
if @length == nil
|
|
220
|
-
if @buffer.size > 2
|
|
221
|
-
@length = @buffer.string.byteslice(0, 2).unpack('n')[0]
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# If we have received more data than expected, should this be an error?
|
|
226
|
-
if @buffer.size >= (@length + 2)
|
|
227
|
-
data = @buffer.string.byteslice(2, @length)
|
|
228
|
-
|
|
229
|
-
message = RubyDNS::decode_message(data)
|
|
230
|
-
|
|
231
|
-
@request.process_response!(message)
|
|
232
|
-
end
|
|
233
|
-
rescue Resolv::DNS::DecodeError => error
|
|
234
|
-
@request.process_response!(error)
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
end
|
data/lib/rubydns/server.rb
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
# Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
-
#
|
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
# furnished to do so, subject to the following conditions:
|
|
9
|
-
#
|
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
|
11
|
-
# all copies or substantial portions of the Software.
|
|
12
|
-
#
|
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
-
# THE SOFTWARE.
|
|
20
|
-
|
|
21
|
-
require 'rubydns/transaction'
|
|
22
|
-
require 'rubydns/extensions/logger'
|
|
23
|
-
|
|
24
|
-
module RubyDNS
|
|
25
|
-
|
|
26
|
-
# This class provides the core of the DSL. It contains a list of rules which
|
|
27
|
-
# are used to match against incoming DNS questions. These rules are used to
|
|
28
|
-
# generate responses which are either DNS resource records or failures.
|
|
29
|
-
class Server
|
|
30
|
-
class Rule
|
|
31
|
-
def initialize(pattern, callback)
|
|
32
|
-
@pattern = pattern
|
|
33
|
-
@callback = callback
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def match(name, resource_class)
|
|
37
|
-
# If the pattern doesn't specify any resource classes, we implicitly pass this test:
|
|
38
|
-
return true if @pattern.size < 2
|
|
39
|
-
|
|
40
|
-
# Otherwise, we try to match against some specific resource classes:
|
|
41
|
-
if Class === @pattern[1]
|
|
42
|
-
@pattern[1] == resource_class
|
|
43
|
-
else
|
|
44
|
-
@pattern[1].include?(resource_class) rescue false
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def call(server, name, resource_class, *args)
|
|
49
|
-
unless match(name, resource_class)
|
|
50
|
-
server.logger.debug "Resource class #{resource_class} failed to match #{@pattern[1].inspect}!"
|
|
51
|
-
|
|
52
|
-
return false
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Match succeeded against name?
|
|
56
|
-
case @pattern[0]
|
|
57
|
-
when Regexp
|
|
58
|
-
match_data = @pattern[0].match(name)
|
|
59
|
-
if match_data
|
|
60
|
-
server.logger.debug "Regexp pattern matched with #{match_data.inspect}."
|
|
61
|
-
return @callback[*args, match_data]
|
|
62
|
-
end
|
|
63
|
-
when String
|
|
64
|
-
if @pattern[0] == name
|
|
65
|
-
server.logger.debug "String pattern matched."
|
|
66
|
-
return @callback[*args]
|
|
67
|
-
end
|
|
68
|
-
else
|
|
69
|
-
if (@pattern[0].call(name, resource_class) rescue false)
|
|
70
|
-
server.logger.debug "Callable pattern matched."
|
|
71
|
-
return @callback[*args]
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
server.logger.debug "No pattern matched."
|
|
76
|
-
# We failed to match the pattern.
|
|
77
|
-
return false
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def to_s
|
|
81
|
-
@pattern.inspect
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Instantiate a server with a block
|
|
86
|
-
#
|
|
87
|
-
# server = Server.new do
|
|
88
|
-
# match(/server.mydomain.com/, IN::A) do |transaction|
|
|
89
|
-
# transaction.respond!("1.2.3.4")
|
|
90
|
-
# end
|
|
91
|
-
# end
|
|
92
|
-
#
|
|
93
|
-
def initialize(&block)
|
|
94
|
-
@events = {}
|
|
95
|
-
@rules = []
|
|
96
|
-
@otherwise = nil
|
|
97
|
-
|
|
98
|
-
@logger = Logger.new($stderr)
|
|
99
|
-
|
|
100
|
-
if block_given?
|
|
101
|
-
instance_eval &block
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
attr :logger, true
|
|
106
|
-
|
|
107
|
-
# This function connects a pattern with a block. A pattern is either
|
|
108
|
-
# a String or a Regex instance. Optionally, a second argument can be
|
|
109
|
-
# provided which is either a String, Symbol or Array of resource record
|
|
110
|
-
# types which the rule matches against.
|
|
111
|
-
#
|
|
112
|
-
# match("www.google.com")
|
|
113
|
-
# match("gmail.com", IN::MX)
|
|
114
|
-
# match(/g?mail.(com|org|net)/, [IN::MX, IN::A])
|
|
115
|
-
#
|
|
116
|
-
def match(*pattern, &block)
|
|
117
|
-
@rules << Rule.new(pattern, block)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Register a named event which may be invoked later using #fire
|
|
121
|
-
# on(:start) do |server|
|
|
122
|
-
# RExec.change_user(RUN_AS)
|
|
123
|
-
# end
|
|
124
|
-
def on(event_name, &block)
|
|
125
|
-
@events[event_name] = block
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Fire the named event, which must have been registered using on.
|
|
129
|
-
def fire(event_name)
|
|
130
|
-
callback = @events[event_name]
|
|
131
|
-
|
|
132
|
-
if callback
|
|
133
|
-
callback.call(self)
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Specify a default block to execute if all other rules fail to match.
|
|
138
|
-
# This block is typially used to pass the request on to another server
|
|
139
|
-
# (i.e. recursive request).
|
|
140
|
-
#
|
|
141
|
-
# otherwise do |transaction|
|
|
142
|
-
# transaction.passthrough!($R)
|
|
143
|
-
# end
|
|
144
|
-
#
|
|
145
|
-
def otherwise(&block)
|
|
146
|
-
@otherwise = block
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def next!
|
|
150
|
-
throw :next
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# Give a name and a record type, try to match a rule and use it for
|
|
154
|
-
# processing the given arguments.
|
|
155
|
-
#
|
|
156
|
-
# If a rule returns false, it is considered that the rule failed and
|
|
157
|
-
# futher matching is carried out.
|
|
158
|
-
def process(name, resource_class, *args)
|
|
159
|
-
@logger.debug "Searching for #{name} #{resource_class.name}"
|
|
160
|
-
|
|
161
|
-
@rules.each do |rule|
|
|
162
|
-
@logger.debug "Checking rule #{rule}..."
|
|
163
|
-
|
|
164
|
-
catch (:next) do
|
|
165
|
-
# If the rule returns true, we assume that it was successful and no further rules need to be evaluated.
|
|
166
|
-
return true if rule.call(self, name, resource_class, *args)
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
if @otherwise
|
|
171
|
-
@otherwise.call(*args)
|
|
172
|
-
else
|
|
173
|
-
@logger.warn "Failed to handle #{name} #{resource_class.name}!"
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# Process an incoming DNS message. Returns a serialized message to be
|
|
178
|
-
# sent back to the client.
|
|
179
|
-
def process_query(query, options = {}, &block)
|
|
180
|
-
# Setup answer
|
|
181
|
-
answer = Resolv::DNS::Message::new(query.id)
|
|
182
|
-
answer.qr = 1 # 0 = Query, 1 = Response
|
|
183
|
-
answer.opcode = query.opcode # Type of Query; copy from query
|
|
184
|
-
answer.aa = 1 # Is this an authoritative response: 0 = No, 1 = Yes
|
|
185
|
-
answer.rd = query.rd # Is Recursion Desired, copied from query
|
|
186
|
-
answer.ra = 0 # Does name server support recursion: 0 = No, 1 = Yes
|
|
187
|
-
answer.rcode = 0 # Response code: 0 = No errors
|
|
188
|
-
|
|
189
|
-
# 1/ This chain contains a reverse list of question lambdas.
|
|
190
|
-
chain = []
|
|
191
|
-
|
|
192
|
-
# 4/ Finally, the answer is given back to the calling block:
|
|
193
|
-
chain << lambda do
|
|
194
|
-
@logger.debug "Passing answer back to caller..."
|
|
195
|
-
yield answer
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# There may be multiple questions per query
|
|
199
|
-
query.question.reverse.each do |question, resource_class|
|
|
200
|
-
next_link = chain.last
|
|
201
|
-
|
|
202
|
-
chain << lambda do
|
|
203
|
-
@logger.debug "Processing question #{question} #{resource_class}..."
|
|
204
|
-
|
|
205
|
-
transaction = Transaction.new(self, query, question, resource_class, answer, options)
|
|
206
|
-
|
|
207
|
-
# Call the next link in the chain:
|
|
208
|
-
transaction.callback do
|
|
209
|
-
# 3/ ... which calls the previous item in the chain, i.e. the next question to be answered:
|
|
210
|
-
next_link.call
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# If there was an error, log it and fail:
|
|
214
|
-
transaction.errback do |response|
|
|
215
|
-
if Exception === response
|
|
216
|
-
@logger.error "Exception thrown while processing #{transaction}!"
|
|
217
|
-
RubyDNS.log_exception(@logger, response)
|
|
218
|
-
else
|
|
219
|
-
@logger.error "Failure while processing #{transaction}!"
|
|
220
|
-
@logger.error "#{response.inspect}"
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
answer.rcode = Resolv::DNS::RCode::ServFail
|
|
224
|
-
|
|
225
|
-
chain.first.call
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
begin
|
|
229
|
-
# Transaction.process will call succeed if it wasn't deferred:
|
|
230
|
-
transaction.process
|
|
231
|
-
rescue
|
|
232
|
-
transaction.fail($!)
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
# 2/ We call the last lambda...
|
|
238
|
-
chain.last.call
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end
|
data/lib/rubydns/system.rb
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
# Copyright, 2009, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
-
#
|
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
# furnished to do so, subject to the following conditions:
|
|
9
|
-
#
|
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
|
11
|
-
# all copies or substantial portions of the Software.
|
|
12
|
-
#
|
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
-
# THE SOFTWARE.
|
|
20
|
-
|
|
21
|
-
begin
|
|
22
|
-
require 'win32/resolv'
|
|
23
|
-
rescue LoadError
|
|
24
|
-
# Ignore this - we aren't running on windows.
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
module RubyDNS
|
|
28
|
-
# This module encapsulates system dependent name lookup functionality.
|
|
29
|
-
module System
|
|
30
|
-
RESOLV_CONF = "/etc/resolv.conf"
|
|
31
|
-
HOSTS = "/etc/hosts"
|
|
32
|
-
|
|
33
|
-
def self.hosts_path
|
|
34
|
-
if RUBY_PLATFORM =~ /mswin32|mingw|bccwin/
|
|
35
|
-
Win32::Resolv.get_hosts_path
|
|
36
|
-
else
|
|
37
|
-
HOSTS
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# This code is very experimental
|
|
42
|
-
class Hosts
|
|
43
|
-
def initialize
|
|
44
|
-
@addresses = {}
|
|
45
|
-
@names = {}
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
attr :addresses
|
|
49
|
-
attr :names
|
|
50
|
-
|
|
51
|
-
# This is used to match names against the list of known hosts:
|
|
52
|
-
def call(name)
|
|
53
|
-
@names.include?(name)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def lookup(name)
|
|
57
|
-
addresses = @names[name]
|
|
58
|
-
|
|
59
|
-
if addresses
|
|
60
|
-
addresses.last
|
|
61
|
-
else
|
|
62
|
-
nil
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
alias [] lookup
|
|
67
|
-
|
|
68
|
-
def add(address, names)
|
|
69
|
-
@addresses[address] ||= []
|
|
70
|
-
@addresses[address] += names
|
|
71
|
-
|
|
72
|
-
names.each do |name|
|
|
73
|
-
@names[name] ||= []
|
|
74
|
-
@names[name] << address
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def parse_hosts(io)
|
|
79
|
-
io.each do |line|
|
|
80
|
-
line.sub!(/#.*/, '')
|
|
81
|
-
address, hostname, *aliases = line.split(/\s+/)
|
|
82
|
-
|
|
83
|
-
add(address, [hostname] + aliases)
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def self.local
|
|
88
|
-
hosts = self.new
|
|
89
|
-
|
|
90
|
-
path = System::hosts_path
|
|
91
|
-
|
|
92
|
-
if path and File.exist?(path)
|
|
93
|
-
File.open(path) do |file|
|
|
94
|
-
hosts.parse_hosts(file)
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
return hosts
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def self.parse_resolv_configuration(path)
|
|
103
|
-
nameservers = []
|
|
104
|
-
File.open(path) do |file|
|
|
105
|
-
file.each do |line|
|
|
106
|
-
# Remove any comments:
|
|
107
|
-
line.sub!(/[#;].*/, '')
|
|
108
|
-
|
|
109
|
-
# Extract resolv.conf command:
|
|
110
|
-
keyword, *args = line.split(/\s+/)
|
|
111
|
-
|
|
112
|
-
case keyword
|
|
113
|
-
when 'nameserver'
|
|
114
|
-
nameservers += args
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
return nameservers
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def self.standard_connections(nameservers)
|
|
123
|
-
connections = []
|
|
124
|
-
|
|
125
|
-
nameservers.each do |host|
|
|
126
|
-
connections << [:udp, host, 53]
|
|
127
|
-
connections << [:tcp, host, 53]
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
return connections
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Get a list of standard nameserver connections which can be used for querying any standard servers that the system has been configured with. There is no equivalent facility to use the `hosts` file at present.
|
|
134
|
-
def self.nameservers
|
|
135
|
-
nameservers = []
|
|
136
|
-
|
|
137
|
-
if File.exist? RESOLV_CONF
|
|
138
|
-
nameservers = parse_resolv_configuration(RESOLV_CONF)
|
|
139
|
-
elsif defined?(Win32::Resolv) and RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
|
|
140
|
-
search, nameservers = Win32::Resolv.get_resolv_info
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
return standard_connections(nameservers)
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
end
|