dnsruby 1.57.0 → 1.58.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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -2
  3. data/.travis.yml +1 -1
  4. data/README.md +2 -2
  5. data/RELEASE_NOTES.md +16 -0
  6. data/Rakefile +2 -0
  7. data/dnsruby.gemspec +2 -0
  8. data/lib/dnsruby.rb +5 -0
  9. data/lib/dnsruby/bit_mapping.rb +138 -0
  10. data/lib/dnsruby/bitmap.rb +108 -0
  11. data/lib/dnsruby/message/decoder.rb +90 -80
  12. data/lib/dnsruby/message/message.rb +16 -3
  13. data/lib/dnsruby/message/section.rb +3 -3
  14. data/lib/dnsruby/name.rb +2 -2
  15. data/lib/dnsruby/packet_sender.rb +73 -1
  16. data/lib/dnsruby/resolv.rb +56 -42
  17. data/lib/dnsruby/resolver.rb +95 -73
  18. data/lib/dnsruby/resource/GPOS.rb +9 -3
  19. data/lib/dnsruby/resource/HIP.rb +1 -1
  20. data/lib/dnsruby/resource/IN.rb +3 -1
  21. data/lib/dnsruby/resource/NAPTR.rb +2 -2
  22. data/lib/dnsruby/resource/NSEC3.rb +2 -2
  23. data/lib/dnsruby/resource/NXT.rb +302 -0
  24. data/lib/dnsruby/resource/OPT.rb +2 -2
  25. data/lib/dnsruby/resource/TXT.rb +2 -2
  26. data/lib/dnsruby/resource/generic.rb +1 -0
  27. data/lib/dnsruby/resource/type_bitmap.rb +0 -0
  28. data/lib/dnsruby/select_thread.rb +174 -83
  29. data/lib/dnsruby/single_resolver.rb +2 -2
  30. data/lib/dnsruby/version.rb +1 -1
  31. data/lib/dnsruby/zone_reader.rb +19 -9
  32. data/lib/dnsruby/zone_transfer.rb +1 -1
  33. data/test/spec_helper.rb +9 -1
  34. data/test/tc_axfr.rb +17 -4
  35. data/test/tc_gpos.rb +3 -3
  36. data/test/tc_message.rb +7 -1
  37. data/test/tc_nxt.rb +192 -0
  38. data/test/tc_recur.rb +2 -1
  39. data/test/tc_resolv.rb +73 -0
  40. data/test/tc_resolver.rb +22 -4
  41. data/test/tc_rr-opt.rb +9 -8
  42. data/test/tc_rr.rb +22 -0
  43. data/test/tc_single_resolver.rb +15 -15
  44. data/test/tc_soak.rb +154 -65
  45. data/test/tc_soak_base.rb +15 -15
  46. data/test/tc_tcp.rb +1 -1
  47. data/test/tc_tcp_pipelining.rb +203 -0
  48. data/test/tc_zone_reader.rb +73 -0
  49. data/test/test_dnsserver.rb +208 -0
  50. data/test/test_utils.rb +49 -0
  51. data/test/ts_offline.rb +59 -41
  52. data/test/ts_online.rb +92 -63
  53. metadata +40 -3
  54. data/test/tc_dnsruby.rb +0 -51
@@ -113,14 +113,15 @@ class TestRrOpt < Minitest::Test
113
113
  assert(p.additional()[0].klass.code == 4096)
114
114
  end
115
115
 
116
- def test_large_packet
117
- # Query TXT for overflow.dnsruby.validation-test-servers.nominet.org.uk
118
- # with a large udp_size
119
- res = SingleResolver.new
120
- res.udp_size = 4096
121
- ret = res.query("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT)
122
- assert(ret.rcode == RCode.NoError)
123
- end
116
+ # Sadly Nominet no longer host these servers :-(
117
+ # def test_large_packet
118
+ # # Query TXT for overflow.dnsruby.validation-test-servers.nominet.org.uk
119
+ # # with a large udp_size
120
+ # res = SingleResolver.new
121
+ # res.udp_size = 4096
122
+ # ret = res.query("overflow.dnsruby.validation-test-servers.nominet.org.uk", Types.TXT)
123
+ # assert(ret.rcode == RCode.NoError)
124
+ # end
124
125
 
125
126
  def test_decode_opt
126
127
  # Create an OPT RR
@@ -328,4 +328,26 @@ class TestRR < Minitest::Test
328
328
  assert_equal a1.hash, a2.hash
329
329
  assert_equal a1.hash, a3.hash
330
330
  end
331
+
332
+ def _test_duplicate_answer(method_as_symbol)
333
+ expected_count = case method_as_symbol
334
+ when :add_answer
335
+ 1
336
+ when :add_answer!
337
+ 2
338
+ end
339
+
340
+ rr = RR.new_from_string 'techhumans.com. 1111 IN A 69.89.31.97'
341
+ message = Message.new
342
+ 2.times { message.send(method_as_symbol, rr) }
343
+ assert_equal(expected_count, message.header.ancount)
344
+ end
345
+
346
+ def test_add_dup_answer_no_force
347
+ _test_duplicate_answer(:add_answer)
348
+ end
349
+
350
+ def test_add_dup_answer_force
351
+ _test_duplicate_answer(:add_answer!)
352
+ end
331
353
  end
@@ -28,30 +28,30 @@ class TestSingleResolver < Minitest::Test
28
28
  Rrs = [
29
29
  {
30
30
  :type => Types.A,
31
- :name => 'a.t.dnsruby.validation-test-servers.nominet.org.uk',
31
+ :name => 'a.t.net-dns.org',
32
32
  :address => '10.0.1.128'
33
33
  },
34
34
  {
35
35
  :type => Types::MX,
36
- :name => 'mx.t.dnsruby.validation-test-servers.nominet.org.uk',
37
- :exchange => 'a.t.dnsruby.validation-test-servers.nominet.org.uk',
36
+ :name => 'mx.t.net-dns.org',
37
+ :exchange => 'a.t.net-dns.org',
38
38
  :preference => 10
39
39
  },
40
40
  {
41
41
  :type => 'CNAME',
42
- :name => 'cname.t.dnsruby.validation-test-servers.nominet.org.uk',
43
- :domainname => 'a.t.dnsruby.validation-test-servers.nominet.org.uk'
42
+ :name => 'cname.t.net-dns.org',
43
+ :domainname => 'a.t.net-dns.org'
44
44
  },
45
45
  {
46
46
  :type => Types.TXT,
47
- :name => 'txt.t.dnsruby.validation-test-servers.nominet.org.uk',
47
+ :name => 'txt.t.net-dns.org',
48
48
  :strings => ['Net-DNS']
49
49
  }
50
50
  ]
51
51
 
52
52
  def test_simple
53
53
  res = SingleResolver.new()
54
- m = res.query("a.t.dnsruby.validation-test-servers.nominet.org.uk")
54
+ m = res.query("ns1.google.com.")
55
55
  end
56
56
 
57
57
  def test_timeout
@@ -69,7 +69,7 @@ class TestSingleResolver < Minitest::Test
69
69
  res.port = port
70
70
  res.packet_timeout=1
71
71
  start_time = Time.now.to_i
72
- m = res.query("a.t.dnsruby.validation-test-servers.nominet.org.uk")
72
+ m = res.query("a.t.net-dns.org")
73
73
  fail "Got response when should have got none"
74
74
  rescue ResolvTimeout
75
75
  stop_time = Time.now.to_i
@@ -85,7 +85,7 @@ class TestSingleResolver < Minitest::Test
85
85
  res.packet_timeout=1
86
86
  start_time = Time.now.to_i
87
87
  # TheLog.level = Logger::DEBUG
88
- m = res.query("a.t.dnsruby.validation-test-servers.nominet.org.uk")
88
+ m = res.query("a.t.net-dns.org")
89
89
  fail "TCP timeouts"
90
90
  rescue ResolvTimeout
91
91
  # print "Got Timeout for TCP\n"
@@ -112,7 +112,7 @@ class TestSingleResolver < Minitest::Test
112
112
  res.port = port
113
113
  res.packet_timeout=1
114
114
  q = Queue.new
115
- msg = Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk")
115
+ msg = Message.new("a.t.net-dns.org")
116
116
  res.send_async(msg, q, msg)
117
117
  id,ret, error = q.pop
118
118
  assert(id==msg)
@@ -141,7 +141,7 @@ class TestSingleResolver < Minitest::Test
141
141
 
142
142
  assert(packet, "Got an answer for #{data[:name]} IN #{data[:type]}")
143
143
  assert_equal(1, packet.header.qdcount, 'Only one question')
144
- assert_equal(1, packet.header.ancount, 'Got single answer')
144
+ assert_equal(1, packet.header.ancount, "Got single answer (for question #{data[:name]}")
145
145
 
146
146
  question = (packet.question)[0]
147
147
  answer = (packet.answer)[0]
@@ -184,11 +184,11 @@ class TestSingleResolver < Minitest::Test
184
184
  def test_res_config
185
185
  res = Dnsruby::SingleResolver.new
186
186
 
187
- res.server=('a.t.dnsruby.validation-test-servers.nominet.org.uk')
187
+ res.server=('a.t.net-dns.org')
188
188
  ip = res.server
189
189
  assert_equal('10.0.1.128', ip.to_s, 'nameserver() looks up IP.')
190
190
 
191
- res.server=('cname.t.dnsruby.validation-test-servers.nominet.org.uk')
191
+ res.server=('cname.t.net-dns.org')
192
192
  ip = res.server
193
193
  assert_equal('10.0.1.128', ip.to_s, 'nameserver() looks up cname.')
194
194
  end
@@ -196,9 +196,9 @@ class TestSingleResolver < Minitest::Test
196
196
  def test_truncated_response
197
197
  res = SingleResolver.new
198
198
  # print "Dnssec = #{res.dnssec}\n"
199
- res.server=('ns0.validation-test-servers.nominet.org.uk')
199
+ # res.server=('ns0.validation-test-servers.nominet.org.uk')
200
200
  res.packet_timeout = 15
201
- m = res.query("overflow.dnsruby.validation-test-servers.nominet.org.uk", 'txt')
201
+ m = res.query("overflow.net-dns.org", 'txt')
202
202
  assert(m.header.ancount == 61, "61 answer records expected, got #{m.header.ancount}")
203
203
  assert(!m.header.tc, "Message was truncated!")
204
204
  end
@@ -16,24 +16,76 @@
16
16
 
17
17
  require_relative 'spec_helper'
18
18
 
19
- begin
20
- require 'test/tc_single_resolver'
21
- rescue LoadError
22
- require 'tc_single_resolver'
23
- end
24
- begin
25
- require 'test/tc_soak_base'
26
- rescue LoadError
27
- require 'tc_soak_base'
28
- end
19
+ # require_relative 'tc_single_resolver'
20
+ require_relative 'tc_soak_base'
21
+ require_relative 'test_dnsserver'
22
+
29
23
  include Dnsruby
24
+
30
25
  # This class tries to soak test the Dnsruby library.
31
26
  # It can't do this very well, owing to the small number of sockets allowed to be open simultaneously.
32
27
  # @TODO@ Future versions of dnsruby will allow random streaming over a fixed number of (cycling) random sockets,
33
28
  # so this test can be beefed up considerably at that point.
34
29
  # @todo@ A test DNS server running on localhost is really needed here
30
+
31
+ class MyServer < RubyDNS::Server
32
+
33
+ IP = "127.0.0.1"
34
+ PORT = 53927
35
+
36
+ @@stats = Stats.new
37
+
38
+ def self.stats
39
+ @@stats
40
+ end
41
+
42
+ def process(name, resource_class, transaction)
43
+ transaction.respond!("93.184.216.34", { resource_class: Resolv::DNS::Resource::IN::A })
44
+ Celluloid.logger.debug "got message"
45
+ end
46
+ end
47
+
48
+ class PipeliningServer < MyServer
49
+ def run
50
+ fire(:setup)
51
+
52
+ link NioTcpPipeliningHandler.new(self, IP, PORT, 5) #5 max request
53
+ link RubyDNS::UDPHandler.new(self, IP, PORT)
54
+
55
+ fire(:start)
56
+ end
57
+ end
58
+
35
59
  class TestSingleResolverSoak < Minitest::Test
36
60
 
61
+ IP = MyServer::IP
62
+ PORT = MyServer::PORT
63
+
64
+ def initialize(arg)
65
+ super(arg)
66
+ self.class.init
67
+ end
68
+
69
+ def self.init
70
+ unless @initialized
71
+ Celluloid.boot
72
+ # By default, Celluloid logs output to console. Use Dnsruby.log instead.
73
+ Celluloid.logger = Dnsruby.log
74
+ @initialized = true
75
+ end
76
+ end
77
+
78
+ SINGLE_RESOLVER_QUERY_TIMES = 63
79
+
80
+ def setup
81
+ # Instantiate a new server
82
+ # For each query respond with 93.184.216.34
83
+
84
+ @@supervisor ||= RubyDNS::run_server(asynchronous: true,
85
+ server_class: PipeliningServer)
86
+
87
+ end
88
+
37
89
  def test_many_asynchronous_queries_one_single_resolver
38
90
  run_many_asynch_queries_test_single_res(1)
39
91
  end
@@ -42,58 +94,87 @@ class TestSingleResolverSoak < Minitest::Test
42
94
  run_many_asynch_queries_test_single_res(50)
43
95
  end
44
96
 
45
- def run_many_asynch_queries_test_single_res(num_resolvers)
97
+ def test_many_asynchronous_queries_one_single_resolver_tcp
98
+ run_many_asynch_queries_test_single_res(1, true)
99
+ end
100
+
101
+ def test_many_asynchronous_queries_many_single_resolvers_tcp
102
+ run_many_asynch_queries_test_single_res(50, true)
103
+ end
104
+
105
+ def test_many_asynchronous_queries_one_single_resolver_tcp_pipelining
106
+ run_many_asynch_queries_test_single_res(1, true, true)
107
+ end
108
+
109
+ def test_many_asynchronous_queries_many_single_resolvers_tcp_pipelining
110
+ run_many_asynch_queries_test_single_res(50, true, true)
111
+ end
112
+
113
+ def run_many_asynch_queries_test_single_res(num_resolvers, tcp = false, pipelining = false)
46
114
  q = Queue.new
47
- resolvers = []
48
- timed_out = 0
49
- query_count = 0
50
- num_resolvers.times do |n|
51
- resolvers.push(SingleResolver.new)
52
- resolvers[n].packet_timeout=4
115
+ timeout_count = 0
116
+ resolvers = Array.new(num_resolvers) do
117
+ SingleResolver.new(server: IP,
118
+ port: PORT,
119
+ do_caching: false,
120
+ do_validation: false,
121
+ tcp_pipelining: pipelining,
122
+ packet_timeout: 10,
123
+ tcp_pipelining_max_queries: 5,
124
+ use_tcp: tcp)
53
125
  end
54
- res_pos = 0
55
126
  start = Time.now
127
+
56
128
  # @todo@ On windows, MAX_FILES is 256. This means that we have to limit
57
129
  # this test while we're not using single sockets.
58
130
  # We run four queries per iteration, so we're limited to 64 runs.
59
- 63.times do |i|
60
- rr_count = 0
61
- TestSoakBase::Rrs.each do |data|
62
- rr_count+=1
63
- res = resolvers[res_pos]
64
- res_pos=+1
65
- if (res_pos >= num_resolvers)
66
- res_pos = 0
131
+ messages = TestSoakBase::Rrs.map do |data|
132
+ message = Message.new(data[:name], data[:type])
133
+ message.do_validation = false
134
+ message.do_caching = false
135
+ message
136
+ end
137
+
138
+ query_count = SINGLE_RESOLVER_QUERY_TIMES * messages.count
139
+
140
+ receive_thread = Thread.new do
141
+ query_count.times do
142
+ _id, ret, error = q.pop
143
+ if error.is_a?(ResolvTimeout)
144
+ timeout_count+=1
145
+ elsif ret.class != Message
146
+ p "ERROR RETURNED : #{error}"
67
147
  end
68
- res.send_async(Message.new(data[:name], data[:type]), q, [i,rr_count])
69
- # p "Sent #{i}, #{rr_count}, Queue #{q}"
70
- query_count+=1
71
148
  end
72
149
  end
73
- query_count.times do |i|
74
- id,ret, error = q.pop
75
- if (error.class == ResolvTimeout)
76
- timed_out+=1
77
- elsif (ret.class != Message)
78
- p "ERROR RETURNED : #{error}"
79
- end
80
150
 
151
+ resolver_cycler = resolvers.cycle
152
+
153
+ SINGLE_RESOLVER_QUERY_TIMES.times do |i|
154
+ rr_count = 0
155
+ messages.each do | message |
156
+ rr_count += 1
157
+ resolver_cycler.next.send_async(message, q, rr_count + i * messages.count)
158
+ # p "Sent #{i}, #{rr_count}, Queue #{q}"
159
+ end
81
160
  end
82
- stop=Time.now
83
- time_taken=stop-start
84
- p "Query count : #{query_count}, #{timed_out} timed out. #{time_taken} time taken"
85
- assert(timed_out < query_count * 0.1, "#{timed_out} of #{query_count} timed out!")
86
- end
87
161
 
162
+ receive_thread.join
163
+
164
+ time_taken = Time.now - start
165
+ puts "Query count : #{query_count}, #{timeout_count} timed out. #{time_taken} time taken"
166
+ assert(timeout_count < query_count * 0.1, "#{timeout_count} of #{query_count} timed out!")
167
+ end
88
168
 
89
169
  def test_many_threads_on_one_single_resolver_synchronous
90
170
  # Test multi-threaded behaviour
91
171
  # Check the header IDs to make sure they're all different
92
172
  threads = Array.new
93
- res = SingleResolver.new
173
+
174
+ res = create_default_single_resolver
94
175
  ids = []
95
176
  mutex = Mutex.new
96
- timed_out = 0
177
+ timeout_count = 0
97
178
  query_count = 0
98
179
  res.packet_timeout=4
99
180
  start=Time.now
@@ -107,16 +188,12 @@ class TestSingleResolverSoak < Minitest::Test
107
188
  threads[i] = Thread.new{
108
189
  40.times do |j|
109
190
  TestSoakBase::Rrs.each do |data|
110
- mutex.synchronize do
111
- query_count+=1
112
- end
191
+ mutex.synchronize { query_count += 1 }
113
192
  packet=nil
114
193
  begin
115
194
  packet = res.query(data[:name], data[:type])
116
195
  rescue ResolvTimeout
117
- mutex.synchronize {
118
- timed_out+=1
119
- }
196
+ mutex.synchronize { timeout_count += 1 }
120
197
  next
121
198
  end
122
199
  assert(packet)
@@ -131,9 +208,9 @@ class TestSingleResolverSoak < Minitest::Test
131
208
  end
132
209
  stop=Time.now
133
210
  time_taken=stop-start
134
- p "Query count : #{query_count}, #{timed_out} timed out. #{time_taken} time taken"
211
+ puts "Query count : #{query_count}, #{timeout_count} timed out. #{time_taken} time taken"
135
212
  # check_ids(ids) # only do this if we expect all different IDs - e.g. if we stream over a single socket
136
- assert(timed_out < query_count * 0.1, "#{timed_out} of #{query_count} timed out!")
213
+ assert(timeout_count < query_count * 0.1, "#{timeout_count} of #{query_count} timed out!")
137
214
  end
138
215
 
139
216
  def check_ids(ids)
@@ -152,7 +229,7 @@ class TestSingleResolverSoak < Minitest::Test
152
229
  # @todo@ Check the header IDs to make sure they're all different
153
230
  threads = Array.new
154
231
  mutex = Mutex.new
155
- timed_out = 0
232
+ timeout_count = 0
156
233
  query_count = 0
157
234
  start=Time.now
158
235
  num_times=250
@@ -162,23 +239,28 @@ class TestSingleResolverSoak < Minitest::Test
162
239
  end
163
240
  num_times.times do |i|
164
241
  threads[i] = Thread.new{
165
- res = SingleResolver.new
166
- res.packet_timeout=4
242
+ res = create_default_single_resolver
167
243
  40.times do |j|
168
244
  TestSoakBase::Rrs.each do |data|
169
245
  mutex.synchronize do
170
246
  query_count+=1
171
247
  end
172
248
  q = Queue.new
173
- res.send_async(Message.new(data[:name], data[:type]), q, [i,j])
249
+
250
+ message = Message.new(data[:name], data[:type])
251
+ message.do_validation = false
252
+ message.do_caching = false
253
+
254
+ res.send_async(message, q, [i,j])
255
+
174
256
  id, packet, error = q.pop
175
257
  if (error.class == ResolvTimeout)
176
258
  mutex.synchronize {
177
- timed_out+=1
259
+ timeout_count+=1
178
260
  }
179
261
  next
180
262
  elsif (packet.class!=Message)
181
- p "ERROR! #{error}"
263
+ puts "ERROR! #{error}"
182
264
  end
183
265
 
184
266
  assert(packet)
@@ -187,14 +269,21 @@ class TestSingleResolverSoak < Minitest::Test
187
269
  end
188
270
  }
189
271
  end
190
- threads.each do |thread|
191
- thread.join
192
- end
193
- stop=Time.now
194
- time_taken=stop-start
195
- p "Query count : #{query_count}, #{timed_out} timed out. #{time_taken} time taken"
196
- assert(timed_out < query_count * 0.1, "#{timed_out} of #{query_count} timed out!")
272
+ # NOTE: For methods on the objects taking no params, we can use this shorthand.
273
+ threads.each(&:join)
274
+
275
+ time_taken = Time.now - start
276
+ puts "Query count : #{query_count}, #{timeout_count} timed out. #{time_taken} time taken"
277
+ assert(timeout_count < query_count * 0.1, "#{timeout_count} of #{query_count} timed out!")
197
278
  end
198
279
 
199
280
 
200
- end
281
+ def create_default_single_resolver
282
+ SingleResolver.new(server: IP,
283
+ port: PORT,
284
+ do_caching: false,
285
+ do_validation: false,
286
+ packet_timeout: 10)
287
+
288
+ end
289
+ end