From d5f0e3726041a36c97d09747d4a0190b2c345958 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Fri, 17 Apr 2026 04:09:37 -0400 Subject: [PATCH] Include hosts resolver in request socket DNS lookup (#38699) --- app/lib/request.rb | 8 +++----- spec/lib/fasp/request_spec.rb | 16 +++++++++------- spec/lib/request_spec.rb | 35 +++++++++++++++++++++-------------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/app/lib/request.rb b/app/lib/request.rb index 66d7ece70f..376a37638c 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -292,11 +292,9 @@ class Request begin addresses = [IPAddr.new(host)] rescue IPAddr::InvalidAddressError - Resolv::DNS.open do |dns| - dns.timeouts = 5 - addresses = dns.getaddresses(host) - addresses = addresses.grep(Resolv::IPv6).take(2) + addresses.grep_v(Resolv::IPv6).take(2) - end + resolvers = [Resolv::Hosts.new, Resolv::DNS.new.tap { |dns| dns.timeouts = 5 }] + addresses = Resolv.new(resolvers).getaddresses(host) + addresses = addresses.grep(Resolv::IPv6::Regex).take(2) + addresses.grep_v(Resolv::IPv6::Regex).take(2) end socks = [] diff --git a/spec/lib/fasp/request_spec.rb b/spec/lib/fasp/request_spec.rb index 6456057b5e..cdc20270cd 100644 --- a/spec/lib/fasp/request_spec.rb +++ b/spec/lib/fasp/request_spec.rb @@ -86,14 +86,16 @@ RSpec.describe Fasp::Request do WebMock.enable! end + let(:resolv_service) { instance_double(Resolv) } + + before do + allow(Resolv).to receive(:new).and_return(resolv_service) + allow(resolv_service).to receive(:getaddresses).with('reqprov.example.com').and_return(%w(0.0.0.0 2001:db8::face)) + end + it 'raises Mastodon::ValidationError' do - resolver = instance_double(Resolv::DNS) - - allow(resolver).to receive(:getaddresses).with('reqprov.example.com').and_return(%w(0.0.0.0 2001:db8::face)) - allow(resolver).to receive(:timeouts=).and_return(nil) - allow(Resolv::DNS).to receive(:open).and_yield(resolver) - - expect { subject.send(method, '/test_path') }.to raise_error(Mastodon::ValidationError) + expect { subject.send(method, '/test_path') } + .to raise_error(Mastodon::ValidationError) end end end diff --git a/spec/lib/request_spec.rb b/spec/lib/request_spec.rb index da61823455..228c8bbb67 100644 --- a/spec/lib/request_spec.rb +++ b/spec/lib/request_spec.rb @@ -66,15 +66,20 @@ RSpec.describe Request do expect(a_request(:get, 'http://example.com')).to have_been_made.once end - it 'executes a HTTP request when the first address is private' do - resolver = instance_double(Resolv::DNS) + context 'when first address is private' do + let(:resolv_service) { instance_double(Resolv) } - allow(resolver).to receive(:getaddresses).with('example.com').and_return(%w(0.0.0.0 2001:4860:4860::8844)) - allow(resolver).to receive(:timeouts=).and_return(nil) - allow(Resolv::DNS).to receive(:open).and_yield(resolver) + before do + allow(Resolv).to receive(:new).and_return(resolv_service) + allow(resolv_service).to receive(:getaddresses).with('example.com').and_return(%w(0.0.0.0 2001:4860:4860::8844)) + end - expect { |block| subject.perform(&block) }.to yield_control - expect(a_request(:get, 'http://example.com')).to have_been_made.once + it 'executes a HTTP request' do + expect { |block| subject.perform(&block) } + .to yield_control + expect(a_request(:get, 'http://example.com')) + .to have_been_made.once + end end it 'makes a request with expected headers, yields, and closes the underlying connection' do @@ -123,14 +128,16 @@ RSpec.describe Request do WebMock.enable! end + let(:resolv_service) { instance_double(Resolv) } + + before do + allow(Resolv).to receive(:new).with([be_a(Resolv::Hosts), be_a(Resolv::DNS)]).and_return(resolv_service) + allow(resolv_service).to receive(:getaddresses).with('example.com').and_return(%w(0.0.0.0 2001:db8::face)) + end + it 'raises Mastodon::ValidationError' do - resolver = instance_double(Resolv::DNS) - - allow(resolver).to receive(:getaddresses).with('example.com').and_return(%w(0.0.0.0 2001:db8::face)) - allow(resolver).to receive(:timeouts=).and_return(nil) - allow(Resolv::DNS).to receive(:open).and_yield(resolver) - - expect { subject.perform }.to raise_error Mastodon::ValidationError + expect { subject.perform } + .to raise_error Mastodon::ValidationError end end