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
|
@@ -0,0 +1,180 @@
|
|
|
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 'async/dns/server'
|
|
22
|
+
|
|
23
|
+
module RubyDNS
|
|
24
|
+
# Provides the core of the RubyDNS domain-specific language (DSL). It contains a list of rules which are used to match against incoming DNS questions. These rules are used to generate responses which are either DNS resource records or failures.
|
|
25
|
+
class RuleBasedServer < Async::DNS::Server
|
|
26
|
+
# Represents a single rule in the server.
|
|
27
|
+
class Rule
|
|
28
|
+
def initialize(pattern, callback)
|
|
29
|
+
@pattern = pattern
|
|
30
|
+
@callback = callback
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns true if the name and resource_class are sufficient:
|
|
34
|
+
def match(name, resource_class)
|
|
35
|
+
# If the pattern doesn't specify any resource classes, we implicitly pass this test:
|
|
36
|
+
return true if @pattern.size < 2
|
|
37
|
+
|
|
38
|
+
# Otherwise, we try to match against some specific resource classes:
|
|
39
|
+
if Class === @pattern[1]
|
|
40
|
+
@pattern[1] == resource_class
|
|
41
|
+
else
|
|
42
|
+
@pattern[1].include?(resource_class) rescue false
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Invoke the rule, if it matches the incoming request, it is evaluated and returns `true`, otherwise returns `false`.
|
|
47
|
+
def call(server, name, resource_class, transaction)
|
|
48
|
+
unless match(name, resource_class)
|
|
49
|
+
server.logger.debug "<#{transaction.query.id}> Resource class #{resource_class} failed to match #{@pattern[1].inspect}!"
|
|
50
|
+
|
|
51
|
+
return false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Does this rule match against the supplied name?
|
|
55
|
+
case @pattern[0]
|
|
56
|
+
when Regexp
|
|
57
|
+
match_data = @pattern[0].match(name)
|
|
58
|
+
|
|
59
|
+
if match_data
|
|
60
|
+
server.logger.debug "<#{transaction.query.id}> Regexp pattern matched with #{match_data.inspect}."
|
|
61
|
+
|
|
62
|
+
@callback[transaction, match_data]
|
|
63
|
+
|
|
64
|
+
return true
|
|
65
|
+
end
|
|
66
|
+
when String
|
|
67
|
+
if @pattern[0] == name
|
|
68
|
+
server.logger.debug "<#{transaction.query.id}> String pattern matched."
|
|
69
|
+
|
|
70
|
+
@callback[transaction]
|
|
71
|
+
|
|
72
|
+
return true
|
|
73
|
+
end
|
|
74
|
+
else
|
|
75
|
+
if (@pattern[0].call(name, resource_class) rescue false)
|
|
76
|
+
server.logger.debug "<#{transaction.query.id}> Callable pattern matched."
|
|
77
|
+
|
|
78
|
+
@callback[transaction]
|
|
79
|
+
|
|
80
|
+
return true
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
server.logger.debug "<#{transaction.query.id}> No pattern matched."
|
|
85
|
+
|
|
86
|
+
# We failed to match the pattern.
|
|
87
|
+
return false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def to_s
|
|
91
|
+
@pattern.inspect
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Instantiate a server with a block
|
|
96
|
+
#
|
|
97
|
+
# server = Server.new do
|
|
98
|
+
# match(/server.mydomain.com/, IN::A) do |transaction|
|
|
99
|
+
# transaction.respond!("1.2.3.4")
|
|
100
|
+
# end
|
|
101
|
+
# end
|
|
102
|
+
#
|
|
103
|
+
def initialize(*args, &block)
|
|
104
|
+
super(*args)
|
|
105
|
+
|
|
106
|
+
@events = {}
|
|
107
|
+
@rules = []
|
|
108
|
+
@otherwise = nil
|
|
109
|
+
|
|
110
|
+
if block_given?
|
|
111
|
+
instance_eval(&block)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
attr_accessor :logger
|
|
116
|
+
|
|
117
|
+
# This function connects a pattern with a block. A pattern is either a String or a Regex instance. Optionally, a second argument can be provided which is either a String, Symbol or Array of resource record types which the rule matches against.
|
|
118
|
+
#
|
|
119
|
+
# match("www.google.com")
|
|
120
|
+
# match("gmail.com", IN::MX)
|
|
121
|
+
# match(/g?mail.(com|org|net)/, [IN::MX, IN::A])
|
|
122
|
+
#
|
|
123
|
+
def match(*pattern, &block)
|
|
124
|
+
@rules << Rule.new(pattern, block)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Register a named event which may be invoked later using #fire
|
|
128
|
+
#
|
|
129
|
+
# on(:start) do |server|
|
|
130
|
+
# Process::Daemon::Permissions.change_user(RUN_AS)
|
|
131
|
+
# end
|
|
132
|
+
def on(event_name, &block)
|
|
133
|
+
@events[event_name] = block
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Fire the named event, which must have been registered using on.
|
|
137
|
+
def fire(event_name)
|
|
138
|
+
callback = @events[event_name]
|
|
139
|
+
|
|
140
|
+
if callback
|
|
141
|
+
callback.call(self)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Specify a default block to execute if all other rules fail to match. This block is typially used to pass the request on to another server (i.e. recursive request).
|
|
146
|
+
#
|
|
147
|
+
# otherwise do |transaction|
|
|
148
|
+
# transaction.passthrough!($R)
|
|
149
|
+
# end
|
|
150
|
+
#
|
|
151
|
+
def otherwise(&block)
|
|
152
|
+
@otherwise = block
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# If you match a rule, but decide within the rule that it isn't the correct one to use, you can call `next!` to evaluate the next rule - in other words, to continue falling down through the list of rules.
|
|
156
|
+
def next!
|
|
157
|
+
throw :next
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Give a name and a record type, try to match a rule and use it for processing the given arguments.
|
|
161
|
+
def process(name, resource_class, transaction)
|
|
162
|
+
@logger.debug {"<#{transaction.query.id}> Searching for #{name} #{resource_class.name}"}
|
|
163
|
+
|
|
164
|
+
@rules.each do |rule|
|
|
165
|
+
@logger.debug {"<#{transaction.query.id}> Checking rule #{rule}..."}
|
|
166
|
+
|
|
167
|
+
catch (:next) do
|
|
168
|
+
# If the rule returns true, we assume that it was successful and no further rules need to be evaluated.
|
|
169
|
+
return if rule.call(self, name, resource_class, transaction)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
if @otherwise
|
|
174
|
+
@otherwise.call(transaction)
|
|
175
|
+
else
|
|
176
|
+
@logger.warn "<#{transaction.query.id}> Failed to handle #{name} #{resource_class.name}!"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
data/lib/rubydns/version.rb
CHANGED
data/lib/rubydns.rb
CHANGED
|
@@ -18,74 +18,24 @@
|
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
19
|
# THE SOFTWARE.
|
|
20
20
|
|
|
21
|
-
require '
|
|
21
|
+
require 'async/dns'
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
elsif RUBY_VERSION < "1.9.3"
|
|
26
|
-
require 'rubydns/extensions/string-1.9.2'
|
|
27
|
-
else
|
|
28
|
-
require 'rubydns/extensions/string-1.9.3'
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
require 'rubydns/message'
|
|
32
|
-
require 'rubydns/server'
|
|
33
|
-
require 'rubydns/resolver'
|
|
34
|
-
require 'rubydns/handler'
|
|
35
|
-
|
|
36
|
-
require 'logger'
|
|
37
|
-
|
|
38
|
-
require 'rexec'
|
|
39
|
-
require 'rexec/daemon'
|
|
40
|
-
|
|
41
|
-
require 'rubydns/handler'
|
|
23
|
+
require_relative 'rubydns/version'
|
|
24
|
+
require_relative 'rubydns/rule_based_server'
|
|
42
25
|
|
|
43
26
|
module RubyDNS
|
|
27
|
+
# Backwards compatibility:
|
|
28
|
+
Resolver = Async::DNS::Resolver
|
|
44
29
|
|
|
45
|
-
# Run a server with the given rules.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# allows you to specify a set of network interfaces and ports to run the server on. This
|
|
51
|
-
# must be a list of <tt>[protocol, interface address, port]</tt>.
|
|
52
|
-
#
|
|
53
|
-
# INTERFACES = [[:udp, "0.0.0.0", 5300]]
|
|
54
|
-
# RubyDNS::run_server(:listen => INTERFACES) do
|
|
55
|
-
# ...
|
|
56
|
-
# end
|
|
57
|
-
#
|
|
58
|
-
# You can specify already connected sockets if need be:
|
|
59
|
-
#
|
|
60
|
-
# socket = UDPSocket.new; socket.bind("0.0.0.0", 53)
|
|
61
|
-
# Process::Sys.setuid(server_uid)
|
|
62
|
-
# INTERFACES = [socket]
|
|
63
|
-
#
|
|
64
|
-
# The default interface is <tt>[[:udp, "0.0.0.0", 53]]</tt>. The server typically needs
|
|
65
|
-
# to run as root for this to work, since port 53 is privileged.
|
|
66
|
-
#
|
|
67
|
-
def self.run_server (options = {}, &block)
|
|
68
|
-
server = RubyDNS::Server.new(&block)
|
|
69
|
-
server.logger.info "Starting RubyDNS server (v#{RubyDNS::VERSION})..."
|
|
70
|
-
|
|
71
|
-
options[:listen] ||= [[:udp, "0.0.0.0", 53], [:tcp, "0.0.0.0", 53]]
|
|
72
|
-
|
|
73
|
-
EventMachine.run do
|
|
74
|
-
server.fire(:setup)
|
|
75
|
-
|
|
76
|
-
# Setup server sockets
|
|
77
|
-
options[:listen].each do |spec|
|
|
78
|
-
server.logger.info "Listening on #{spec.join(':')}"
|
|
79
|
-
if spec[0] == :udp
|
|
80
|
-
EventMachine.open_datagram_socket(spec[1], spec[2], UDPHandler, server)
|
|
81
|
-
elsif spec[0] == :tcp
|
|
82
|
-
EventMachine.start_server(spec[1], spec[2], TCPHandler, server)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
server.fire(:start)
|
|
30
|
+
# Run a server with the given rules.
|
|
31
|
+
def self.run_server (*args, server_class: RuleBasedServer, **options, &block)
|
|
32
|
+
if listen = options.delete(:listen)
|
|
33
|
+
warn "Using `listen:` option is deprecated, please pass as the first argument."
|
|
34
|
+
args.unshift(listen)
|
|
87
35
|
end
|
|
88
36
|
|
|
89
|
-
server.
|
|
37
|
+
server = server_class.new(*args, **options, &block)
|
|
38
|
+
|
|
39
|
+
server.run
|
|
90
40
|
end
|
|
91
41
|
end
|
data/rubydns.gemspec
CHANGED
|
@@ -3,31 +3,37 @@ lib = File.expand_path('../lib', __FILE__)
|
|
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'rubydns/version'
|
|
5
5
|
|
|
6
|
-
Gem::Specification.new do |
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "rubydns"
|
|
8
|
+
spec.version = RubyDNS::VERSION
|
|
9
|
+
spec.authors = ["Samuel Williams"]
|
|
10
|
+
spec.email = ["samuel.williams@oriontransfer.co.nz"]
|
|
11
|
+
spec.description = <<-EOF
|
|
12
|
+
RubyDNS is a high-performance DNS server which can be easily integrated into
|
|
13
|
+
other projects or used as a stand-alone daemon. By default it uses
|
|
14
|
+
rule-based pattern matching. Results can be hard-coded, computed, fetched from
|
|
15
|
+
a remote DNS server or fetched from a local cache, depending on requirements.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
In addition, RubyDNS includes a high-performance asynchronous DNS resolver
|
|
18
|
+
built on top of Celluloid. This module can be used by itself in client
|
|
19
|
+
applications without using the full RubyDNS server stack.
|
|
20
20
|
EOF
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
spec.summary = "An easy to use DNS server and resolver for Ruby."
|
|
22
|
+
spec.homepage = "http://www.codeotaku.com/projects/rubydns"
|
|
23
|
+
spec.license = "MIT"
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
spec.files = `git ls-files`.split($/)
|
|
26
|
+
spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
27
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
28
|
+
spec.require_paths = ["lib"]
|
|
29
|
+
spec.has_rdoc = "yard"
|
|
30
|
+
|
|
31
|
+
spec.required_ruby_version = '>= 2.2.6'
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
spec.add_dependency("async-dns", "~> 1.0")
|
|
34
|
+
spec.add_development_dependency("async-rspec", "~> 1.0")
|
|
35
|
+
|
|
36
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
37
|
+
spec.add_development_dependency "rspec", "~> 3.4"
|
|
38
|
+
spec.add_development_dependency "rake"
|
|
33
39
|
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
# THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
require 'rubydns'
|
|
24
|
+
require 'process/daemon'
|
|
25
|
+
|
|
26
|
+
class BasicTestServer < Process::Daemon
|
|
27
|
+
SERVER_PORTS = [[:udp, '127.0.0.1', 5350], [:tcp, '127.0.0.1', 5350]]
|
|
28
|
+
|
|
29
|
+
IN = Resolv::DNS::Resource::IN
|
|
30
|
+
|
|
31
|
+
def working_directory
|
|
32
|
+
File.expand_path("../tmp", __FILE__)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def reactor
|
|
36
|
+
Async::Reactor.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def startup
|
|
40
|
+
reactor.run do
|
|
41
|
+
RubyDNS::run_server(SERVER_PORTS) do
|
|
42
|
+
match("test.local", IN::A) do |transaction|
|
|
43
|
+
transaction.respond!("192.168.1.1")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
match(/foo.*/, IN::A) do |transaction|
|
|
47
|
+
transaction.respond!("192.168.1.2")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
match(/peername/, IN::A) do |transaction|
|
|
51
|
+
transaction.respond!(transaction[:remote_address].ip_address)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Default DNS handler
|
|
55
|
+
otherwise do |transaction|
|
|
56
|
+
transaction.fail!(:NXDomain)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def shutdown
|
|
63
|
+
@reactor.stop
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
describe "RubyDNS Daemonized Server" do
|
|
68
|
+
include_context Async::RSpec::Reactor
|
|
69
|
+
|
|
70
|
+
before(:all) do
|
|
71
|
+
# BasicTestServer.controller output: $stderr
|
|
72
|
+
BasicTestServer.start
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
after(:all) do
|
|
76
|
+
BasicTestServer.stop
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should resolve local domain correctly" do
|
|
80
|
+
expect(BasicTestServer.status).to be == :running
|
|
81
|
+
|
|
82
|
+
resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
|
|
83
|
+
|
|
84
|
+
response = resolver.query("test.local")
|
|
85
|
+
|
|
86
|
+
answer = response.answer.first
|
|
87
|
+
|
|
88
|
+
expect(answer[0].to_s).to be == "test.local."
|
|
89
|
+
expect(answer[2].address.to_s).to be == "192.168.1.1"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "should pattern match correctly" do
|
|
93
|
+
expect(BasicTestServer.status).to be == :running
|
|
94
|
+
|
|
95
|
+
resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
|
|
96
|
+
|
|
97
|
+
response = resolver.query("foobar")
|
|
98
|
+
answer = response.answer.first
|
|
99
|
+
|
|
100
|
+
expect(answer[0]).to be == resolver.fully_qualified_name("foobar")
|
|
101
|
+
expect(answer[2].address.to_s).to be == "192.168.1.2"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should give peer ip address" do
|
|
105
|
+
expect(BasicTestServer.status).to be == :running
|
|
106
|
+
|
|
107
|
+
resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
|
|
108
|
+
|
|
109
|
+
response = resolver.query("peername")
|
|
110
|
+
answer = response.answer.first
|
|
111
|
+
|
|
112
|
+
expect(answer[2].address.to_s).to be == "127.0.0.1"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -20,38 +20,45 @@
|
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
21
|
# THE SOFTWARE.
|
|
22
22
|
|
|
23
|
-
require 'helper'
|
|
24
|
-
require 'pathname'
|
|
25
|
-
|
|
26
23
|
require 'rubydns'
|
|
27
|
-
require '
|
|
28
|
-
|
|
29
|
-
require 'rexec'
|
|
24
|
+
require 'async/dns/extensions/string'
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
EventMachine::run do
|
|
37
|
-
resolver.query('google.com') do |response|
|
|
38
|
-
assert_equal RubyDNS::Message, response.class
|
|
39
|
-
assert_equal Resolv::DNS::RCode::NoError, response.rcode
|
|
40
|
-
|
|
41
|
-
EventMachine::stop
|
|
42
|
-
end
|
|
26
|
+
module RubyDNS::InjectedSupervisorSpec
|
|
27
|
+
class TestServer < RubyDNS::RuleBasedServer
|
|
28
|
+
def test_message
|
|
29
|
+
'Testing...'
|
|
43
30
|
end
|
|
44
31
|
end
|
|
45
32
|
|
|
46
|
-
|
|
47
|
-
|
|
33
|
+
SERVER_PORTS = [[:udp, '127.0.0.1', 5520]]
|
|
34
|
+
IN = Resolv::DNS::Resource::IN
|
|
35
|
+
|
|
36
|
+
describe "RubyDNS::run_server(server_class: ...)" do
|
|
37
|
+
include_context Async::RSpec::Reactor
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
let(:server) do
|
|
40
|
+
# Start the RubyDNS server
|
|
41
|
+
RubyDNS::run_server(SERVER_PORTS, server_class: TestServer) do
|
|
42
|
+
match("test_message", IN::TXT) do |transaction|
|
|
43
|
+
transaction.respond!(*test_message.chunked)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Default DNS handler
|
|
47
|
+
otherwise do |transaction|
|
|
48
|
+
transaction.fail!(:NXDomain)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
52
51
|
end
|
|
53
52
|
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
it "should use the injected class" do
|
|
54
|
+
task = server
|
|
55
|
+
|
|
56
|
+
resolver = RubyDNS::Resolver.new(SERVER_PORTS)
|
|
57
|
+
response = resolver.query("test_message", IN::TXT)
|
|
58
|
+
text = response.answer.first
|
|
59
|
+
expect(text[2].strings.join).to be == 'Testing...'
|
|
60
|
+
|
|
61
|
+
task.stop
|
|
62
|
+
end
|
|
56
63
|
end
|
|
57
64
|
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
# THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
require 'rubydns'
|
|
24
|
+
|
|
25
|
+
module RubyDNS::PassthroughSpec
|
|
26
|
+
SERVER_PORTS = [[:udp, '127.0.0.1', 5340], [:tcp, '127.0.0.1', 5340]]
|
|
27
|
+
Name = Resolv::DNS::Name
|
|
28
|
+
IN = Resolv::DNS::Resource::IN
|
|
29
|
+
|
|
30
|
+
describe "RubyDNS Passthrough Server" do
|
|
31
|
+
include_context Async::RSpec::Reactor
|
|
32
|
+
|
|
33
|
+
def run_server
|
|
34
|
+
task = RubyDNS::run_server(listen: SERVER_PORTS) do
|
|
35
|
+
resolver = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
|
|
36
|
+
|
|
37
|
+
match(/.*\.com/, IN::A) do |transaction|
|
|
38
|
+
transaction.passthrough!(resolver)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
match(/a-(.*\.org)/) do |transaction, match_data|
|
|
42
|
+
transaction.passthrough!(resolver, :name => match_data[1])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Default DNS handler
|
|
46
|
+
otherwise do |transaction|
|
|
47
|
+
transaction.fail!(:NXDomain)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
yield
|
|
52
|
+
|
|
53
|
+
ensure
|
|
54
|
+
task.stop
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should resolve domain correctly" do
|
|
58
|
+
run_server do
|
|
59
|
+
resolver = RubyDNS::Resolver.new(SERVER_PORTS, timeout: 1)
|
|
60
|
+
|
|
61
|
+
response = resolver.query("google.com")
|
|
62
|
+
expect(response.ra).to be == 1
|
|
63
|
+
|
|
64
|
+
answer = response.answer.first
|
|
65
|
+
expect(answer).not_to be == nil
|
|
66
|
+
expect(answer.count).to be > 0
|
|
67
|
+
|
|
68
|
+
addresses = answer.select {|record| record.kind_of? Resolv::DNS::Resource::IN::A}
|
|
69
|
+
expect(addresses.size).to be > 0
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should resolve prefixed domain correctly" do
|
|
74
|
+
run_server do
|
|
75
|
+
resolver = RubyDNS::Resolver.new(SERVER_PORTS)
|
|
76
|
+
|
|
77
|
+
response = resolver.query("a-slashdot.org")
|
|
78
|
+
answer = response.answer.first
|
|
79
|
+
|
|
80
|
+
expect(answer).not_to be == nil
|
|
81
|
+
expect(answer.count).to be > 0
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
# THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
require 'rubydns'
|
|
24
|
+
|
|
25
|
+
module RubyDNS::RulesSpec
|
|
26
|
+
describe RubyDNS::RuleBasedServer do
|
|
27
|
+
IN = Resolv::DNS::Resource::IN
|
|
28
|
+
|
|
29
|
+
true_callback = Proc.new { true }
|
|
30
|
+
|
|
31
|
+
it "should match string patterns correctly" do
|
|
32
|
+
server = double(:logger => Logger.new("/dev/null"))
|
|
33
|
+
transaction = double(:query => Resolv::DNS::Message.new(0))
|
|
34
|
+
|
|
35
|
+
rule = RubyDNS::RuleBasedServer::Rule.new(["foobar", IN::A], true_callback)
|
|
36
|
+
|
|
37
|
+
expect(rule.call(server, "foobar", IN::A, transaction)).to be == true
|
|
38
|
+
expect(rule.call(server, "barfoo", IN::A, transaction)).to be == false
|
|
39
|
+
expect(rule.call(server, "foobar", IN::TXT, transaction)).to be == false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should match regular expression patterns correctly" do
|
|
43
|
+
server = double(:logger => Logger.new("/dev/null"))
|
|
44
|
+
transaction = double(:query => Resolv::DNS::Message.new(0))
|
|
45
|
+
|
|
46
|
+
rule = RubyDNS::RuleBasedServer::Rule.new([/foo/, IN::A], true_callback)
|
|
47
|
+
|
|
48
|
+
expect(rule.call(server, "foobar", IN::A, transaction)).to be == true
|
|
49
|
+
expect(rule.call(server, "barbaz", IN::A, transaction)).to be == false
|
|
50
|
+
expect(rule.call(server, "foobar", IN::TXT, transaction)).to be == false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should match callback patterns correctly" do
|
|
54
|
+
server = double(:logger => Logger.new("/dev/null"))
|
|
55
|
+
transaction = double(:query => Resolv::DNS::Message.new(0))
|
|
56
|
+
|
|
57
|
+
calls = 0
|
|
58
|
+
|
|
59
|
+
callback = Proc.new do |name, resource_class|
|
|
60
|
+
# A counter used to check the number of times this block was invoked.
|
|
61
|
+
calls += 1
|
|
62
|
+
|
|
63
|
+
name.size == 6
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
rule = RubyDNS::RuleBasedServer::Rule.new([callback], true_callback)
|
|
67
|
+
|
|
68
|
+
expect(rule.call(server, "foobar", IN::A, transaction)).to be == true
|
|
69
|
+
expect(rule.call(server, "foobarbaz", IN::A, transaction)).to be == false
|
|
70
|
+
|
|
71
|
+
expect(calls).to be == 2
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|