bug -- resolv.rb dies when "domain" in resolv.conf has no arg

S

Sam Roberts

Example input:

-- domain.rb --
domain
nameserver 1.2.3.4
--

What happens with an unpatched resolv.rb is:

[ensemble] ~/p/ruby/zeroconf $ ruby18 -rresolv -rpp -e '(r=Resolv::DNS::Config.new("./domain.conf")).lazy_initialize; pp r'
/usr/local/lib/ruby/1.8/resolv.rb:921:in `split': private method `scan' called for nil:NilClass (NoMethodError)
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `lazy_initialize'
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `map'
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `lazy_initialize'
from /usr/local/lib/ruby/1.8/resolv.rb:761:in `synchronize'
from /usr/local/lib/ruby/1.8/resolv.rb:761:in `lazy_initialize'
from -e:1

Please don't tell me the resolv.conf synatx is invalid - these files are
automatically created by OS X's network system. Also, resolv.rb accepts
an empty nameserver and an empty search command without problems.

-- search.rb --
search
nameserver 1.2.3.4
--

Patch to fix this:

Index: 1.8-resolv.rb
===================================================================
--- 1.8-resolv.rb (revision 5)
+++ 1.8-resolv.rb (working copy)
@@ -734,7 +734,7 @@
when 'nameserver'
nameserver += args
when 'domain'
- search = [args[0]]
+ search = args[0, 1]
when 'search'
search = args
end
===================================================================

Without this patch, @search becomes [nil] when domain had no arguments,
causing death later.

This can be tested with:

ruby18 -r1.8-resolv -rpp -e '(r=Resolv::DNS::Config.new("./search.conf")).lazy_initialize; pp r'
ruby18 -r1.8-resolv -rpp -e '(r=Resolv::DNS::Config.new("./domain.conf")).lazy_initialize; pp r'

You can see the behaviour before/after the patch.

Note that search (before) and domain (now, instead of dieing) with no
arguments both surpress the automagical choice of a search path based on
Socket.hostname. I believe this to be the correct behaviour, because it
seems reasonable to want to surpress the lookup (as a config option),
and because this is how the OS X C library resolver behaves.

Thanks,
Sam
 
T

Tanaka Akira

Sam Roberts said:
[ensemble] ~/p/ruby/zeroconf $ ruby18 -rresolv -rpp -e '(r=Resolv::DNS::Config.new("./domain.conf")).lazy_initialize; pp r'
/usr/local/lib/ruby/1.8/resolv.rb:921:in `split': private method `scan' called for nil:NilClass (NoMethodError)
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `lazy_initialize'
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `map'
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `lazy_initialize'
from /usr/local/lib/ruby/1.8/resolv.rb:761:in `synchronize'
from /usr/local/lib/ruby/1.8/resolv.rb:761:in `lazy_initialize'
from -e:1

Thank you for the report.
Note that search (before) and domain (now, instead of dieing) with no
arguments both surpress the automagical choice of a search path based on
Socket.hostname. I believe this to be the correct behaviour, because it
seems reasonable to want to surpress the lookup (as a config option),
and because this is how the OS X C library resolver behaves.

bind-9.3.0/lib/bind/resolv/res_init.c seems to ignore domain and
search directive with no arguments. So I modified resolv.rb to ignore
them. This may be incompatible with OS X C library resolver, though.
 
S

Sam Roberts

Quoteing (e-mail address removed), on Wed, Jan 19, 2005 at 01:29:57AM +0900:
Sam Roberts said:
[ensemble] ~/p/ruby/zeroconf $ ruby18 -rresolv -rpp -e '(r=Resolv::DNS::Config.new("./domain.conf")).lazy_initialize; pp r'
/usr/local/lib/ruby/1.8/resolv.rb:921:in `split': private method `scan' called for nil:NilClass (NoMethodError)
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `lazy_initialize'
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `map'
from /usr/local/lib/ruby/1.8/resolv.rb:788:in `lazy_initialize'
from /usr/local/lib/ruby/1.8/resolv.rb:761:in `synchronize'
from /usr/local/lib/ruby/1.8/resolv.rb:761:in `lazy_initialize'
from -e:1

Thank you for the report.

Thank you for the fix!
bind-9.3.0/lib/bind/resolv/res_init.c seems to ignore domain and
search directive with no arguments. So I modified resolv.rb to ignore
them. This may be incompatible with OS X C library resolver, though.

That's OK, it can't be compatible with everybody's resolver libraries,
so being compatible with bind9 seems very reasonable.

Sam
 
S

Sam Roberts

resolv.rb:877 does

raise ResolvError.new("DNS resolv error: #{name}")

This means that when Resolv::DNS fails to find at least one record for
a query, it raises an error, preventing the next resolver in @resolvers
from being checked (see Resolv#each_address()).

Symptoms:

- you can't do a DNS lookup, then fallback to a Hosts lookup:

resolv = Resolv.new([Resolv::DNS.new, Resolv::Hosts.new])

resolv.getaddress("localhost")

# This will fail (unless your DNS server has an entry for "localhost",
# some do). Doing the opposite (the default) does work, i.e. doing
# the hosts lookup, then the DNS lookup.


- resolv.rb:228 (and probably 256) are no-ops

Instead of getting ResolvError.new("no address for #{name}') raised as
it appears was intended, you will always get the lower level "DNS
resolv error".

- if you have two DNS resolvers registered the second will never be
reached

You might do this if the first uses resolv.conf for its configuration,
and you add a second with a default config specified with a Hash:

resolv = Resolv.new([Resolv::DNS.new, Resolv::DNS.new( ... my config ...) ])


- If you write you own resolver module, then the failure of the DNS
module prevents the next resolver from being used to attempt the
lookup.


Cheers,
Sam


Example patch:

===================================================================
--- 1.8-resolv.rb (revision 11)
+++ 1.8-resolv.rb (working copy)
@@ -226,6 +226,8 @@
def getaddress(name)
each_address(name) {|address| return address}
raise ResolvError.new("no address for #{name}")
+# - this line is a no-op, it isn't reached because an internal DNS error is
+# raised instead.
end

def getaddresses(name)
@@ -872,7 +874,8 @@
rescue OtherResolvError
raise ResolvError.new("DNS error: #{$!.message}")
end
- raise ResolvError.new("DNS resolv error: #{name}")
+# raise ResolvError.new("DNS resolv error: #{name}")
+# - this shouldn't be fatal, the next resolver might be able to resolve this name!
end

class NXDomain < ResolvError


Test code:

#!/usr/bin/ruby

require 'pp'
require 'socket'
require 'resolv'

puts "(C library resolver) localhost -->"
addr = IPSocket.getaddress("localhost")
pp addr

# This is the default, args = [Resolv::Hosts.new, Resolv::DNS.new]
resolv = Resolv.new

puts "(Hosts, DNS) localhost -->"
addr = resolv.getaddress("localhost")
pp addr
puts "OK - address resolves!"

begin
puts "(Hosts, DNS) asergh.net -->"
addr = resolv.getaddress("asergh.net")
pp addr
rescue
pp $!
unless($!.to_s =~ /^no address for/)
puts "BUG - this should have hit resolv.rb:228, instead it is returning a DNS-specific error!"
end
end

# This is trying DNS lookup before host lookup
resolv = Resolv.new([Resolv::DNS.new, Resolv::Hosts.new])

begin
puts "(resolv) localhost -->"
addr = resolv.getaddress("localhost")
pp addr
rescue
pp $!
puts "BUG - this failed to find localhost when the resolvers were reordered!"
end
 
T

Tanaka Akira

resolv.rb:877 does

raise ResolvError.new("DNS resolv error: #{name}")

This means that when Resolv::DNS fails to find at least one record for
a query, it raises an error, preventing the next resolver in @resolvers
from being checked (see Resolv#each_address()).

Thank you for the report.

Resolv::DNS#each_address shouldn't raise ResolvError.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top