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.
Files changed (58) hide show
  1. checksums.yaml +6 -14
  2. data/.gitignore +23 -14
  3. data/.rspec +4 -0
  4. data/.simplecov +15 -0
  5. data/.travis.yml +9 -5
  6. data/.yardopts +1 -0
  7. data/Gemfile +6 -2
  8. data/README.md +82 -92
  9. data/Rakefile +2 -5
  10. data/bin/rubydns-check +374 -0
  11. data/examples/Gemfile +7 -0
  12. data/examples/README.md +137 -0
  13. data/examples/basic-dns.rb +24 -0
  14. data/examples/cname.rb +25 -0
  15. data/{test/examples/dropping-dns.rb → examples/flakey-dns.rb} +28 -31
  16. data/{test/examples → examples}/fortune-dns.rb +42 -46
  17. data/examples/geoip-dns.rb +115 -0
  18. data/examples/simple.rb +25 -0
  19. data/{test/examples → examples}/soa-dns.rb +27 -27
  20. data/{test/examples → examples}/test-dns-1.rb +26 -20
  21. data/{test/examples → examples}/test-dns-2.rb +17 -19
  22. data/examples/wikipedia-dns.rb +107 -0
  23. data/lib/rubydns/rule_based_server.rb +180 -0
  24. data/lib/rubydns/version.rb +1 -1
  25. data/lib/rubydns.rb +13 -63
  26. data/rubydns.gemspec +29 -23
  27. data/spec/rubydns/daemon_spec.rb +114 -0
  28. data/{test/test_system.rb → spec/rubydns/injected_supervisor_spec.rb} +32 -25
  29. data/spec/rubydns/passthrough_spec.rb +85 -0
  30. data/spec/rubydns/rules_spec.rb +74 -0
  31. data/spec/spec_helper.rb +30 -0
  32. metadata +101 -78
  33. data/bin/rd-dns-check +0 -374
  34. data/bin/rd-resolve-test +0 -160
  35. data/lib/rubydns/chunked.rb +0 -34
  36. data/lib/rubydns/extensions/hexdump.rb +0 -38
  37. data/lib/rubydns/extensions/logger.rb +0 -30
  38. data/lib/rubydns/extensions/resolv.rb +0 -53
  39. data/lib/rubydns/extensions/string-1.8.rb +0 -35
  40. data/lib/rubydns/extensions/string-1.9.2.rb +0 -29
  41. data/lib/rubydns/extensions/string-1.9.3.rb +0 -31
  42. data/lib/rubydns/extensions/string.rb +0 -27
  43. data/lib/rubydns/handler.rb +0 -140
  44. data/lib/rubydns/message.rb +0 -41
  45. data/lib/rubydns/resolver.rb +0 -239
  46. data/lib/rubydns/server.rb +0 -241
  47. data/lib/rubydns/system.rb +0 -146
  48. data/lib/rubydns/transaction.rb +0 -250
  49. data/test/examples/geoip-dns.rb +0 -86
  50. data/test/helper.rb +0 -9
  51. data/test/test_daemon.rb +0 -100
  52. data/test/test_domains.txt +0 -185
  53. data/test/test_passthrough.rb +0 -80
  54. data/test/test_resolver.rb +0 -105
  55. data/test/test_rules.rb +0 -74
  56. data/test/test_slow_server.rb +0 -98
  57. data/test/test_truncation.rb +0 -78
  58. /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
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module RubyDNS
22
- VERSION = "0.6.0"
22
+ VERSION = '2.0.0'
23
23
  end
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 'rubydns/version'
21
+ require 'async/dns'
22
22
 
23
- if RUBY_VERSION < "1.9"
24
- require 'rubydns/extensions/string-1.8'
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. A number of options can be supplied:
46
- #
47
- # <tt>:interfaces</tt>:: A set of sockets or addresses as defined below.
48
- #
49
- # One important feature of DNS is the port it runs on. The <tt>options[:listen]</tt>
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.fire(:stop)
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 |gem|
7
- gem.name = "rubydns"
8
- gem.version = RubyDNS::VERSION
9
- gem.authors = ["Samuel Williams"]
10
- gem.email = ["samuel.williams@oriontransfer.co.nz"]
11
- gem.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 (via RExec). 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.
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
- In addition, RubyDNS includes a high-performance asynchronous DNS resolver
18
- built on top of EventMachine. This module can be used by itself in client
19
- applications without using the full RubyDNS server stack.
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
- gem.summary = "An easy to use DNS server and resolver for Ruby."
22
- gem.homepage = "https://github.com/ioquatix/rubydns"
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
- gem.files = `git ls-files`.split($/)
25
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
26
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
27
- gem.require_paths = ["lib"]
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
- gem.add_dependency("rexec", "~> 1.5.1")
30
- gem.add_dependency("eventmachine", "~> 1.0.0")
31
-
32
- gem.has_rdoc = "yard"
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 'rubydns/system'
28
-
29
- require 'rexec'
24
+ require 'async/dns/extensions/string'
30
25
 
31
- class SystemTest < Test::Unit::TestCase
32
- def test_system_nameservers
33
- # There technically should be at least one nameserver:
34
- resolver = RubyDNS::Resolver.new(RubyDNS::System::nameservers)
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
- def test_hosts
47
- hosts = RubyDNS::System::Hosts.new
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
- # Load the test hosts data:
50
- File.open(File.expand_path("../hosts.txt", __FILE__)) do |file|
51
- hosts.parse_hosts(file)
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
- assert hosts.call('testing')
55
- assert_equal '1.2.3.4', hosts['testing']
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