rubydns 0.1.8
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.rdoc +62 -0
- data/bin/rd-dns-check +370 -0
- data/bin/rd-resolve-test +155 -0
- data/lib/rubydns.rb +108 -0
- data/lib/rubydns/resolv.rb +76 -0
- data/lib/rubydns/server.rb +170 -0
- data/lib/rubydns/transaction.rb +208 -0
- data/lib/rubydns/version.rb +24 -0
- data/test/daemon1.rb +66 -0
- data/test/daemon2.rb +82 -0
- data/test/example1.rb +58 -0
- data/test/log/Server/stderr.log +657 -0
- data/test/log/Server/stdout.log +0 -0
- data/test/soa_example1.rb +74 -0
- data/test/test_domains.txt +185 -0
- metadata +89 -0
data/lib/rubydns.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
# Copyright (c) 2009 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
# Thanks to "jmorgan" who provided some basic ideas for how to do this
|
17
|
+
# using Ruby: http://half-penny.org/computing/simple-ruby-dns-server
|
18
|
+
|
19
|
+
require 'rubydns/version'
|
20
|
+
require 'rubydns/resolv'
|
21
|
+
require 'rubydns/server'
|
22
|
+
|
23
|
+
require 'logger'
|
24
|
+
|
25
|
+
require 'rexec'
|
26
|
+
require 'rexec/daemon'
|
27
|
+
|
28
|
+
module RubyDNS
|
29
|
+
|
30
|
+
# Run a server with the given rules. A number of options can be supplied:
|
31
|
+
#
|
32
|
+
# <tt>:interfaces</tt>:: A set of sockets or addresses as defined below.
|
33
|
+
#
|
34
|
+
# One important feature of DNS is the port it runs on. The <tt>options[:listen]</tt>
|
35
|
+
# allows you to specify a set of network interfaces and ports to run the server on. This
|
36
|
+
# must be a list of <tt>[protocol, interface address, port]</tt>.
|
37
|
+
#
|
38
|
+
# INTERFACES = [[:udp, "0.0.0.0", 5300]]
|
39
|
+
# RubyDNS::run_server(:listen => INTERFACES) do
|
40
|
+
# ...
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# You can specify already connected sockets if need be:
|
44
|
+
#
|
45
|
+
# socket = UDPSocket.new; socket.bind("0.0.0.0", 53)
|
46
|
+
# Process::Sys.setuid(server_uid)
|
47
|
+
# INTERFACES = [socket]
|
48
|
+
#
|
49
|
+
# The default interface is <tt>[[:udp, "0.0.0.0", 53]]</tt>. The server typically needs
|
50
|
+
# to run as root for this to work, since port 53 is privileged.
|
51
|
+
#
|
52
|
+
def self.run_server (options = {}, &block)
|
53
|
+
server = RubyDNS::Server.new(&block)
|
54
|
+
threads = ThreadGroup.new
|
55
|
+
|
56
|
+
server.logger.info "Starting server..."
|
57
|
+
|
58
|
+
options[:listen] ||= [[:udp, "0.0.0.0", 53]]
|
59
|
+
|
60
|
+
sockets = []
|
61
|
+
|
62
|
+
# Setup server sockets
|
63
|
+
options[:listen].each do |spec|
|
64
|
+
if spec.kind_of?(BasicSocket)
|
65
|
+
sockets << spec
|
66
|
+
elsif spec[0] == :udp
|
67
|
+
socket = UDPSocket.new
|
68
|
+
socket.bind(spec[1], spec[2])
|
69
|
+
|
70
|
+
sockets << socket
|
71
|
+
elsif spec[0] == :tcp
|
72
|
+
server.logger.warn "Sorry, TCP is not currently supported!"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
begin
|
77
|
+
# Listen for incoming packets
|
78
|
+
while true
|
79
|
+
ready = IO.select(sockets)
|
80
|
+
|
81
|
+
ready[0].each do |socket|
|
82
|
+
packet, sender = socket.recvfrom(1024*5)
|
83
|
+
server.logger.debug "Receiving incoming query..."
|
84
|
+
|
85
|
+
thr = Thread.new do
|
86
|
+
begin
|
87
|
+
result = server.receive_data(packet)
|
88
|
+
|
89
|
+
server.logger.debug "Sending result to #{sender.inspect}:"
|
90
|
+
server.logger.debug "#{result.inspect}"
|
91
|
+
socket.send(result, 0, sender[2], sender[1])
|
92
|
+
rescue
|
93
|
+
server.logger.error "Error processing request!"
|
94
|
+
server.logger.error "#{$!.class}: #{$!.message}"
|
95
|
+
$!.backtrace.each { |at| server.logger.error at }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
threads.add thr
|
100
|
+
end
|
101
|
+
end
|
102
|
+
rescue Interrupt
|
103
|
+
server.logger.info "Server interrupted - stopping #{threads.list.size} request(s)."
|
104
|
+
threads.list.each { |thr| thr.join }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright (c) 2009 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
# Resolv rdoc
|
17
|
+
# http://www.ruby-doc.org/stdlib/libdoc/resolv/rdoc/index.html
|
18
|
+
|
19
|
+
require 'resolv'
|
20
|
+
|
21
|
+
class Resolv
|
22
|
+
class DNS
|
23
|
+
# Queries the given DNS server and returns its response in its entirety.
|
24
|
+
# This allows such responses to be passed upstream with little or no
|
25
|
+
# modification/reinterpretation.
|
26
|
+
def query(name, typeclass)
|
27
|
+
lazy_initialize
|
28
|
+
requester = make_requester
|
29
|
+
senders = {}
|
30
|
+
begin
|
31
|
+
@config.resolv(name) {|candidate, tout, nameserver|
|
32
|
+
msg = Message.new
|
33
|
+
msg.rd = 1
|
34
|
+
msg.add_question(candidate, typeclass)
|
35
|
+
unless sender = senders[[candidate, nameserver]]
|
36
|
+
sender = senders[[candidate, nameserver]] =
|
37
|
+
requester.sender(msg, candidate, nameserver)
|
38
|
+
end
|
39
|
+
reply, reply_name = requester.request(sender, tout)
|
40
|
+
|
41
|
+
return reply, reply_name
|
42
|
+
}
|
43
|
+
ensure
|
44
|
+
requester.close
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Message
|
49
|
+
# Merge the given message with this message. A number of heuristics are
|
50
|
+
# applied in order to ensure that the result makes sense. For example,
|
51
|
+
# If the current message is not recursive but is being merged with a
|
52
|
+
# message that was recursive, this bit is maintained. If either message
|
53
|
+
# is authoritive, then the result is also authoritive.
|
54
|
+
#
|
55
|
+
# Modifies the current message in place.
|
56
|
+
def merge! (other)
|
57
|
+
# Authoritive Answer
|
58
|
+
@aa = @aa && other.aa
|
59
|
+
|
60
|
+
@additional += other.additional
|
61
|
+
@answer += other.answer
|
62
|
+
@authority += other.authority
|
63
|
+
@question += other.question
|
64
|
+
|
65
|
+
# Recursion Available
|
66
|
+
@ra = @ra || other.ra
|
67
|
+
|
68
|
+
# Result Code (Error Code)
|
69
|
+
@rcode = other.rcode unless other.rcode == 0
|
70
|
+
|
71
|
+
# Recursion Desired
|
72
|
+
@rd = @rd || other.rd
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# Copyright (c) 2009 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'rubydns/transaction'
|
17
|
+
|
18
|
+
module RubyDNS
|
19
|
+
|
20
|
+
# This class provides the core of the DSL. It contains a list of rules which
|
21
|
+
# are used to match against incoming DNS questions. These rules are used to
|
22
|
+
# generate responses which are either DNS resource records or failures.
|
23
|
+
class Server
|
24
|
+
|
25
|
+
# Instantiate a server with a block
|
26
|
+
#
|
27
|
+
# server = Server.new do
|
28
|
+
# match(/server.mydomain.com/, :A) do |transaction|
|
29
|
+
# transaction.respond!("1.2.3.4")
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
def initialize(&block)
|
34
|
+
@rules = []
|
35
|
+
@otherwise = nil
|
36
|
+
|
37
|
+
@logger = Logger.new($stderr)
|
38
|
+
|
39
|
+
if block_given?
|
40
|
+
instance_eval &block
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
attr :logger, true
|
45
|
+
|
46
|
+
# This function connects a pattern with a block. A pattern is either
|
47
|
+
# a String or a Regex instance. Optionally, a second argument can be
|
48
|
+
# provided which is either a String, Symbol or Array of resource record
|
49
|
+
# types which the rule matches against.
|
50
|
+
#
|
51
|
+
# match("www.google.com")
|
52
|
+
# match("gmail.com", :MX)
|
53
|
+
# match(/g?mail.(com|org|net)/, [:MX, :A])
|
54
|
+
#
|
55
|
+
def match (*pattern, &block)
|
56
|
+
# Normalize pattern
|
57
|
+
case pattern[1]
|
58
|
+
when nil
|
59
|
+
# Do nothing
|
60
|
+
when String
|
61
|
+
pattern[1] = pattern[1].upcase
|
62
|
+
when Symbol
|
63
|
+
pattern[1] = pattern[1].to_s.upcase
|
64
|
+
when Array
|
65
|
+
pattern[1] = pattern[1].collect { |v| v.to_s.upcase }
|
66
|
+
end
|
67
|
+
|
68
|
+
@rules << [pattern, Proc.new(&block)]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Specify a default block to execute if all other rules fail to match.
|
72
|
+
# This block is typially used to pass the request on to another server
|
73
|
+
# (i.e. recursive request).
|
74
|
+
#
|
75
|
+
# otherwise do |transaction|
|
76
|
+
# transaction.passthrough!($R)
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
def otherwise(&block)
|
80
|
+
@otherwise = Proc.new(&block)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Give a name and a record type, try to match a rule and use it for
|
84
|
+
# processing the given arguments.
|
85
|
+
#
|
86
|
+
# If a rule returns false, it is considered that the rule failed and
|
87
|
+
# futher matching is carried out.
|
88
|
+
def process(name, record_type, *args)
|
89
|
+
@logger.debug "Searching for #{name} #{record_type}"
|
90
|
+
|
91
|
+
@rules.each do |rule|
|
92
|
+
@logger.debug "Checking rule #{rule[0].inspect}..."
|
93
|
+
|
94
|
+
pattern = rule[0]
|
95
|
+
|
96
|
+
# Match failed against record_type?
|
97
|
+
case pattern[1]
|
98
|
+
when String
|
99
|
+
next if pattern[1] != record_type
|
100
|
+
@logger.debug "Resource type #{record_type} matched"
|
101
|
+
when Array
|
102
|
+
next if pattern[1].include?(record_type)
|
103
|
+
@logger.debug "Resource type #{record_type} matched #{pattern[1].inspect}"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Match succeeded against name?
|
107
|
+
case pattern[0]
|
108
|
+
when Regexp
|
109
|
+
match_data = pattern[0].match(name)
|
110
|
+
if match_data
|
111
|
+
@logger.debug "Query #{name} matched #{pattern[0].to_s} with result #{match_data.inspect}"
|
112
|
+
if rule[1].call(match_data, *args)
|
113
|
+
@logger.debug "Rule returned successfully"
|
114
|
+
return
|
115
|
+
end
|
116
|
+
else
|
117
|
+
@logger.debug "Query #{name} failed to match against #{pattern[0].to_s}"
|
118
|
+
end
|
119
|
+
when String
|
120
|
+
if pattern[0] == name
|
121
|
+
@logger.debug "Query #{name} matched #{pattern[0]}"
|
122
|
+
if rule[1].call(*args)
|
123
|
+
@logger.debug "Rule returned successfully"
|
124
|
+
return
|
125
|
+
end
|
126
|
+
else
|
127
|
+
@logger.debug "Query #{name} failed to match against #{pattern[0]}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if @otherwise
|
133
|
+
@otherwise.call(*args)
|
134
|
+
else
|
135
|
+
@logger.warn "Failed to handle #{name} #{record_type}!"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Process an incoming DNS message. Returns a serialized message to be
|
140
|
+
# sent back to the client.
|
141
|
+
def receive_data(data)
|
142
|
+
query = Resolv::DNS::Message::decode(data)
|
143
|
+
|
144
|
+
# Setup answer
|
145
|
+
answer = Resolv::DNS::Message::new(query.id)
|
146
|
+
answer.qr = 1 # 0 = Query, 1 = Response
|
147
|
+
answer.opcode = query.opcode # Type of Query; copy from query
|
148
|
+
answer.aa = 1 # Is this an authoritative response: 0 = No, 1 = Yes
|
149
|
+
answer.rd = query.rd # Is Recursion Desired, copied from query
|
150
|
+
answer.ra = 0 # Does name server support recursion: 0 = No, 1 = Yes
|
151
|
+
answer.rcode = 0 # Response code: 0 = No errors
|
152
|
+
|
153
|
+
query.each_question do |question, resource_class| # There may be multiple questions per query
|
154
|
+
transaction = Transaction.new(self, query, question, resource_class, answer)
|
155
|
+
|
156
|
+
begin
|
157
|
+
transaction.process
|
158
|
+
rescue
|
159
|
+
@logger.error "Exception thrown while processing #{transaction}!"
|
160
|
+
@logger.error "#{$!.class}: #{$!.message}"
|
161
|
+
$!.backtrace.each { |at| @logger.error at }
|
162
|
+
|
163
|
+
answer.rcode = Resolv::DNS::RCode::ServFail
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
return answer.encode
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# Copyright (c) 2009 Samuel Williams. Released under the GNU GPLv3.
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
module RubyDNS
|
17
|
+
|
18
|
+
# Turn a symbol or string name into a resource class. For example,
|
19
|
+
# convert <tt>:A</tt> into <tt>Resolv::DNS::Resource::IN::A</tt>
|
20
|
+
def self.lookup_resource_class(klass)
|
21
|
+
return nil if klass == nil
|
22
|
+
|
23
|
+
if Symbol === klass
|
24
|
+
klass = klass.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
if String === klass
|
28
|
+
if Resolv::DNS::Resource.const_defined?(klass)
|
29
|
+
return Resolv::DNS::Resource.const_get(klass)
|
30
|
+
elsif Resolv::DNS::Resource::IN.const_defined?(klass)
|
31
|
+
return Resolv::DNS::Resource::IN.const_get(klass)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
return klass
|
36
|
+
end
|
37
|
+
|
38
|
+
# This class provides all details of a single DNS question and answer. This
|
39
|
+
# is used by the DSL to provide DNS related functionality.
|
40
|
+
class Transaction
|
41
|
+
def initialize(server, query, question, resource_class, answer)
|
42
|
+
@server = server
|
43
|
+
@query = query
|
44
|
+
@question = question
|
45
|
+
@resource_class = resource_class
|
46
|
+
@answer = answer
|
47
|
+
|
48
|
+
@question_appended = false
|
49
|
+
end
|
50
|
+
|
51
|
+
# The resource_class that was requested. This is typically used to generate a
|
52
|
+
# response.
|
53
|
+
attr :resource_class
|
54
|
+
|
55
|
+
# The incoming query which is a set of questions.
|
56
|
+
attr :query
|
57
|
+
|
58
|
+
# The question that this transaction represents.
|
59
|
+
attr :question
|
60
|
+
|
61
|
+
# The current full answer to the incoming query.
|
62
|
+
attr :answer
|
63
|
+
|
64
|
+
# Return the type of record (eg. <tt>A</tt>, <tt>MX</tt>) as a <tt>String</tt>.
|
65
|
+
def record_type
|
66
|
+
@resource_class.name.split("::").last
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return the name of the question, which is typically the requested hostname.
|
70
|
+
def name
|
71
|
+
@question.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
# Suitable for debugging purposes
|
75
|
+
def to_s
|
76
|
+
"#{name} #{record_type}"
|
77
|
+
end
|
78
|
+
|
79
|
+
# Run a new query through the rules with the given name and resource type. The
|
80
|
+
# results of this query are appended to the current transactions <tt>answer</tt>.
|
81
|
+
def append_query!(name, resource_type = nil)
|
82
|
+
Transaction.new(@server, @query, name, RubyDNS.lookup_resource_class(resource_type) || @resource_class, @answer).process
|
83
|
+
end
|
84
|
+
|
85
|
+
def process
|
86
|
+
@server.process(name, record_type, self)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Use the given resolver to respond to the question. This will <tt>query</tt>
|
90
|
+
# the resolver and <tt>merge!</tt> the answer if one is received. If recursion is
|
91
|
+
# not requested, the result is <tt>failure!(:Refused)</tt>. If the resolver does
|
92
|
+
# not respond, the result is <tt>failure!(:NXDomain)</tt>
|
93
|
+
#
|
94
|
+
# If a block is supplied, this function yields with the reply and reply_name if
|
95
|
+
# successful. This could be used, for example, to update a cache.
|
96
|
+
def passthrough! (resolver, &block)
|
97
|
+
# Were we asked to recursively find this name?
|
98
|
+
if @query.rd
|
99
|
+
reply, reply_name = resolver.query(name, resource_class)
|
100
|
+
|
101
|
+
if reply
|
102
|
+
if block_given?
|
103
|
+
yield(reply, reply_name)
|
104
|
+
end
|
105
|
+
|
106
|
+
@answer.merge!(reply)
|
107
|
+
else
|
108
|
+
failure!(:NXDomain)
|
109
|
+
end
|
110
|
+
else
|
111
|
+
failure!(:Refused)
|
112
|
+
end
|
113
|
+
|
114
|
+
true
|
115
|
+
end
|
116
|
+
|
117
|
+
# Respond to the given query with a resource record. The arguments to this
|
118
|
+
# function depend on the <tt>resource_class</tt> requested. The last argument
|
119
|
+
# can optionally be a hash of options.
|
120
|
+
#
|
121
|
+
# <tt>options[:resource_class]</tt>:: Override the default <tt>resource_class</tt>
|
122
|
+
# <tt>options[:ttl]</tt>:: Specify the TTL for the resource
|
123
|
+
# <tt>options[:name]</tt>:: Override the name (question) of the response.
|
124
|
+
#
|
125
|
+
# for A records:: <tt>respond!("1.2.3.4")</tt>
|
126
|
+
# for MX records:: <tt>respond!("mail.blah.com", 10)</tt>
|
127
|
+
#
|
128
|
+
# This function instantiates the resource class with the supplied arguments, and
|
129
|
+
# then passes it to <tt>append!</tt>.
|
130
|
+
#
|
131
|
+
# See <tt>Resolv::DNS::Resource</tt> for more information about the various
|
132
|
+
# <tt>resource_class</tt>s available.
|
133
|
+
# http://www.ruby-doc.org/stdlib/libdoc/resolv/rdoc/index.html
|
134
|
+
def respond! (*data)
|
135
|
+
options = data.last.kind_of?(Hash) ? data.pop : {}
|
136
|
+
|
137
|
+
case options[:resource_class]
|
138
|
+
when nil
|
139
|
+
append!(@resource_class.new(*data), options)
|
140
|
+
when Class
|
141
|
+
append!(options[:resource_class].new(*data), options)
|
142
|
+
else
|
143
|
+
raise ArgumentError, "Could not instantiate resource!"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Append a given set of resources to the answer. The last argument can
|
148
|
+
# optionally be a hash of options.
|
149
|
+
#
|
150
|
+
# <tt>options[:ttl]</tt>:: Specify the TTL for the resource
|
151
|
+
# <tt>options[:name]</tt>:: Override the name (question) of the response.
|
152
|
+
#
|
153
|
+
# This function can be used to supply multiple responses to a given question.
|
154
|
+
# For example, each argument is expected to be an instantiated resource from
|
155
|
+
# <tt>Resolv::DNS::Resource</tt> module.
|
156
|
+
def append! (*resources)
|
157
|
+
append_question!
|
158
|
+
|
159
|
+
options = resources.last.kind_of?(Hash) ? resources.pop.dup : {}
|
160
|
+
options[:ttl] ||= 16000
|
161
|
+
options[:name] ||= @question.to_s + "."
|
162
|
+
|
163
|
+
resources.each do |resource|
|
164
|
+
@answer.add_answer(options[:name], options[:ttl], resource)
|
165
|
+
end
|
166
|
+
|
167
|
+
@answer.encode
|
168
|
+
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
# This function indicates that there was a failure to resolve the given
|
173
|
+
# question. The single argument must be an integer error code, typically
|
174
|
+
# given by the constants in <tt>Resolv::DNS::RCode</tt>.
|
175
|
+
#
|
176
|
+
# The easiest way to use this function it to simply supply a symbol. Here is
|
177
|
+
# a list of the most commonly used ones:
|
178
|
+
#
|
179
|
+
# <tt>:NoError</tt>:: No error occurred.
|
180
|
+
# <tt>:FormErr</tt>:: The incoming data was not formatted correctly.
|
181
|
+
# <tt>:ServFail</tt>:: The operation caused a server failure (internal error, etc).
|
182
|
+
# <tt>:NXDomain</tt>:: Non-eXistant Domain (domain record does not exist).
|
183
|
+
# <tt>:NotImp</tt>:: The operation requested is not implemented.
|
184
|
+
# <tt>:Refused</tt>:: The operation was refused by the server.
|
185
|
+
# <tt>:NotAuth</tt>:: The server is not authoritive for the zone.
|
186
|
+
#
|
187
|
+
# See http://www.rfc-editor.org/rfc/rfc2929.txt for more information
|
188
|
+
# about DNS error codes (specifically, page 3).
|
189
|
+
def failure! (rcode)
|
190
|
+
append_question!
|
191
|
+
|
192
|
+
if rcode.kind_of? Symbol
|
193
|
+
@answer.rcode = Resolv::DNS::RCode.const_get(rcode)
|
194
|
+
else
|
195
|
+
@answer.rcode = rcode.to_i
|
196
|
+
end
|
197
|
+
|
198
|
+
true
|
199
|
+
end
|
200
|
+
|
201
|
+
protected
|
202
|
+
def append_question!
|
203
|
+
if @answer.question.size == 0
|
204
|
+
@answer.add_question(@question, @resource_class) unless @question_appended
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|