rubydns 0.8.5 → 0.9.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -0
  3. data/Gemfile +0 -4
  4. data/README.md +16 -8
  5. data/Rakefile +6 -6
  6. data/{test/examples → examples}/dropping-dns.rb +0 -0
  7. data/lib/rubydns/handler.rb +133 -102
  8. data/lib/rubydns/message.rb +0 -1
  9. data/lib/rubydns/resolver.rb +135 -187
  10. data/lib/rubydns/server.rb +92 -86
  11. data/lib/rubydns/transaction.rb +24 -48
  12. data/lib/rubydns/{binary_string.rb → transport.rb} +38 -0
  13. data/lib/rubydns/version.rb +1 -1
  14. data/lib/rubydns.rb +15 -8
  15. data/rubydns.gemspec +5 -2
  16. data/{test/test_daemon.rb → spec/rubydns/daemon_spec.rb} +36 -48
  17. data/{test → spec/rubydns}/hosts.txt +0 -0
  18. data/{test/test_rules.rb → spec/rubydns/message_spec.rb} +26 -44
  19. data/spec/rubydns/passthrough_spec.rb +78 -0
  20. data/spec/rubydns/resolver_performance_spec.rb +110 -0
  21. data/spec/rubydns/resolver_spec.rb +144 -0
  22. data/spec/rubydns/rules_spec.rb +74 -0
  23. data/{test/performance → spec/rubydns/server}/benchmark.rb +0 -0
  24. data/{test/performance → spec/rubydns/server}/bind9/generate-local.rb +0 -0
  25. data/{test/performance → spec/rubydns/server}/bind9/local.zone +0 -0
  26. data/{test/performance → spec/rubydns/server}/bind9/named.conf +0 -0
  27. data/spec/rubydns/server/bind9/named.run +0 -0
  28. data/{test/performance → spec/rubydns/server}/million.rb +1 -3
  29. data/spec/rubydns/server/rubydns.stackprof +0 -0
  30. data/spec/rubydns/server_performance_spec.rb +136 -0
  31. data/spec/rubydns/slow_server_spec.rb +89 -0
  32. data/spec/rubydns/socket_spec.rb +77 -0
  33. data/{test/test_system.rb → spec/rubydns/system_spec.rb} +28 -22
  34. data/spec/rubydns/transaction_spec.rb +64 -0
  35. data/{test/test_truncation.rb → spec/rubydns/truncation_spec.rb} +22 -48
  36. metadata +91 -54
  37. data/test/examples/fortune-dns.rb +0 -107
  38. data/test/examples/geoip-dns.rb +0 -76
  39. data/test/examples/soa-dns.rb +0 -82
  40. data/test/examples/test-dns-1.rb +0 -77
  41. data/test/examples/test-dns-2.rb +0 -83
  42. data/test/examples/wikipedia-dns.rb +0 -112
  43. data/test/test_message.rb +0 -65
  44. data/test/test_passthrough.rb +0 -120
  45. data/test/test_resolver.rb +0 -106
  46. data/test/test_resolver_performance.rb +0 -123
  47. data/test/test_server_performance.rb +0 -134
  48. data/test/test_slow_server.rb +0 -125
@@ -18,32 +18,23 @@
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 'eventmachine'
22
-
23
21
  module RubyDNS
24
22
 
25
- class PassthroughError < StandardError
26
- end
27
-
28
- # This class provides all details of a single DNS question and answer. This is used by the DSL to provide DNS related functionality.
23
+ # This class provides all details of a single DNS question and response. This is used by the DSL to provide DNS related functionality.
29
24
  #
30
25
  # The main functions to complete the trasaction 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).
31
26
  class Transaction
32
27
  # The default time used for responses (24 hours).
33
28
  DEFAULT_TTL = 86400
34
29
 
35
- def initialize(server, query, question, resource_class, answer, options = {})
30
+ def initialize(server, query, question, resource_class, response, options = {})
36
31
  @server = server
37
32
  @query = query
38
33
  @question = question
39
34
  @resource_class = resource_class
40
- @answer = answer
35
+ @response = response
41
36
 
42
37
  @options = options
43
-
44
- @question_appended = false
45
-
46
- @fiber = nil
47
38
  end
48
39
 
49
40
  # The resource_class that was requested. This is typically used to generate a response.
@@ -55,8 +46,8 @@ module RubyDNS
55
46
  # The question that this transaction represents.
56
47
  attr :question
57
48
 
58
- # The current full answer to the incoming query.
59
- attr :answer
49
+ # The current full response to the incoming query.
50
+ attr :response
60
51
 
61
52
  # Any options or configuration associated with the given transaction.
62
53
  attr :options
@@ -75,9 +66,9 @@ module RubyDNS
75
66
  "#{name} #{@resource_class.name}"
76
67
  end
77
68
 
78
- # 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 `answer`.
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`.
79
70
  def append!(name, resource_class = nil, options = {})
80
- Transaction.new(@server, @query, name, resource_class || @resource_class, @answer, options).process
71
+ Transaction.new(@server, @query, name, resource_class || @resource_class, @response, options).process
81
72
  end
82
73
 
83
74
  # Use the given resolver to respond to the question. Uses `passthrough` to do the lookup and merges the result.
@@ -86,22 +77,21 @@ module RubyDNS
86
77
  #
87
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.
88
79
  #
89
- # If the resolver does not respond, the result is `fail!(:NXDomain)`.
80
+ # If the resolver can't reach upstream servers, `fail!(:ServFail)` is invoked.
90
81
  def passthrough!(resolver, options = {}, &block)
91
82
  if @query.rd || options[:force] || options[:name]
92
- passthrough(resolver, options) do |response|
93
- if block_given?
94
- yield response
95
- end
96
-
83
+ response = passthrough(resolver, options)
84
+
85
+ if response
97
86
  # Recursion is available and is being used:
98
87
  # See issue #26 for more details.
99
- @answer.ra = 1
100
-
101
- @answer.merge!(response)
88
+ @response.ra = 1
89
+ @response.merge!(response)
90
+ else
91
+ fail!(:ServFail)
102
92
  end
103
93
  else
104
- raise PassthroughError.new("Request is not recursive!")
94
+ fail!(:Refused)
105
95
  end
106
96
  end
107
97
 
@@ -110,25 +100,11 @@ module RubyDNS
110
100
  # 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.
111
101
  #
112
102
  # If `options[:name]` is provided, this overrides the default query name sent to the upstream server. The same logic applies to `options[:resource_class]`.
113
- def passthrough(resolver, options = {}, &block)
103
+ def passthrough(resolver, options = {})
114
104
  query_name = options[:name] || name
115
105
  query_resource_class = options[:resource_class] || resource_class
116
106
 
117
- response = @server.defer do |handle|
118
- resolver.query(query_name, query_resource_class) do |response|
119
- # Not sure if this is potentially a race condition? Could fiber.resume occur before Fiber.yield?
120
- handle.resume(response)
121
- end
122
- end
123
-
124
- case response
125
- when RubyDNS::Message
126
- yield response
127
- when RubyDNS::ResolutionFailure
128
- fail!(:ServFail)
129
- else
130
- throw PassthroughError.new("Bad response from query: #{response.inspect}")
131
- end
107
+ resolver.query(query_name, query_resource_class)
132
108
  end
133
109
 
134
110
  # 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!}.
@@ -173,7 +149,7 @@ module RubyDNS
173
149
  resources.each do |resource|
174
150
  @server.logger.debug "#{method}: #{resource.inspect} #{resource.class::TypeValue} #{resource.class::ClassValue}"
175
151
 
176
- @answer.send(method, name, ttl, resource)
152
+ @response.send(method, name, ttl, resource)
177
153
  end
178
154
  end
179
155
 
@@ -196,9 +172,9 @@ module RubyDNS
196
172
  append_question!
197
173
 
198
174
  if rcode.kind_of? Symbol
199
- @answer.rcode = Resolv::DNS::RCode.const_get(rcode)
175
+ @response.rcode = Resolv::DNS::RCode.const_get(rcode)
200
176
  else
201
- @answer.rcode = rcode.to_i
177
+ @response.rcode = rcode.to_i
202
178
  end
203
179
  end
204
180
 
@@ -216,10 +192,10 @@ module RubyDNS
216
192
 
217
193
  protected
218
194
 
219
- # A typical response to a DNS request includes both the question and answer. This helper appends the question unless it looks like the user is already managing that aspect of the response.
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.
220
196
  def append_question!
221
- if @answer.question.size == 0
222
- @answer.add_question(@question, @resource_class) unless @question_appended
197
+ if @response.question.size == 0
198
+ @response.add_question(@question, @resource_class)
223
199
  end
224
200
  end
225
201
  end
@@ -20,6 +20,8 @@
20
20
 
21
21
  require 'stringio'
22
22
 
23
+ require_relative 'message'
24
+
23
25
  module RubyDNS
24
26
  # A helper class for processing incoming network data.
25
27
  class BinaryStringIO < StringIO
@@ -29,4 +31,40 @@ module RubyDNS
29
31
  set_encoding("BINARY")
30
32
  end
31
33
  end
34
+
35
+ module StreamTransport
36
+ def self.read_chunk(socket)
37
+ # The data buffer:
38
+ buffer = BinaryStringIO.new
39
+
40
+ # First we need to read in the length of the packet
41
+ while buffer.size < 2
42
+ buffer.write socket.readpartial(1)
43
+ end
44
+
45
+ # Read in the length, the first two bytes:
46
+ length = buffer.string.byteslice(0, 2).unpack('n')[0]
47
+
48
+ # Read data until we have the amount specified:
49
+ while (buffer.size - 2) < length
50
+ required = (2 + length) - buffer.size
51
+
52
+ # Read precisely the required amount:
53
+ buffer.write socket.readpartial(required)
54
+ end
55
+
56
+ return buffer.string.byteslice(2, length)
57
+ end
58
+
59
+ def self.write_message(socket, message)
60
+ write_chunk(socket, message.encode)
61
+ end
62
+
63
+ def self.write_chunk(socket, output_data)
64
+ socket.write([output_data.bytesize].pack('n'))
65
+ socket.write(output_data)
66
+
67
+ return output_data.bytesize
68
+ end
69
+ end
32
70
  end
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module RubyDNS
22
- VERSION = "0.8.5"
22
+ VERSION = "0.9.0"
23
23
  end
data/lib/rubydns.rb CHANGED
@@ -24,20 +24,27 @@ require_relative 'rubydns/message'
24
24
  require_relative 'rubydns/server'
25
25
  require_relative 'rubydns/resolver'
26
26
  require_relative 'rubydns/handler'
27
+ require_relative 'rubydns/logger'
27
28
 
28
29
  module RubyDNS
29
30
  # Run a server with the given rules.
30
31
  def self.run_server (options = {}, &block)
31
- server = RubyDNS::RuleBasedServer.new(&block)
32
+ supervisor = RubyDNS::RuleBasedServer.supervise(options, &block)
32
33
 
33
- EventMachine.run do
34
- trap("INT") do
35
- EventMachine::stop
36
- end
34
+ supervisor.actors.first.run
35
+
36
+ if options[:asynchronous]
37
+ return supervisor
38
+ else
39
+ read, write = IO.pipe
40
+
41
+ trap(:INT) {
42
+ write.puts
43
+ }
44
+
45
+ IO.select([read])
37
46
 
38
- server.run(options)
47
+ supervisor.terminate
39
48
  end
40
-
41
- server.fire(:stop)
42
49
  end
43
50
  end
data/rubydns.gemspec CHANGED
@@ -30,9 +30,12 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  spec.required_ruby_version = '>= 1.9.3'
32
32
 
33
- spec.add_dependency("eventmachine", "~> 1.0.0")
33
+ spec.add_dependency("celluloid", "~> 0.16.0")
34
+ spec.add_dependency("celluloid-io", "~> 0.16.1")
35
+ spec.add_dependency("timers", "~> 4.0.1")
34
36
 
35
37
  spec.add_development_dependency "bundler", "~> 1.3"
36
- spec.add_development_dependency "process-daemon", "~> 0.5.3"
38
+ spec.add_development_dependency "process-daemon", "~> 0.5.5"
39
+ spec.add_development_dependency "rspec", "~> 3.0.0"
37
40
  spec.add_development_dependency "rake"
38
41
  end
@@ -20,10 +20,7 @@
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 'minitest/autorun'
24
-
25
23
  require 'rubydns'
26
-
27
24
  require 'process/daemon'
28
25
 
29
26
  class BasicTestServer < Process::Daemon
@@ -36,6 +33,8 @@ class BasicTestServer < Process::Daemon
36
33
  end
37
34
 
38
35
  def startup
36
+ Celluloid.boot
37
+
39
38
  # Start the RubyDNS server
40
39
  RubyDNS::run_server(:listen => SERVER_PORTS) do
41
40
  match("test.local", IN::A) do |transaction|
@@ -47,7 +46,7 @@ class BasicTestServer < Process::Daemon
47
46
  end
48
47
 
49
48
  match(/peername/, IN::A) do |transaction|
50
- transaction.respond!(transaction[:connection].peername[1])
49
+ transaction.respond!(transaction[:peer])
51
50
  end
52
51
 
53
52
  # Default DNS handler
@@ -58,64 +57,53 @@ class BasicTestServer < Process::Daemon
58
57
  end
59
58
  end
60
59
 
61
- class DaemonTest < MiniTest::Test
62
- def setup
63
- BasicTestServer.controller output: File.open("/dev/null", "w")
60
+ describe "RubyDNS Daemonized Server" do
61
+ before(:all) do
62
+ Celluloid.shutdown
64
63
 
64
+ # Trying to fork with Celluloid running is a recipe for disaster.
65
+ # BasicTestServer.controller output: $stderr
65
66
  BasicTestServer.start
67
+
68
+ Celluloid.boot
66
69
  end
67
70
 
68
- def teardown
71
+ after(:all) do
69
72
  BasicTestServer.stop
70
73
  end
71
74
 
72
- def test_basic_dns
73
- assert_equal :running, BasicTestServer.status
75
+ it "should resolve local domain correctly" do
76
+ expect(BasicTestServer.status).to be == :running
74
77
 
75
- EventMachine.run do
76
- resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
78
+ resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
79
+
80
+ response = resolver.query("test.local")
81
+ answer = response.answer.first
77
82
 
78
- resolver.query("test.local") do |response|
79
- answer = response.answer.first
80
-
81
- assert_equal "test.local", answer[0].to_s
82
- assert_equal "192.168.1.1", answer[2].address.to_s
83
-
84
- EventMachine.stop
85
- end
86
- end
83
+ expect(answer[0].to_s).to be == "test.local"
84
+ expect(answer[2].address.to_s).to be == "192.168.1.1"
87
85
  end
88
86
 
89
- def test_pattern_matching
90
- assert_equal :running, BasicTestServer.status
91
-
92
- EventMachine.run do
93
- resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
87
+ it "should pattern match correctly" do
88
+ expect(BasicTestServer.status).to be == :running
89
+
90
+ resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
94
91
 
95
- resolver.query("foobar") do |response|
96
- answer = response.answer.first
97
-
98
- assert_equal "foobar", answer[0].to_s
99
- assert_equal "192.168.1.2", answer[2].address.to_s
100
-
101
- EventMachine.stop
102
- end
103
- end
92
+ response = resolver.query("foobar")
93
+ answer = response.answer.first
94
+
95
+ expect(answer[0].to_s).to be == "foobar"
96
+ expect(answer[2].address.to_s).to be == "192.168.1.2"
104
97
  end
105
98
 
106
- def test_peername
107
- assert_equal :running, BasicTestServer.status
108
-
109
- EventMachine.run do
110
- resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
99
+ it "should give peer ip address" do
100
+ expect(BasicTestServer.status).to be == :running
101
+
102
+ resolver = RubyDNS::Resolver.new(BasicTestServer::SERVER_PORTS)
111
103
 
112
- resolver.query("peername") do |response|
113
- answer = response.answer.first
114
-
115
- assert_equal "127.0.0.1", answer[2].address.to_s
116
-
117
- EventMachine.stop
118
- end
119
- end
104
+ response = resolver.query("peername")
105
+ answer = response.answer.first
106
+
107
+ expect(answer[2].address.to_s).to be == "127.0.0.1"
120
108
  end
121
109
  end
File without changes
@@ -20,55 +20,37 @@
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 'minitest/autorun'
24
-
25
23
  require 'rubydns'
24
+ require 'base64'
26
25
 
27
- class RulesTest < MiniTest::Test
28
- IN = Resolv::DNS::Resource::IN
26
+ module RubyDNS::MessageSpec
27
+ describe RubyDNS::Message do
28
+ it "should be decoded correctly" do
29
+ data = Base64.decode64(<<-EOF)
30
+ HQCBgAABAAgAAAABA3d3dwV5YWhvbwNjb20AAAEAAcAMAAUAAQAAASwADwZm
31
+ ZC1mcDMDd2cxAWLAEMArAAUAAQAAASwACQZkcy1mcDPAMsBGAAUAAQAAADwA
32
+ FQ5kcy1hbnktZnAzLWxmYgN3YTHANsBbAAUAAQAAASwAEg9kcy1hbnktZnAz
33
+ LXJlYWzAasB8AAEAAQAAADwABGKK/B7AfAABAAEAAAA8AARii7SVwHwAAQAB
34
+ AAAAPAAEYou3GMB8AAEAAQAAADwABGKK/W0AACkQAAAAAAAAAA==
35
+ EOF
29
36
 
30
- def setup
31
- @logger = Logger.new($stderr)
32
- @logger.level = Logger::INFO
33
-
34
- @server = RubyDNS::Server.new(logger: @logger)
35
- @true_callback = Proc.new { true }
36
- end
37
+ message = RubyDNS::decode_message(data)
38
+ expect(message.class).to be == RubyDNS::Message
39
+ expect(message.id).to be == 0x1d00
37
40
 
38
- def teardown
39
- end
40
-
41
- def test_string_pattern
42
- rule = RubyDNS::RuleBasedServer::Rule.new(["foobar", IN::A], @true_callback)
43
-
44
- assert rule.call(@server, "foobar", IN::A)
45
- assert !rule.call(@server, "barfoo", IN::A)
46
- assert !rule.call(@server, "foobar", IN::TXT)
47
- end
48
-
49
- def test_regexp_pattern
50
- rule = RubyDNS::RuleBasedServer::Rule.new([/foo/, IN::A], @true_callback)
51
-
52
- assert rule.call(@server, "foobar", IN::A)
53
- assert !rule.call(@server, "barbaz", IN::A)
54
- assert !rule.call(@server, "foobar", IN::TXT)
55
- end
41
+ expect(message.question.count).to be == 1
42
+ expect(message.answer.count).to be == 8
43
+ expect(message.authority.count).to be == 0
44
+ expect(message.additional.count).to be == 1
45
+ end
46
+
47
+ it "should fail to decode due to bad AAAA length" do
48
+ data = Base64.decode64(<<-EOF)
49
+ 6p6BgAABAAEAAAABCGJhaWNhaWNuA2NvbQAAHAABwAwAHAABAAABHgAEMhd7
50
+ dwAAKRAAAAAAAAAA
51
+ EOF
56
52
 
57
- def test_callback_pattern
58
- calls = 0
59
-
60
- callback = Proc.new do |name, resource_class|
61
- # A counter used to check the number of times this block was invoked.
62
- calls += 1
63
-
64
- name.size == 6
53
+ expect{RubyDNS::decode_message(data)}.to raise_error(RubyDNS::DecodeError)
65
54
  end
66
-
67
- rule = RubyDNS::RuleBasedServer::Rule.new([callback], @true_callback)
68
-
69
- assert rule.call(@server, "foobar", IN::A)
70
- assert !rule.call(@server, "foobarbaz", IN::A)
71
-
72
- assert_equal 2, calls
73
55
  end
74
56
  end
@@ -0,0 +1,78 @@
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
+ before(:all) do
32
+ Celluloid.shutdown
33
+ Celluloid.boot
34
+
35
+ # Start the RubyDNS server
36
+ @server = RubyDNS::run_server(:listen => SERVER_PORTS, asynchronous: true) do
37
+ resolver = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
38
+
39
+ match(/.*\.com/, IN::A) do |transaction|
40
+ transaction.passthrough!(resolver)
41
+ end
42
+
43
+ match(/a-(.*\.org)/) do |transaction, match_data|
44
+ transaction.passthrough!(resolver, :name => match_data[1])
45
+ end
46
+
47
+ # Default DNS handler
48
+ otherwise do |transaction|
49
+ transaction.fail!(:NXDomain)
50
+ end
51
+ end
52
+ end
53
+
54
+ it "should resolve domain correctly" do
55
+ resolver = RubyDNS::Resolver.new(SERVER_PORTS)
56
+
57
+ response = resolver.query("google.com")
58
+ expect(response.ra).to be == 1
59
+
60
+ answer = response.answer.first
61
+ expect(answer).not_to be == nil
62
+ expect(answer.count).to be > 0
63
+
64
+ addresses = answer.select {|record| record.kind_of? Resolv::DNS::Resource::IN::A}
65
+ expect(addresses.size).to be > 0
66
+ end
67
+
68
+ it "should resolve prefixed domain correctly" do
69
+ resolver = RubyDNS::Resolver.new(SERVER_PORTS)
70
+
71
+ response = resolver.query("a-slashdot.org")
72
+ answer = response.answer.first
73
+
74
+ expect(answer).not_to be == nil
75
+ expect(answer.count).to be > 0
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,110 @@
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::ResolverPerformanceSpec
26
+ describe RubyDNS::Resolver do
27
+ context 'benchmark' do
28
+ domains = %W{
29
+ Facebook.com
30
+ Twitter.com
31
+ Google.com
32
+ Youtube.com
33
+ Wordpress.org
34
+ Adobe.com
35
+ Blogspot.com
36
+ Wikipedia.org
37
+ Linkedin.com
38
+ Wordpress.com
39
+ Yahoo.com
40
+ Amazon.com
41
+ Flickr.com
42
+ Pinterest.com
43
+ Tumblr.com
44
+ W3.org
45
+ Apple.com
46
+ Myspace.com
47
+ Vimeo.com
48
+ Microsoft.com
49
+ Youtu.be
50
+ Qq.com
51
+ Digg.com
52
+ Baidu.com
53
+ Stumbleupon.com
54
+ Addthis.com
55
+ Statcounter.com
56
+ Feedburner.com
57
+ TradeMe.co.nz
58
+ Delicious.com
59
+ Nytimes.com
60
+ Reddit.com
61
+ Weebly.com
62
+ Bbc.co.uk
63
+ Blogger.com
64
+ Msn.com
65
+ Macromedia.com
66
+ Goo.gl
67
+ Instagram.com
68
+ Gov.uk
69
+ Icio.us
70
+ Yandex.ru
71
+ Cnn.com
72
+ Webs.com
73
+ Google.de
74
+ T.co
75
+ Livejournal.com
76
+ Imdb.com
77
+ Mail.ru
78
+ Jimdo.com
79
+ }
80
+
81
+ before do
82
+ require 'benchmark'
83
+ end
84
+
85
+ it 'should be faster than native resolver' do
86
+ Celluloid.logger.level = Logger::ERROR
87
+
88
+ Benchmark.bm(20) do |x|
89
+ a = x.report("RubyDNS::Resolver") do
90
+ resolver = RubyDNS::Resolver.new([[:udp, "8.8.8.8", 53], [:tcp, "8.8.8.8", 53]])
91
+
92
+ futures = domains.collect{|domain| resolver.future.addresses_for(domain)}
93
+
94
+ futures.collect{|future| future.value}
95
+ end
96
+
97
+ b = x.report("Resolv::DNS") do
98
+ resolver = Resolv::DNS.new(:nameserver => "8.8.8.8")
99
+
100
+ resolved = domains.collect do |domain|
101
+ [domain, resolver.getaddresses(domain)]
102
+ end
103
+ end
104
+
105
+ expect(a.real).to be < b.real
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end