celluloid-dns 0.0.1 → 0.17.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +0 -2
- data/.simplecov +15 -0
- data/.travis.yml +13 -7
- data/Gemfile +5 -6
- data/README.md +118 -41
- data/Rakefile +8 -3
- data/celluloid-dns.gemspec +29 -18
- data/lib/celluloid/dns.rb +30 -7
- data/lib/celluloid/dns/chunked.rb +34 -0
- data/lib/celluloid/dns/extensions/resolv.rb +136 -0
- data/lib/celluloid/dns/extensions/string.rb +28 -0
- data/lib/celluloid/dns/handler.rb +198 -0
- data/lib/celluloid/dns/logger.rb +31 -0
- data/lib/celluloid/dns/message.rb +76 -0
- data/lib/celluloid/dns/replace.rb +54 -0
- data/lib/celluloid/dns/resolver.rb +288 -0
- data/lib/celluloid/dns/server.rb +151 -27
- data/lib/celluloid/dns/system.rb +146 -0
- data/lib/celluloid/dns/transaction.rb +202 -0
- data/lib/celluloid/dns/transport.rb +75 -0
- data/lib/celluloid/dns/version.rb +23 -3
- data/spec/celluloid/dns/celluloid_bug_spec.rb +92 -0
- data/spec/celluloid/dns/hosts.txt +2 -0
- data/spec/celluloid/dns/ipv6_spec.rb +70 -0
- data/spec/celluloid/dns/message_spec.rb +56 -0
- data/spec/celluloid/dns/origin_spec.rb +106 -0
- data/spec/celluloid/dns/replace_spec.rb +42 -0
- data/spec/celluloid/dns/resolver_performance_spec.rb +110 -0
- data/spec/celluloid/dns/resolver_spec.rb +152 -0
- data/spec/celluloid/dns/server/bind9/generate-local.rb +25 -0
- data/spec/celluloid/dns/server/bind9/local.zone +5014 -0
- data/spec/celluloid/dns/server/bind9/named.conf +14 -0
- data/spec/celluloid/dns/server/bind9/named.run +0 -0
- data/spec/celluloid/dns/server/million.rb +85 -0
- data/spec/celluloid/dns/server_performance_spec.rb +139 -0
- data/spec/celluloid/dns/slow_server_spec.rb +91 -0
- data/spec/celluloid/dns/socket_spec.rb +71 -0
- data/spec/celluloid/dns/system_spec.rb +60 -0
- data/spec/celluloid/dns/transaction_spec.rb +138 -0
- data/spec/celluloid/dns/truncation_spec.rb +62 -0
- metadata +124 -56
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -17
- data/CHANGES.md +0 -3
- data/LICENSE.txt +0 -22
- data/lib/celluloid/dns/request.rb +0 -46
- data/spec/celluloid/dns/server_spec.rb +0 -26
- data/spec/spec_helper.rb +0 -5
- data/tasks/rspec.task +0 -7
data/lib/celluloid/dns/server.rb
CHANGED
@@ -1,27 +1,151 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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 'celluloid/io'
|
22
|
+
|
23
|
+
require_relative 'transaction'
|
24
|
+
require_relative 'logger'
|
25
|
+
|
26
|
+
module Celluloid::DNS
|
27
|
+
class Server
|
28
|
+
include Celluloid::IO
|
29
|
+
|
30
|
+
# The default server interfaces
|
31
|
+
DEFAULT_INTERFACES = [[:udp, "0.0.0.0", 53], [:tcp, "0.0.0.0", 53]]
|
32
|
+
|
33
|
+
# Instantiate a server with a block
|
34
|
+
#
|
35
|
+
# server = Server.new do
|
36
|
+
# match(/server.mydomain.com/, IN::A) do |transaction|
|
37
|
+
# transaction.respond!("1.2.3.4")
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
def initialize(options = {})
|
42
|
+
@logger = options[:logger] || Celluloid.logger
|
43
|
+
@interfaces = options[:listen] || DEFAULT_INTERFACES
|
44
|
+
|
45
|
+
@origin = options[:origin] || '.'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Records are relative to this origin:
|
49
|
+
attr_accessor :origin
|
50
|
+
|
51
|
+
attr_accessor :logger
|
52
|
+
|
53
|
+
# Fire the named event as part of running the server.
|
54
|
+
def fire(event_name)
|
55
|
+
end
|
56
|
+
|
57
|
+
finalizer def stop
|
58
|
+
# Celluloid.logger.debug(self.class.name) {"-> Shutdown..."}
|
59
|
+
|
60
|
+
fire(:stop)
|
61
|
+
|
62
|
+
# Celluloid.logger.debug(self.class.name) {"<- Shutdown..."}
|
63
|
+
end
|
64
|
+
|
65
|
+
# Give a name and a record type, try to match a rule and use it for processing the given arguments.
|
66
|
+
def process(name, resource_class, transaction)
|
67
|
+
raise NotImplementedError.new
|
68
|
+
end
|
69
|
+
|
70
|
+
# Process an incoming DNS message. Returns a serialized message to be sent back to the client.
|
71
|
+
def process_query(query, options = {}, &block)
|
72
|
+
start_time = Time.now
|
73
|
+
|
74
|
+
# Setup response
|
75
|
+
response = Resolv::DNS::Message::new(query.id)
|
76
|
+
response.qr = 1 # 0 = Query, 1 = Response
|
77
|
+
response.opcode = query.opcode # Type of Query; copy from query
|
78
|
+
response.aa = 1 # Is this an authoritative response: 0 = No, 1 = Yes
|
79
|
+
response.rd = query.rd # Is Recursion Desired, copied from query
|
80
|
+
response.ra = 0 # Does name server support recursion: 0 = No, 1 = Yes
|
81
|
+
response.rcode = 0 # Response code: 0 = No errors
|
82
|
+
|
83
|
+
transaction = nil
|
84
|
+
|
85
|
+
begin
|
86
|
+
query.question.each do |question, resource_class|
|
87
|
+
begin
|
88
|
+
question = question.without_origin(@origin)
|
89
|
+
|
90
|
+
@logger.debug {"<#{query.id}> Processing question #{question} #{resource_class}..."}
|
91
|
+
|
92
|
+
transaction = Transaction.new(self, query, question, resource_class, response, options)
|
93
|
+
|
94
|
+
transaction.process
|
95
|
+
rescue Resolv::DNS::OriginError
|
96
|
+
# This is triggered if the question is not part of the specified @origin:
|
97
|
+
@logger.debug {"<#{query.id}> Skipping question #{question} #{resource_class} because #{$!}"}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
rescue StandardError => error
|
101
|
+
@logger.error "<#{query.id}> Exception thrown while processing #{transaction}!"
|
102
|
+
Celluloid::DNS.log_exception(@logger, error)
|
103
|
+
|
104
|
+
response.rcode = Resolv::DNS::RCode::ServFail
|
105
|
+
end
|
106
|
+
|
107
|
+
end_time = Time.now
|
108
|
+
@logger.debug {"<#{query.id}> Time to process request: #{end_time - start_time}s"}
|
109
|
+
|
110
|
+
return response
|
111
|
+
end
|
112
|
+
|
113
|
+
# Setup all specified interfaces and begin accepting incoming connections.
|
114
|
+
def run
|
115
|
+
@logger.info "Starting Celluloid::DNS server (v#{Celluloid::DNS::VERSION})..."
|
116
|
+
|
117
|
+
fire(:setup)
|
118
|
+
|
119
|
+
# Setup server sockets
|
120
|
+
@interfaces.each do |spec|
|
121
|
+
if spec.is_a?(BasicSocket)
|
122
|
+
spec.do_not_reverse_lookup
|
123
|
+
protocol = spec.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).unpack("i")[0]
|
124
|
+
ip = spec.local_address.ip_address
|
125
|
+
port = spec.local_address.ip_port
|
126
|
+
|
127
|
+
case protocol
|
128
|
+
when Socket::SOCK_DGRAM
|
129
|
+
@logger.info "<> Attaching to pre-existing UDP socket #{ip}:#{port}"
|
130
|
+
link UDPSocketHandler.new(self, Celluloid::IO::Socket.try_convert(spec))
|
131
|
+
when Socket::SOCK_STREAM
|
132
|
+
@logger.info "<> Attaching to pre-existing TCP socket #{ip}:#{port}"
|
133
|
+
link TCPSocketHandler.new(self, Celluloid::IO::Socket.try_convert(spec))
|
134
|
+
else
|
135
|
+
raise ArgumentError.new("Unknown socket protocol: #{protocol}")
|
136
|
+
end
|
137
|
+
elsif spec[0] == :udp
|
138
|
+
@logger.info "<> Listening on #{spec.join(':')}"
|
139
|
+
link UDPHandler.new(self, spec[1], spec[2])
|
140
|
+
elsif spec[0] == :tcp
|
141
|
+
@logger.info "<> Listening on #{spec.join(':')}"
|
142
|
+
link TCPHandler.new(self, spec[1], spec[2])
|
143
|
+
else
|
144
|
+
raise ArgumentError.new("Invalid connection specification: #{spec.inspect}")
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
fire(:start)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,146 @@
|
|
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 Celluloid::DNS
|
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
|
@@ -0,0 +1,202 @@
|
|
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
|
+
module Celluloid::DNS
|
22
|
+
|
23
|
+
# This class provides all details of a single DNS question and response. This is used by the DSL to provide DNS related functionality.
|
24
|
+
#
|
25
|
+
# 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).
|
26
|
+
class Transaction
|
27
|
+
# The default time used for responses (24 hours).
|
28
|
+
DEFAULT_TTL = 86400
|
29
|
+
|
30
|
+
def initialize(server, query, question, resource_class, response, options = {})
|
31
|
+
@server = server
|
32
|
+
@query = query
|
33
|
+
@question = question
|
34
|
+
@resource_class = resource_class
|
35
|
+
@response = response
|
36
|
+
|
37
|
+
@options = options
|
38
|
+
end
|
39
|
+
|
40
|
+
# The resource_class that was requested. This is typically used to generate a response.
|
41
|
+
attr :resource_class
|
42
|
+
|
43
|
+
# The incoming query which is a set of questions.
|
44
|
+
attr :query
|
45
|
+
|
46
|
+
# The question that this transaction represents.
|
47
|
+
attr :question
|
48
|
+
|
49
|
+
# The current full response to the incoming query.
|
50
|
+
attr :response
|
51
|
+
|
52
|
+
# Any options or configuration associated with the given transaction.
|
53
|
+
attr :options
|
54
|
+
|
55
|
+
def [] key
|
56
|
+
@options[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
# The name of the question, which is typically the requested hostname.
|
60
|
+
def name
|
61
|
+
@question.to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
# Shows the question name and resource class. Suitable for debugging purposes.
|
65
|
+
def to_s
|
66
|
+
"#{name} #{@resource_class.name}"
|
67
|
+
end
|
68
|
+
|
69
|
+
# 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`.
|
70
|
+
def append!(name, resource_class = nil, options = {})
|
71
|
+
Transaction.new(@server, @query, name, resource_class || @resource_class, @response, options).process
|
72
|
+
end
|
73
|
+
|
74
|
+
# Use the given resolver to respond to the question. Uses `passthrough` to do the lookup and merges the result.
|
75
|
+
#
|
76
|
+
# 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.
|
77
|
+
#
|
78
|
+
# If recursion is not requested, the result is `fail!(:Refused)`. This check is ignored if an explicit `options[:name]` or `options[:force]` is given.
|
79
|
+
#
|
80
|
+
# If the resolver can't reach upstream servers, `fail!(:ServFail)` is invoked.
|
81
|
+
def passthrough!(resolver, options = {}, &block)
|
82
|
+
if @query.rd || options[:force] || options[:name]
|
83
|
+
response = passthrough(resolver, options)
|
84
|
+
|
85
|
+
if response
|
86
|
+
yield response if block_given?
|
87
|
+
|
88
|
+
# Recursion is available and is being used:
|
89
|
+
# See issue #26 for more details.
|
90
|
+
@response.ra = 1
|
91
|
+
@response.merge!(response)
|
92
|
+
else
|
93
|
+
fail!(:ServFail)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
fail!(:Refused)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Use the given resolver to respond to the question.
|
101
|
+
#
|
102
|
+
# 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.
|
103
|
+
#
|
104
|
+
# If `options[:name]` is provided, this overrides the default query name sent to the upstream server. The same logic applies to `options[:resource_class]`.
|
105
|
+
def passthrough(resolver, options = {})
|
106
|
+
query_name = options[:name] || name
|
107
|
+
query_resource_class = options[:resource_class] || resource_class
|
108
|
+
|
109
|
+
resolver.query(query_name, query_resource_class)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Respond to the given query with a resource record. The arguments to this function depend on the `resource_class` requested. This function instantiates the resource class with the supplied arguments, and then passes it to {#append!}.
|
113
|
+
#
|
114
|
+
# e.g. For A records: `respond!("1.2.3.4")`, For MX records: `respond!(10, Name.create("mail.blah.com"))`
|
115
|
+
|
116
|
+
# 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!}.
|
117
|
+
#
|
118
|
+
# See `Resolv::DNS::Resource` for more information about the various `resource_classes` available (http://www.ruby-doc.org/stdlib/libdoc/resolv/rdoc/index.html).
|
119
|
+
def respond!(*args)
|
120
|
+
append_question!
|
121
|
+
|
122
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
123
|
+
resource_class = options[:resource_class] || @resource_class
|
124
|
+
|
125
|
+
if resource_class == nil
|
126
|
+
raise ArgumentError.new("Could not instantiate resource #{resource_class}!")
|
127
|
+
end
|
128
|
+
|
129
|
+
resource = resource_class.new(*args)
|
130
|
+
|
131
|
+
add([resource], options)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Append a list of resources.
|
135
|
+
#
|
136
|
+
# By default resources are appended to the `answers` section, but this can be changed by setting `options[:section]` to either `:authority` or `:additional`.
|
137
|
+
#
|
138
|
+
# The time-to-live (TTL) of the resources can be specified using `options[:ttl]` and defaults to `DEFAULT_TTL`.
|
139
|
+
def add(resources, options = {})
|
140
|
+
# Use the default options if provided:
|
141
|
+
options = options.merge(@options)
|
142
|
+
|
143
|
+
ttl = options[:ttl] || DEFAULT_TTL
|
144
|
+
name = options[:name] || @question.to_s + "."
|
145
|
+
|
146
|
+
section = (options[:section] || 'answer').to_sym
|
147
|
+
method = "add_#{section}".to_sym
|
148
|
+
|
149
|
+
resources.each do |resource|
|
150
|
+
@server.logger.debug "#{method}: #{resource.inspect} #{resource.class::TypeValue} #{resource.class::ClassValue}"
|
151
|
+
|
152
|
+
@response.send(method, name, ttl, resource)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# 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}.
|
157
|
+
#
|
158
|
+
# The easiest way to use this function it to simply supply a symbol. Here is a list of the most commonly used ones:
|
159
|
+
#
|
160
|
+
# - `:NoError`: No error occurred.
|
161
|
+
# - `:FormErr`: The incoming data was not formatted correctly.
|
162
|
+
# - `:ServFail`: The operation caused a server failure (internal error, etc).
|
163
|
+
# - `:NXDomain`: Non-eXistant Domain (domain record does not exist).
|
164
|
+
# - `:NotImp`: The operation requested is not implemented.
|
165
|
+
# - `:Refused`: The operation was refused by the server.
|
166
|
+
# - `:NotAuth`: The server is not authoritive for the zone.
|
167
|
+
#
|
168
|
+
# See [RFC2929](http://www.rfc-editor.org/rfc/rfc2929.txt) for more information about DNS error codes (specifically, page 3).
|
169
|
+
#
|
170
|
+
# **This function will complete deferred transactions.**
|
171
|
+
def fail!(rcode)
|
172
|
+
append_question!
|
173
|
+
|
174
|
+
if rcode.kind_of? Symbol
|
175
|
+
@response.rcode = Resolv::DNS::RCode.const_get(rcode)
|
176
|
+
else
|
177
|
+
@response.rcode = rcode.to_i
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# @deprecated
|
182
|
+
def failure!(*args)
|
183
|
+
@server.logger.warn "failure! is deprecated, use fail! instead"
|
184
|
+
|
185
|
+
fail!(*args)
|
186
|
+
end
|
187
|
+
|
188
|
+
# A helper method to process the transaction on the given server. Unless the transaction is deferred, it will {#succeed} on completion.
|
189
|
+
def process
|
190
|
+
@server.process(name, @resource_class, self)
|
191
|
+
end
|
192
|
+
|
193
|
+
protected
|
194
|
+
|
195
|
+
# 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.
|
196
|
+
def append_question!
|
197
|
+
if @response.question.size == 0
|
198
|
+
@response.add_question(@question, @resource_class)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|