From e70c4de5ff7c2d0bd1a299e2c5d5fc3480e168f7 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Fri, 25 Sep 2026 09:45:50 +0900 Subject: [PATCH] Skip search candidates longer than 255 octets Appending a search domain can take a relative name past the 255 octet limit. The query was still sent, and because the decoder rejects a name that long in the reply, the lookup sat until the timeout before trying the next candidate. Co-Authored-By: Claude Opus 5.5 --- lib/resolv.rb | 7 ++++++- test/resolv/test_dns.rb | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/resolv.rb b/lib/resolv.rb index 174b913..e55dd80 100644 --- a/lib/resolv.rb +++ b/lib/resolv.rb @@ -1244,7 +1244,12 @@ def generate_candidates(name) [name] else abs = Name.new(name.to_a) - search = @search.map {|domain| Name.new(name.to_a + domain)} + search = @search.map {|domain| name.to_a + domain} + # A search domain can push a candidate past 255 octets even though + # the name itself fits. No reply can carry such a name, so skip it + # and leave the other candidates to be tried. [RFC 1035 3.1] + search.reject! {|labels| labels.inject(1) {|size, label| size + 1 + label.string.bytesize} > 255} + search.map! {|labels| Name.new(labels)} if @ndots <= name.length - 1 [abs, *search].uniq else diff --git a/test/resolv/test_dns.rb b/test/resolv/test_dns.rb index b8ecef2..260feea 100644 --- a/test/resolv/test_dns.rb +++ b/test/resolv/test_dns.rb @@ -154,6 +154,23 @@ def test_conf_search_root_domain assert_equal ['example.com', 'example.com.local'], candidates.map(&:to_s) end + def test_conf_search_skips_candidate_over_255_octets + # 246 octets encoded, so a search domain may add at most 9 more. + name = (["a" * 63] * 3 + ["a" * 52]).join(".") + conf = Resolv::DNS::Config.new(nameserver: '127.0.0.1', search: ['b' * 9, 'c' * 8], ndots: 1) + conf.lazy_initialize + candidates = conf.generate_candidates(name) + assert_equal [name, "#{name}.#{'c' * 8}"], candidates.map(&:to_s) + msg = Resolv::DNS::Message.new + msg.add_question(candidates[1], Resolv::DNS::Resource::IN::A) + assert_equal candidates[1], Resolv::DNS::Message.decode(msg.encode).question[0][0] + + conf = Resolv::DNS::Config.new(nameserver: '127.0.0.1', search: ['b' * 9], ndots: 5) + conf.lazy_initialize + candidates = conf.generate_candidates(name) + assert_equal [name], candidates.map(&:to_s) + end + def test_query_ipv4_address begin OpenSSL