From 7984602a3ca2be4b41308e3c94a38aff7f3d7400 Mon Sep 17 00:00:00 2001 From: BobSilent Date: Tue, 25 Oct 2022 22:55:19 +0200 Subject: [PATCH 1/3] Handle RCode::ServFail --- lib/resolv.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/resolv.rb b/lib/resolv.rb index 47c4ef6..7c05540 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -116,8 +116,10 @@ def each_address(name) yielded = false @resolvers.each {|r| r.each_address(name) {|address| - yield address.to_s - yielded = true + if block_given? + yield address.to_s + yielded = true + end } return if yielded } @@ -547,6 +549,8 @@ def fetch_resource(name, typeclass) yield(reply, reply_name) end return + when RCode::ServFail + next when RCode::NXDomain raise Config::NXDomain.new(reply_name.to_s) else From f53cea187694eb2b392cf83335733f3b1f030dfb Mon Sep 17 00:00:00 2001 From: BobSilent Date: Wed, 15 Nov 2023 21:02:54 +0100 Subject: [PATCH 2/3] add testcase --- test/resolv/test_dns.rb | 116 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb index 9d243bb..0c3128b 100644 --- a/test/resolv/test_dns.rb +++ b/test/resolv/test_dns.rb @@ -457,4 +457,118 @@ def dns.each_resource(name, typeclass) end assert_raise(Resolv::ResolvError) { dns.each_name('example.com') } end -end + + def test_query_ipv4_try_next_dns_if_first_answers_with_servfail + begin + OpenSSL + rescue LoadError + omit 'autoload problem. see [ruby-dev:45021][Bug #5786]' + end if defined?(OpenSSL) + + with_udp('127.0.0.1', 0) {|first| + with_udp('127.0.0.1', 0) {|second| + _, server_one_port, _, server_one_address = first.addr + _, server_two_port, _, server_two_address = second.addr + begin + client_thread = Thread.new { + Resolv::DNS.open(:nameserver_port => [[server_one_address, server_one_port], [server_two_address, server_two_port]]).getaddress("example.org") + } + server_one_thread = Thread.new { + msg, (_, client_port, _, client_address) = Timeout.timeout(5) {first.recvfrom(4096)} + id, flags, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn") + + qr = (flags & 0x8000) >> 15 + opcode = (flags & 0x7800) >> 11 + aa = (flags & 0x0400) >> 10 + tc = (flags & 0x0200) >> 9 + rd = (flags & 0x0100) >> 8 + ra = (flags & 0x0080) >> 7 + z = (flags & 0x0070) >> 4 + rcode = flags & 0x000f + _rest = msg[12..-1] + questions = msg.bytes[12..-1] + + id = id + qr = 1 + opcode = opcode + aa = 0 + tc = 0 + rd = rd + ra = 1 + z = 0 + rcode = 2 # ServFail + qdcount = 1 + ancount = 0 + nscount = 0 + arcount = 0 + word2 = (qr << 15) | + (opcode << 11) | + (aa << 10) | + (tc << 9) | + (rd << 8) | + (ra << 7) | + (z << 4) | + rcode + msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn") + msg << questions.pack('c*') + + first.send(msg, 0, client_address, client_port) + } + server_two_thread = Thread.new { + msg, (_, client_port, _, client_address) = Timeout.timeout(5) {second.recvfrom(4096)} + id, flags, qdcount, ancount, nscount, arcount = msg.unpack("nnnnnn") + + qr = (flags & 0x8000) >> 15 + opcode = (flags & 0x7800) >> 11 + aa = (flags & 0x0400) >> 10 + tc = (flags & 0x0200) >> 9 + rd = (flags & 0x0100) >> 8 + ra = (flags & 0x0080) >> 7 + z = (flags & 0x0070) >> 4 + rcode = flags & 0x000f + _rest = msg[12..-1] + + questions = msg.bytes[12..-1] + + id = id + qr = 1 + opcode = opcode + aa = 0 + tc = 0 + rd = rd + ra = 1 + z = 0 + rcode = 0 # NoError + qdcount = 1 + ancount = 1 + nscount = 0 + arcount = 0 + word2 = (qr << 15) | + (opcode << 11) | + (aa << 10) | + (tc << 9) | + (rd << 8) | + (ra << 7) | + (z << 4) | + rcode + msg = [id, word2, qdcount, ancount, nscount, arcount].pack("nnnnnn") + msg << questions.pack('c*') + type = 1 + klass = 1 + ttl = 3600 + rdlength = 4 + rdata = [52,0,2,1].pack("CCCC") + rr = [0xc00c, type, klass, ttl, rdlength, rdata].pack("nnnNna*") + msg << rr + + second.send(msg, 0, client_address, client_port) + } + result, _, _ = assert_join_threads([client_thread, server_one_thread, server_two_thread]) + + assert_instance_of(Resolv::IPv4, result) + assert_equal("52.0.2.1", result.to_s) + end + } + } + end +end \ No newline at end of file From 46f1758ff774268f67e11c26262b788fb716b6f1 Mon Sep 17 00:00:00 2001 From: BobSilent Date: Thu, 14 Dec 2023 09:24:33 +0100 Subject: [PATCH 3/3] code review: revert an unnecessary change --- lib/resolv.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/resolv.rb b/lib/resolv.rb index c69b035..a18c47e 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -116,10 +116,8 @@ def each_address(name) yielded = false @resolvers.each {|r| r.each_address(name) {|address| - if block_given? - yield address.to_s - yielded = true - end + yield address.to_s + yielded = true } return if yielded }