PageRenderTime 42ms CodeModel.GetById 2ms RepoModel.GetById 0ms app.codeStats 0ms

/test/resolv/test_dns.rb

http://github.com/ruby/ruby
Ruby | 317 lines | 283 code | 28 blank | 6 comment | 2 complexity | 32b9f2972b5a7bf0e01767ca763b876b MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0
  1. # frozen_string_literal: false
  2. require 'test/unit'
  3. require 'resolv'
  4. require 'socket'
  5. require 'tempfile'
  6. require 'minitest/mock'
  7. class TestResolvDNS < Test::Unit::TestCase
  8. def setup
  9. @save_do_not_reverse_lookup = BasicSocket.do_not_reverse_lookup
  10. BasicSocket.do_not_reverse_lookup = true
  11. end
  12. def teardown
  13. BasicSocket.do_not_reverse_lookup = @save_do_not_reverse_lookup
  14. end
  15. def with_udp(host, port)
  16. u = UDPSocket.new
  17. begin
  18. u.bind(host, port)
  19. yield u
  20. ensure
  21. u.close
  22. end
  23. end
  24. # [ruby-core:65836]
  25. def test_resolve_with_2_ndots
  26. conf = Resolv::DNS::Config.new :nameserver => ['127.0.0.1'], :ndots => 2
  27. assert conf.single?
  28. candidates = []
  29. conf.resolv('example.com') { |candidate, *args|
  30. candidates << candidate
  31. raise Resolv::DNS::Config::NXDomain
  32. }
  33. n = Resolv::DNS::Name.create 'example.com.'
  34. assert_equal n, candidates.last
  35. end
  36. def test_query_ipv4_address
  37. begin
  38. OpenSSL
  39. rescue LoadError
  40. skip 'autoload problem. see [ruby-dev:45021][Bug #5786]'
  41. end if defined?(OpenSSL)
  42. with_udp('127.0.0.1', 0) {|u|
  43. _, server_port, _, server_address = u.addr
  44. begin
  45. client_thread = Thread.new {
  46. Resolv::DNS.open(:nameserver_port => [[server_address, server_port]]) {|dns|
  47. dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
  48. }
  49. }
  50. server_thread = Thread.new {
  51. msg, (_, client_port, _, client_address) = Timeout.timeout(5) {u.recvfrom(4096)}
  52. id, word2, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn")
  53. qr = (word2 & 0x8000) >> 15
  54. opcode = (word2 & 0x7800) >> 11
  55. aa = (word2 & 0x0400) >> 10
  56. tc = (word2 & 0x0200) >> 9
  57. rd = (word2 & 0x0100) >> 8
  58. ra = (word2 & 0x0080) >> 7
  59. z = (word2 & 0x0070) >> 4
  60. rcode = word2 & 0x000f
  61. rest = msg[12..-1]
  62. assert_equal(0, qr) # 0:query 1:response
  63. assert_equal(0, opcode) # 0:QUERY 1:IQUERY 2:STATUS
  64. assert_equal(0, aa) # Authoritative Answer
  65. assert_equal(0, tc) # TrunCation
  66. assert_equal(1, rd) # Recursion Desired
  67. assert_equal(0, ra) # Recursion Available
  68. assert_equal(0, z) # Reserved for future use
  69. assert_equal(0, rcode) # 0:No-error 1:Format-error 2:Server-failure 3:Name-Error 4:Not-Implemented 5:Refused
  70. assert_equal(1, qdcount) # number of entries in the question section.
  71. assert_equal(0, ancount) # number of entries in the answer section.
  72. assert_equal(0, nscount) # number of entries in the authority records section.
  73. assert_equal(0, arcount) # number of entries in the additional records section.
  74. name = [3, "foo", 7, "example", 3, "org", 0].pack("Ca*Ca*Ca*C")
  75. assert_operator(rest, :start_with?, name)
  76. rest = rest[name.length..-1]
  77. assert_equal(4, rest.length)
  78. qtype, _ = rest.unpack("nn")
  79. assert_equal(1, qtype) # A
  80. assert_equal(1, qtype) # IN
  81. id = id
  82. qr = 1
  83. opcode = opcode
  84. aa = 0
  85. tc = 0
  86. rd = rd
  87. ra = 1
  88. z = 0
  89. rcode = 0
  90. qdcount = 0
  91. ancount = 1
  92. nscount = 0
  93. arcount = 0
  94. word2 = (qr << 15) |
  95. (opcode << 11) |
  96. (aa << 10) |
  97. (tc << 9) |
  98. (rd << 8) |
  99. (ra << 7) |
  100. (z << 4) |
  101. rcode
  102. msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn")
  103. type = 1
  104. klass = 1
  105. ttl = 3600
  106. rdlength = 4
  107. rdata = [192,0,2,1].pack("CCCC") # 192.0.2.1 (TEST-NET address) RFC 3330
  108. rr = [name, type, klass, ttl, rdlength, rdata].pack("a*nnNna*")
  109. msg << rr
  110. u.send(msg, 0, client_address, client_port)
  111. }
  112. result, _ = assert_join_threads([client_thread, server_thread])
  113. assert_instance_of(Array, result)
  114. assert_equal(1, result.length)
  115. rr = result[0]
  116. assert_instance_of(Resolv::DNS::Resource::IN::A, rr)
  117. assert_instance_of(Resolv::IPv4, rr.address)
  118. assert_equal("192.0.2.1", rr.address.to_s)
  119. assert_equal(3600, rr.ttl)
  120. end
  121. }
  122. end
  123. def test_query_ipv4_address_timeout
  124. with_udp('127.0.0.1', 0) {|u|
  125. _, port , _, host = u.addr
  126. start = nil
  127. rv = Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
  128. dns.timeouts = 0.1
  129. start = Time.now
  130. dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
  131. }
  132. t2 = Time.now
  133. diff = t2 - start
  134. assert rv.empty?, "unexpected: #{rv.inspect} (expected empty)"
  135. assert_operator 0.1, :<=, diff
  136. rv = Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
  137. dns.timeouts = [ 0.1, 0.2 ]
  138. start = Time.now
  139. dns.getresources("foo.example.org", Resolv::DNS::Resource::IN::A)
  140. }
  141. t2 = Time.now
  142. diff = t2 - start
  143. assert rv.empty?, "unexpected: #{rv.inspect} (expected empty)"
  144. assert_operator 0.3, :<=, diff
  145. }
  146. end
  147. def test_no_server
  148. u = UDPSocket.new
  149. u.bind("127.0.0.1", 0)
  150. _, port, _, host = u.addr
  151. u.close
  152. # A race condition here.
  153. # Another program may use the port.
  154. # But no way to prevent it.
  155. begin
  156. Timeout.timeout(5) do
  157. Resolv::DNS.open(:nameserver_port => [[host, port]]) {|dns|
  158. assert_equal([], dns.getresources("test-no-server.example.org", Resolv::DNS::Resource::IN::A))
  159. }
  160. end
  161. rescue Timeout::Error
  162. if RUBY_PLATFORM.match?(/mingw/)
  163. # cannot repo locally
  164. skip 'Timeout Error on MinGW CI'
  165. else
  166. raise Timeout::Error
  167. end
  168. end
  169. end
  170. def test_invalid_byte_comment
  171. bug9273 = '[ruby-core:59239] [Bug #9273]'
  172. Tempfile.create('resolv_test_dns_') do |tmpfile|
  173. tmpfile.print("\xff\x00\x40")
  174. tmpfile.close
  175. assert_nothing_raised(ArgumentError, bug9273) do
  176. Resolv::DNS::Config.parse_resolv_conf(tmpfile.path)
  177. end
  178. end
  179. end
  180. def test_resolv_conf_by_command
  181. Dir.mktmpdir do |dir|
  182. Dir.chdir(dir) do
  183. assert_raise(Errno::ENOENT, Errno::EINVAL) do
  184. Resolv::DNS::Config.parse_resolv_conf("|echo foo")
  185. end
  186. end
  187. end
  188. end
  189. def test_dots_diffences
  190. name1 = Resolv::DNS::Name.create("example.org")
  191. name2 = Resolv::DNS::Name.create("ex.ampl.eo.rg")
  192. assert_not_equal(name1, name2, "different dots")
  193. end
  194. def test_case_insensitive_name
  195. bug10550 = '[ruby-core:66498] [Bug #10550]'
  196. lower = Resolv::DNS::Name.create("ruby-lang.org")
  197. upper = Resolv::DNS::Name.create("Ruby-Lang.org")
  198. assert_equal(lower, upper, bug10550)
  199. end
  200. def test_ipv6_name
  201. addr = Resolv::IPv6.new("\0"*16)
  202. labels = addr.to_name.to_a
  203. expected = (['0'] * 32 + ['ip6', 'arpa']).map {|label| Resolv::DNS::Label::Str.new(label) }
  204. assert_equal(expected, labels)
  205. end
  206. def test_ipv6_create
  207. ref = '[Bug #11910] [ruby-core:72559]'
  208. assert_instance_of Resolv::IPv6, Resolv::IPv6.create('::1'), ref
  209. assert_instance_of Resolv::IPv6, Resolv::IPv6.create('::1:127.0.0.1'), ref
  210. end
  211. def test_ipv6_to_s
  212. test_cases = [
  213. ["2001::abcd:abcd:abcd", "2001::ABcd:abcd:ABCD"],
  214. ["2001:db8::1", "2001:db8::0:1"],
  215. ["::", "0:0:0:0:0:0:0:0"],
  216. ["2001::", "2001::0"],
  217. ["2001:db8::1:1:1:1:1", "2001:db8:0:1:1:1:1:1"],
  218. ["1::1:0:0:0:1", "1:0:0:1:0:0:0:1"],
  219. ["1::1:0:0:1", "1:0:0:0:1:0:0:1"],
  220. ]
  221. test_cases.each do |expected, ipv6|
  222. assert_equal expected, Resolv::IPv6.create(ipv6).to_s
  223. end
  224. end
  225. def test_ipv6_should_be_16
  226. ref = '[rubygems:1626]'
  227. broken_message =
  228. "\0\0\0\0\0\0\0\0\0\0\0\1" \
  229. "\x03ns2\bdnsimple\x03com\x00" \
  230. "\x00\x1C\x00\x01\x00\x02OD" \
  231. "\x00\x10$\x00\xCB\x00 I\x00\x01\x00\x00\x00\x00"
  232. e = assert_raise_with_message(Resolv::DNS::DecodeError, /IPv6 address must be 16 bytes/, ref) do
  233. Resolv::DNS::Message.decode broken_message
  234. end
  235. assert_kind_of(ArgumentError, e.cause)
  236. end
  237. def test_too_big_label_address
  238. n = 2000
  239. m = Resolv::DNS::Message::MessageEncoder.new {|msg|
  240. 2.times {
  241. n.times {|i| msg.put_labels(["foo#{i}"]) }
  242. }
  243. }
  244. Resolv::DNS::Message::MessageDecoder.new(m.to_s) {|msg|
  245. 2.times {
  246. n.times {|i|
  247. assert_equal(["foo#{i}"], msg.get_labels.map {|label| label.to_s })
  248. }
  249. }
  250. }
  251. assert_operator(2**14, :<, m.to_s.length)
  252. end
  253. def assert_no_fd_leak
  254. socket = assert_throw(self) do |tag|
  255. Resolv::DNS.stub(:bind_random_port, ->(s, *) {throw(tag, s)}) do
  256. yield.getname("8.8.8.8")
  257. end
  258. end
  259. assert_predicate(socket, :closed?, "file descriptor leaked")
  260. end
  261. def test_no_fd_leak_connected
  262. assert_no_fd_leak {Resolv::DNS.new(nameserver_port: [['127.0.0.1', 53]])}
  263. end
  264. def test_no_fd_leak_unconnected
  265. assert_no_fd_leak {Resolv::DNS.new}
  266. end
  267. def test_each_name
  268. dns = Resolv::DNS.new
  269. def dns.each_resource(name, typeclass)
  270. yield typeclass.new(name)
  271. end
  272. dns.each_name('127.0.0.1') do |ptr|
  273. assert_equal('1.0.0.127.in-addr.arpa', ptr.to_s)
  274. end
  275. dns.each_name(Resolv::IPv4.create('127.0.0.1')) do |ptr|
  276. assert_equal('1.0.0.127.in-addr.arpa', ptr.to_s)
  277. end
  278. dns.each_name('::1') do |ptr|
  279. assert_equal('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa', ptr.to_s)
  280. end
  281. dns.each_name(Resolv::IPv6.create('::1')) do |ptr|
  282. assert_equal('1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa', ptr.to_s)
  283. end
  284. dns.each_name(Resolv::DNS::Name.create('1.0.0.127.in-addr.arpa.')) do |ptr|
  285. assert_equal('1.0.0.127.in-addr.arpa', ptr.to_s)
  286. end
  287. assert_raise(Resolv::ResolvError) { dns.each_name('example.com') }
  288. end
  289. end