1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
|
# --
# Copyright 2007 Nominet UK
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either tmexpress or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ++
require_relative 'spec_helper'
require 'socket'
# @TODO@ We also need a test server so we can control behaviour of server to test
# different aspects of retry strategy.
# Of course, with Ruby's limit of 256 open sockets per process, we'd need to run
# the server in a different Ruby process.
class TestResolver < Minitest::Test
include Dnsruby
Thread::abort_on_exception = true
GOOD_DOMAIN_NAME = 'example.com'
BAD_DOMAIN_NAME = 'dnsruby-test-of-bad-domain-name.blah'
PORT = 42138
@@port = PORT
def setup
Dnsruby::Config.reset
end
def assert_valid_response(response)
assert(response.kind_of?(Message), "Expected response to be a message but was a #{response.class}")
end
def assert_nil_response(response)
assert(response.nil?, "Expected no response but got a #{response.class}:\n#{response}")
end
def assert_error_is_exception(error, error_class = Exception)
assert(error.is_a?(error_class), "Expected error to be an #{error_class}, but was a #{error.class}:\n#{error}")
end
def assert_nil_error(error)
assert(error.nil?, "Expected no error but got a #{error.class}:\n#{error}")
end
def test_send_message
response = Resolver.new.send_message(Message.new("example.com", Types.A))
assert_valid_response(response)
end
def test_send_message_bang_noerror
response, error = Resolver.new.send_message!(Message.new(GOOD_DOMAIN_NAME, Types.A))
assert_nil_error(error)
assert_valid_response(response)
end
def test_send_message_bang_error
message = Message.new(BAD_DOMAIN_NAME, Types.A)
response, error = Resolver.new.send_message!(message)
assert_nil_response(response)
assert_error_is_exception(error)
end
def test_send_plain_message
resolver = Resolver.new
response, error = resolver.send_plain_message(Message.new("cnn.com"))
assert_nil_error(error)
assert_valid_response(response)
m = Message.new(BAD_DOMAIN_NAME)
m.header.rd = true
response, error = resolver.send_plain_message(m)
assert_valid_response(response)
assert_error_is_exception(error, NXDomain)
end
def test_query
response = Resolver.new.query("example.com")
assert_valid_response(response)
end
def test_query_bang_noerror
response, error = Resolver.new.query!(GOOD_DOMAIN_NAME)
assert_nil_error(error)
assert_valid_response(response)
end
def test_query_bang_error
response, error = Resolver.new.query!(BAD_DOMAIN_NAME)
assert_nil_response(response)
assert_error_is_exception(error)
end
def test_query_async
q = Queue.new
Resolver.new.send_async(Message.new("example.com", Types.A),q,q)
id, response, error = q.pop
assert_equal(id, q, "Id wrong!")
assert_valid_response(response)
assert_nil_error(error)
end
def test_query_one_duff_server_one_good
res = Resolver.new({:nameserver => ["8.8.8.8", "8.8.8.7"]})
res.retry_delay=1
q = Queue.new
res.send_async(Message.new("example.com", Types.A),q,q)
id, response, error = q.pop
assert_equal(id, q, "Id wrong!")
assert_valid_response(response)
assert_nil_error(error)
end
# @TODO@ Implement!! But then, why would anyone want to do this?
# def test_many_threaded_clients
# assert(false, "IMPLEMENT!")
# end
def test_reverse_lookup
m = Message.new("8.8.8.8", Types.PTR)
r = Resolver.new
q=Queue.new
r.send_async(m,q,q)
id,ret, error=q.pop
assert(ret.kind_of?(Message))
no_pointer=true
ret.each_answer do |answer|
if (answer.type==Types.PTR)
no_pointer=false
assert(answer.domainname.to_s=~/google/)
end
end
assert(!no_pointer)
end
# def test_bad_host
# res = Resolver.new({:nameserver => "localhost"})
# res.retry_times=1
# res.retry_delay=0
# res.query_timeout = 1
# q = Queue.new
# res.send_async(Message.new("example.com", Types.A), q, q)
# id, m, err = q.pop
# assert(id==q)
# assert(m == nil)
# assert(err.kind_of?(OtherResolvError) || err.kind_of?(IOError), "OtherResolvError or IOError expected : got #{err.class}")
# end
#
def test_nxdomain
resolver = Resolver.new
q = Queue.new
resolver .send_async(Message.new(BAD_DOMAIN_NAME, Types.A), q, 1)
id, m, error = q.pop
assert(id==1, "Id should have been 1 but was #{id}")
assert(m.rcode == RCode.NXDOMAIN, "Expected NXDOMAIN but got #{m.rcode} instead.")
assert_error_is_exception(error, NXDomain)
end
def test_timeouts
# test timeout behaviour for different retry, retrans, total timeout etc.
# Problem here is that many sockets will be created for queries which time out.
# Run a query which will not respond, and check that the timeout works
if (RUBY_PLATFORM !~ /darwin/)
start=stop=0
retry_times = 3
retry_delay=1
packet_timeout=2
# Work out what time should be, then time it to check
expected = ((2**(retry_times-1))*retry_delay) + packet_timeout
begin
res = Dnsruby::Resolver.new({:nameserver => "10.0.1.128"})
# res = Resolver.new({:nameserver => "213.248.199.17"})
res.packet_timeout=packet_timeout
res.retry_times=retry_times
res.retry_delay=retry_delay
start=Time.now
m = res.send_message(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A))
fail
rescue ResolvTimeout
stop=Time.now
time = stop-start
assert(time <= expected * 1.3 && time >= expected * 0.9, "Wrong time take, expected #{expected}, took #{time}")
end
end
end
def test_packet_timeout
res = Dnsruby::Resolver.new({:nameserver => []})
# res = Resolver.new({:nameserver => "10.0.1.128"})
start=stop=0
retry_times = retry_delay = packet_timeout= 10
query_timeout=2
begin
res.packet_timeout=packet_timeout
res.retry_times=retry_times
res.retry_delay=retry_delay
res.query_timeout=query_timeout
# Work out what time should be, then time it to check
expected = query_timeout
start=Time.now
m = res.send_message(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A))
fail
rescue Dnsruby::ResolvTimeout
stop=Time.now
time = stop-start
assert(time <= expected * 1.3 && time >= expected * 0.9, "Wrong time take, expected #{expected}, took #{time}")
end #
end
def test_queue_packet_timeout
# if (!RUBY_PLATFORM=~/darwin/)
res = Dnsruby::Resolver.new({:nameserver => "10.0.1.128"})
# bad = SingleResolver.new("localhost")
res.add_server("localhost")
expected = 2
res.query_timeout=expected
q = Queue.new
start = Time.now
m = res.send_async(Message.new("a.t.dnsruby.validation-test-servers.nominet.org.uk", Types.A), q, q)
id,ret,err = q.pop
stop = Time.now
assert(id=q)
assert(ret==nil)
assert(err.class == ResolvTimeout, "#{err.class}, #{err}")
time = stop-start
assert(time <= expected * 1.3 && time >= expected * 0.9, "Wrong time take, expected #{expected}, took #{time}")
# end
end
def test_illegal_src_port
# Also test all singleresolver ports ok
# Try to set src_port to an illegal value - make sure error raised, and port OK
res = Dnsruby::Resolver.new
res.port = 56789
tests = [53, 387, 1265, 3210, 48619]
tests.each do |bad_port|
begin
res.src_port = bad_port
fail("bad port #{bad_port}")
rescue
end
end
assert(res.single_resolvers[0].src_port = 56789)
end
def test_add_src_port
# Try setting and adding port ranges, and invalid ports, and 0.
# Also test all singleresolver ports ok
res = Resolver.new
res.src_port = [56789,56790, 56793]
assert(res.src_port == [56789,56790, 56793])
res.src_port = 56889..56891
assert(res.src_port == [56889,56890,56891])
res.add_src_port(60000..60002)
assert(res.src_port == [56889,56890,56891,60000,60001,60002])
res.add_src_port([60004,60005])
assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005])
res.add_src_port(60006)
assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006])
# Now test invalid src_ports
tests = [0, 53, [60007, 53], [60008, 0], 55..100]
tests.each do |x|
begin
res.add_src_port(x)
fail()
rescue
end
end
assert(res.src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006])
assert(res.single_resolvers[0].src_port == [56889,56890,56891,60000,60001,60002,60004,60005,60006])
end
def test_eventtype_api
# @TODO@ TEST THE Resolver::EventType interface!
end
end
# Tests to see that query_raw handles send_plain_message's return values correctly.
class TestRawQuery < Minitest::Test
KEY_NAME = 'key-name'
KEY = '0123456789'
ALGO = 'hmac-md5'
class CustomError < RuntimeError; end
# Returns a new resolver whose send_plain_message method always returns
# nil for the response, and a RuntimeError for the error.
def resolver_returning_error
resolver = Dnsruby::Resolver.new
def resolver.send_plain_message(_message)
[nil, CustomError.new]
end
resolver
end
# Returns a new resolver whose send_plain_message is overridden to return
# :response_from_send_plain_message instead of a real Dnsruby::Message,
# for easy comparison in the tests.
def resolver_returning_response
resolver = Dnsruby::Resolver.new
def resolver.send_plain_message(_message)
[:response_from_send_plain_message, nil]
end
resolver
end
# Test that when a strategy other than :raise or :return is passed,
# an ArgumentError is raised.
def test_bad_strategy
assert_raises(ArgumentError) do
resolver_returning_error.query_raw(Dnsruby::Message.new, :invalid_strategy)
end
end
# Test that when send_plain_message returns an error,
# and the error strategy is :raise, query_raw raises an error.
def test_raise_error
assert_raises(CustomError) do
resolver_returning_error.query_raw(Dnsruby::Message.new, :raise)
end
end
# Tests that if you don't specify an error strategy, an error will be
# returned rather than raised (i.e. strategy defaults to :return).
def test_return_error_is_default
_response, error = resolver_returning_error.query_raw(Dnsruby::Message.new)
assert error.is_a?(CustomError)
end
# Tests that when no error is returned, no error is raised.
def test_raise_no_error
response, _error = resolver_returning_response.query_raw(Dnsruby::Message.new, :raise)
assert_equal :response_from_send_plain_message, response
end
# Test that when send_plain_message returns an error, and the error strategy
# is set to :return, then an error is returned.
def test_return_error
_response, error = resolver_returning_error.query_raw(Dnsruby::Message.new, :return)
assert error.is_a?(CustomError)
end
# Test that when send_plain_message returns a valid and response
# and nil error, the same are returned by query_raw.
def test_return_no_error
response, error = resolver_returning_response.query_raw(Dnsruby::Message.new, :return)
assert_nil error
assert_equal :response_from_send_plain_message, response
end
def test_2_args_init
options = Dnsruby::Resolver.create_tsig_options(KEY_NAME, KEY)
assert_equal KEY_NAME, options[:name]
assert_equal KEY, options[:key]
assert_nil options[:algorithm]
end
def test_3_args_init
options = Dnsruby::Resolver.create_tsig_options(KEY_NAME,KEY,ALGO)
assert_equal KEY_NAME, options[:name]
assert_equal KEY, options[:key]
assert_equal ALGO, options[:algorithm]
end
def test_threads
resolver = Dnsruby::Resolver.new(nameserver: ["8.8.8.8", "8.8.4.4"])
resolver.query("google.com", "MX")
resolver.query("google.com", "MX")
resolver.query("google.com", "MX")
begin
resolver.query("googlöe.com", "MX")
rescue Dnsruby::ResolvError => e
# fine
end
resolver.query("google.com", "MX")
resolver.query("google.com", "MX")
begin
resolver.query("googlöe.com", "MX")
rescue Dnsruby::ResolvError => e
# fine
end
begin
resolver.query("googlöe.com", "MX")
rescue Dnsruby::ResolvError => e
# fine
end
# Dnsruby::Cache.delete("googlöe.com", "MX")
end
end
|