Resolve SystemStackError issue when resolving IP address in Ruby

  sonic0002        2017-07-08 09:54:35       5,713        0    

In Ruby, Resolv is the default DNS resolution implementation. It can be used to resolve IP address of a hostname. To use it, one just needs to require 'resolv' in the code. But sometimes, a user would want to check /etc/hosts first or some other mechanisms to resolve an IP address. In this case, one can require 'resolv-replace' and then replace the default DNS resolvers with customized DNS resolvers.

For example, using resolv-replace, one would write

require 'resolv-replace'
Resolv::DefaultResolver.replace_resolvers([Resolv::Hosts.new, Resolv::DNS.new])
Resolv.getaddress 'some-site.com'

In this case, it would first go to the /etc/hosts and look up some-site.com, if its IP is found there, that one will be returned. Otherwise, the real DNS server will be searched for, those DNS servers are usually defined in /etc/resolv.conf. The file looks like

One can also replace other DNS resolver implementation as well. This resolv-replace has become very popular in Ruby and Rails applications. But this would cause some problem in some cases if something is wrongly configured in /etc/resolv.conf. One of the issues would be "SystemStackError: stack level too deep". Why would the stack overflows? There must be some recursion happens. The backtrace would look like

SystemStackError: stack level too deep
	from /usr/lib/ruby/1.8/resolv.rb:1336:in `put_labels'
	from /usr/lib/ruby/1.8/resolv.rb:1330:in `each_index'
	from /usr/lib/ruby/1.8/resolv.rb:1330:in `put_labels'
	from /usr/lib/ruby/1.8/resolv.rb:1326:in `put_name'
	from /usr/lib/ruby/1.8/resolv.rb:1272:in `encode'
	from /usr/lib/ruby/1.8/resolv.rb:1270:in `each'
	from /usr/lib/ruby/1.8/resolv.rb:1270:in `encode'
	from /usr/lib/ruby/1.8/resolv.rb:1290:in `initialize'
	from /usr/lib/ruby/1.8/resolv.rb:1256:in `new'
	from /usr/lib/ruby/1.8/resolv.rb:1256:in `encode'
	from /usr/lib/ruby/1.8/resolv.rb:686:in `sender'
	from /usr/lib/ruby/1.8/resolv.rb:487:in `each_resource'
	from /usr/lib/ruby/1.8/resolv.rb:971:in `resolv'
	from /usr/lib/ruby/1.8/resolv.rb:969:in `each'
	from /usr/lib/ruby/1.8/resolv.rb:969:in `resolv'
	from /usr/lib/ruby/1.8/resolv.rb:968:in `each'
... 3200 levels...
	from /usr/lib/ruby/1.8/resolv.rb:971:in `resolv'
	from /usr/lib/ruby/1.8/resolv.rb:969:in `each'
	from /usr/lib/ruby/1.8/resolv.rb:969:in `resolv'
	from /usr/lib/ruby/1.8/resolv.rb:968:in `each'
	from /usr/lib/ruby/1.8/resolv.rb:968:in `resolv'
	from /usr/lib/ruby/1.8/resolv.rb:966:in `each'
	from /usr/lib/ruby/1.8/resolv.rb:966:in `resolv'
	from /usr/lib/ruby/1.8/resolv.rb:481:in `each_resource'
	from /usr/lib/ruby/1.8/resolv.rb:386:in `each_address'
	from /usr/lib/ruby/1.8/resolv.rb:115:in `each_address'
	from /usr/lib/ruby/1.8/resolv.rb:114:in `each'
	from /usr/lib/ruby/1.8/resolv.rb:114:in `each_address'
	from /usr/lib/ruby/1.8/resolv.rb:92:in `getaddress'
	from /usr/lib/ruby/1.8/resolv.rb:43:in `getaddress'

From the backtrace and the source code of resolv-replace.rb,  we would find that the Resolv tries to resolve the IP address, it would loop through all the nameservers defined in the /etc/resolv.conf. However, if the nameserver is not an IP address but a hostname as well. Then this hostname needs to be resolved first, so to resolve this hostname, it would search for the /etc/resolv.conf again and do this again and again until the SystemStackError occurs and the application is down.

See below how resolv-replace.rb is implemented. When contacting the DNS server, it would call Resol.getaddress again.

To resolve this issue, need to check the /etc/resolv.conf and ensure that the nameserver is an IP address instead of a hostname.

RUBY  RUBY ON RAILS  NETWORK 

       

  RELATED


  0 COMMENT


No comment for this article.



  RANDOM FUN

I am a feature but not a bug