Skip to content

Commit 405b702

Browse files
committed
Better handling of system resolver.
1 parent 9d4dac0 commit 405b702

File tree

8 files changed

+81
-32
lines changed

8 files changed

+81
-32
lines changed

fixtures/async/dns/test_server.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
module Async
77
module DNS
88
class TestServer < Server
9-
def initialize(endpoint = DEFAULT_ENDPOINT, resolver: Resolver.new, **options)
9+
def initialize(endpoint = DEFAULT_ENDPOINT, resolver: Resolver.default, **options)
1010
super(endpoint, **options)
1111

1212
@resolver = resolver

lib/async/dns/resolver.rb

+22-9
Original file line numberDiff line numberDiff line change
@@ -26,38 +26,51 @@ class ResolutionFailure < StandardError
2626

2727
# Resolve names to addresses using the DNS protocol.
2828
class Resolver
29+
# The default resolver for the system.
30+
def self.default(**options)
31+
System.resolver(**options)
32+
end
33+
2934
# Servers are specified in the same manor as options[:listen], e.g.
3035
# [:tcp/:udp, address, port]
3136
# In the case of multiple servers, they will be checked in sequence.
32-
def initialize(endpoint = nil, origin: nil, cache: Cache.new, timeout: nil, ndots: 1, search: [nil])
33-
@endpoint = endpoint || System.default_nameservers
34-
35-
if timeout
36-
@endpoint = @endpoint.with(timeout: timeout)
37-
end
37+
def initialize(endpoint, timeout: nil, ndots: 1, search: nil, origin: nil, cache: Cache.new, **options)
38+
@endpoint = endpoint
3839

3940
# Legacy support for multiple endpoints:
4041
if @endpoint.is_a?(Array)
42+
warn "Using Array specifications for endpoint is deprecated. Please use IO::Endpoint::CompositeEndpoint instead.", uplevel: 1
43+
4144
endpoints = @endpoint.map do |specification|
4245
::IO::Endpoint.public_send(specification[0], *specification[1..-1])
4346
end
4447

4548
@endpoint = ::IO::Endpoint.composite(*endpoints)
4649
end
4750

48-
@ndots = ndots
51+
if timeout
52+
@endpoint = @endpoint.with(timeout: timeout)
53+
end
4954

50-
@search = search
55+
@ndots = ndots
56+
if search
57+
@search = search
58+
else
59+
@search = [nil]
60+
end
5161

5262
if origin
5363
@search = [origin] + @search
5464
end
5565

5666
@cache = cache
67+
@options = options
68+
5769
@count = 0
5870
end
5971

60-
attr_accessor :origin
72+
# The search domains, which are used to generate fully qualified names if required.
73+
attr :search
6174

6275
# Generates a fully qualified name from a given name.
6376
#

lib/async/dns/system.rb

+52-12
Original file line numberDiff line numberDiff line change
@@ -98,29 +98,57 @@ def parse_hosts(io)
9898
end
9999
end
100100

101+
DEFAULT_TIMEOUT = 5.0
102+
101103
# Parse the `resolv.conf` file and return a list of nameservers.
102104
def self.parse_resolv_configuration(path)
103105
nameservers = []
106+
search = nil
107+
ndots = 1
108+
edns = nil
109+
timeout = DEFAULT_TIMEOUT
110+
104111
File.open(path) do |file|
105112
file.each do |line|
106113
# Remove any comments:
107114
line.sub!(/[#;].*/, '')
108115

109116
# Extract resolv.conf command:
110-
keyword, *args = line.split(/\s+/)
117+
keyword, *arguments = line.split(/\s+/)
111118

112119
case keyword
113120
when 'nameserver'
114-
nameservers += args
121+
nameservers.concat(arguments)
122+
when 'domain', 'search'
123+
search = arguments
124+
when 'options'
125+
arguments.each do |argument|
126+
key, value = argument.split(':', 2)
127+
128+
case key
129+
when 'ndots'
130+
ndots = value.to_i
131+
when 'edns0'
132+
edns = 0
133+
when 'timeout'
134+
timeout = value.to_f
135+
end
136+
end
115137
end
116138
end
117139
end
118140

119-
return nameservers
141+
return {
142+
nameservers: nameservers,
143+
search: search,
144+
ndots: ndots,
145+
edns: edns,
146+
timeout: timeout,
147+
}
120148
end
121149

122150
# Get a list of standard nameserver connections which can be used for querying any standard servers that the system has been configured with.
123-
def self.standard_connections(nameservers, **options)
151+
def self.endpoint_for(nameservers, **options)
124152
connections = []
125153

126154
nameservers.each do |host|
@@ -132,21 +160,33 @@ def self.standard_connections(nameservers, **options)
132160
end
133161

134162
# Get a list of standard nameserver connections which can be used for querying any standard servers that the system has been configured with. There is no equivalent facility to use the `hosts` file at present.
135-
def self.nameservers(**options)
163+
def self.resolver(**options)
136164
nameservers = []
137165

138166
if File.exist? RESOLV_CONF
139-
nameservers = parse_resolv_configuration(RESOLV_CONF)
167+
options.update(parse_resolv_configuration(RESOLV_CONF))
168+
nameservers = options.delete(:nameservers)
140169
elsif defined?(Win32::Resolv) and RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/
141170
search, nameservers = Win32::Resolv.get_resolv_info
171+
options.update(search: search)
142172
end
143173

144-
return standard_connections(nameservers, **options)
145-
end
146-
147-
# Get a list of default nameservers.
148-
def self.default_nameservers
149-
self.nameservers(timeout: 5.0)
174+
if search = options[:search]
175+
unless search.include?('.')
176+
search << nil
177+
end
178+
else
179+
options[:search] = [nil]
180+
end
181+
182+
timeout = options.delete(:timeout) || DEFAULT_TIMEOUT
183+
endpoint = self.endpoint_for(nameservers, timeout: timeout)
184+
185+
if block_given?
186+
yield endpoint, **options
187+
else
188+
return Resolver.new(endpoint, **options)
189+
end
150190
end
151191
end
152192
end

readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Here is a simple example showing how to use the resolver:
2626

2727
``` ruby
2828
Async::Reactor.run do
29-
resolver = Async::DNS::Resolver.new()
29+
resolver = Async::DNS::System.resolver
3030

3131
addresses = resolver.addresses_for("www.google.com.")
3232

test/async/dns/resolver.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
describe Async::DNS::Resolver do
1111
include Sus::Fixtures::Async::ReactorContext
1212

13-
let(:resolver) {Async::DNS::Resolver.new}
13+
let(:resolver) {Async::DNS::Resolver.default}
1414

1515
it "should result in non-existent domain" do
1616
response = resolver.query('foobar.example.com', Resolv::DNS::Resource::IN::A)
@@ -64,7 +64,7 @@
6464
end
6565

6666
with '#fully_qualified_name' do
67-
let(:resolver) {Async::DNS::Resolver.new(origin: "foo.bar.")}
67+
let(:resolver) {Async::DNS::Resolver.default(origin: "foo.bar.", search: nil)}
6868

6969
it "should generate fully qualified domain name with specified origin" do
7070
fully_qualified_names = resolver.fully_qualified_names("baz").to_a

test/async/dns/resolver/consistency.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def before
7878
resolved_a = resolved_b = nil
7979

8080
async_dns_performance = Benchmark.measure do
81-
resolver = Async::DNS::Resolver.new
81+
resolver = Async::DNS::Resolver.default
8282

8383
resolved_a = DOMAINS.to_h do |domain|
8484
[domain, resolver.addresses_for(domain)]

test/async/dns/system.rb

+1-5
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,8 @@
1111
describe Async::DNS::System do
1212
include Sus::Fixtures::Async::ReactorContext
1313

14-
it "should have at least one namesever" do
15-
expect(Async::DNS::System.nameservers).to have_attributes(size: be > 0)
16-
end
17-
1814
it "should respond to query for google.com" do
19-
resolver = Async::DNS::Resolver.new(Async::DNS::System.nameservers)
15+
resolver = Async::DNS::Resolver.default
2016

2117
response = resolver.query('google.com')
2218

test/async/dns/transaction.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
let(:query) {Resolv::DNS::Message.new(0)}
1717
let(:question) {Resolv::DNS::Name.create("www.google.com.")}
1818
let(:response) {Resolv::DNS::Message.new(0)}
19-
let(:resolver) {Async::DNS::Resolver.new}
19+
let(:resolver) {Async::DNS::Resolver.default}
2020

2121
it "should append an address" do
2222
transaction = Async::DNS::Transaction.new(server, query, question, IN::A, response)

0 commit comments

Comments
 (0)