# frozen_string_literal: true
# Resolv is a thread-aware DNS resolver library written in Ruby. Resolv can
# handle multiple DNS requests concurrently without blocking the entire Ruby
# See also resolv-replace.rb to replace the libc resolver with Resolv.
# Resolv can look up various DNS resources using the DNS module directly.
# p Resolv.getaddress "www.ruby-lang.org"
# p Resolv.getname "210.251.121.214"
# Resolv::DNS.open do |dns|
# ress = dns.getresources "www.ruby-lang.org", Resolv::DNS::Resource::IN::A
# ress = dns.getresources "ruby-lang.org", Resolv::DNS::Resource::IN::MX
# p ress.map { |r| [r.exchange.to_s, r.preference] }
# * NIS is not supported.
# * /etc/nsswitch.conf is not supported.
# Looks up the first IP address for +name+.
def self.getaddress(name)
DefaultResolver.getaddress(name)
# Looks up all IP address for +name+.
def self.getaddresses(name)
DefaultResolver.getaddresses(name)
# Iterates over all IP addresses for +name+.
def self.each_address(name, &block)
DefaultResolver.each_address(name, &block)
# Looks up the hostname of +address+.
def self.getname(address)
DefaultResolver.getname(address)
# Looks up all hostnames for +address+.
def self.getnames(address)
DefaultResolver.getnames(address)
# Iterates over all hostnames for +address+.
def self.each_name(address, &proc)
DefaultResolver.each_name(address, &proc)
# Creates a new Resolv using +resolvers+.
def initialize(resolvers=[Hosts.new, DNS.new])
# Looks up the first IP address for +name+.
each_address(name) {|address| return address}
raise ResolvError.new("no address for #{name}")
# Looks up all IP address for +name+.
each_address(name) {|address| ret << address}
# Iterates over all IP addresses for +name+.
r.each_address(name) {|address|
# Looks up the hostname of +address+.
each_name(address) {|name| return name}
raise ResolvError.new("no name for #{address}")
# Looks up all hostnames for +address+.
each_name(address) {|name| ret << name}
# Iterates over all hostnames for +address+.
r.each_name(address) {|name|
# Indicates a failure to resolve a name or address.
class ResolvError < StandardError; end
# Indicates a timeout resolving a name or address.
class ResolvTimeout < Timeout::Error; end
# Resolv::Hosts is a hostname resolver that uses the system hosts file.
raise LoadError unless /mswin|mingw|cygwin/ =~ RUBY_PLATFORM
DefaultFileName = Win32::Resolv.get_hosts_path
DefaultFileName = '/etc/hosts'
# Creates a new Resolv::Hosts, using +filename+ for its data source.
def initialize(filename = DefaultFileName)
@mutex = Thread::Mutex.new
def lazy_initialize # :nodoc:
File.open(@filename, 'rb') {|f|
addr, hostname, *aliases = line.split(/\s+/)
@addr2name[addr] = [] unless @addr2name.include? addr
@addr2name[addr] << hostname
@addr2name[addr] += aliases
@name2addr[hostname] = [] unless @name2addr.include? hostname
@name2addr[hostname] << addr
@name2addr[n] = [] unless @name2addr.include? n
@name2addr.each {|name, arr| arr.reverse!}
# Gets the IP address of +name+ from the hosts file.
each_address(name) {|address| return address}
raise ResolvError.new("#{@filename} has no name: #{name}")
# Gets all IP addresses for +name+ from the hosts file.
each_address(name) {|address| ret << address}
# Iterates over all IP addresses for +name+ retrieved from the hosts file.
def each_address(name, &proc)
if @name2addr.include?(name)
@name2addr[name].each(&proc)
# Gets the hostname of +address+ from the hosts file.
each_name(address) {|name| return name}
raise ResolvError.new("#{@filename} has no address: #{address}")
# Gets all hostnames for +address+ from the hosts file.
each_name(address) {|name| ret << name}
# Iterates over all hostnames for +address+ retrieved from the hosts file.
def each_name(address, &proc)
@addr2name[address]&.each(&proc)
# Resolv::DNS is a DNS stub resolver.
# Information taken from the following places:
# * ftp://ftp.isi.edu/in-notes/iana/assignments/dns-parameters
# Default DNS UDP packet size
# Creates a new DNS resolver. See Resolv::DNS.new for argument details.
# Yields the created DNS resolver to the block, if given, otherwise
return dns unless block_given?
# Creates a new DNS resolver.
# nil:: Uses /etc/resolv.conf.
# String:: Path to a file using /etc/resolv.conf's format.
# Hash:: Must contain :nameserver, :search and :ndots keys.
# :nameserver_port can be used to specify port number of nameserver address.
# The value of :nameserver should be an address string or
# an array of address strings.
# - :nameserver => '8.8.8.8'
# - :nameserver => ['8.8.8.8', '8.8.4.4']
# The value of :nameserver_port should be an array of
# pair of nameserver address and port number.
# - :nameserver_port => [['8.8.8.8', 53], ['8.8.4.4', 53]]
# Resolv::DNS.new(:nameserver => ['210.251.121.21'],
# :search => ['ruby-lang.org'],
def initialize(config_info=nil)
@mutex = Thread::Mutex.new
@config = Config.new(config_info)
# Sets the resolver timeouts. This may be a single positive number
# or an array of positive numbers representing timeouts in seconds.
# If an array is specified, a DNS request will retry and wait for
# each successive interval in the array until a successful response
# is received. Specifying +nil+ reverts to the default timeouts:
# [ 5, second = 5 * 2 / nameserver_count, 2 * second, 4 * second ]
@config.timeouts = values
def lazy_initialize # :nodoc:
# Closes the DNS resolver.
# Gets the IP address of +name+ from the DNS resolver.
# +name+ can be a Resolv::DNS::Name or a String. Retrieved address will
# be a Resolv::IPv4 or Resolv::IPv6
each_address(name) {|address| return address}
raise ResolvError.new("DNS result has no information for #{name}")
# Gets all IP addresses for +name+ from the DNS resolver.
# +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will
# be a Resolv::IPv4 or Resolv::IPv6
each_address(name) {|address| ret << address}
# Iterates over all IP addresses for +name+ retrieved from the DNS
# +name+ can be a Resolv::DNS::Name or a String. Retrieved addresses will
# be a Resolv::IPv4 or Resolv::IPv6
each_resource(name, Resource::IN::A) {|resource| yield resource.address}
each_resource(name, Resource::IN::AAAA) {|resource| yield resource.address}
list = Socket.ip_address_list
rescue NotImplementedError
list.any? {|a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
# Gets the hostname for +address+ from the DNS resolver.
# +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
# name will be a Resolv::DNS::Name.
each_name(address) {|name| return name}
raise ResolvError.new("DNS result has no information for #{address}")
# Gets all hostnames for +address+ from the DNS resolver.
# +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
# names will be Resolv::DNS::Name instances.
each_name(address) {|name| ret << name}
# Iterates over all hostnames for +address+ retrieved from the DNS
# +address+ must be a Resolv::IPv4, Resolv::IPv6 or a String. Retrieved
# names will be Resolv::DNS::Name instances.
ptr = IPv4.create(address).to_name
ptr = IPv6.create(address).to_name
raise ResolvError.new("cannot interpret as address: #{address}")
each_resource(ptr, Resource::IN::PTR) {|resource| yield resource.name}
# Look up the +typeclass+ DNS resource of +name+.
# +name+ must be a Resolv::DNS::Name or a String.
# +typeclass+ should be one of the following:
# * Resolv::DNS::Resource::IN::A
# * Resolv::DNS::Resource::IN::AAAA
# * Resolv::DNS::Resource::IN::ANY
# * Resolv::DNS::Resource::IN::CNAME
# * Resolv::DNS::Resource::IN::HINFO
# * Resolv::DNS::Resource::IN::MINFO
# * Resolv::DNS::Resource::IN::MX
# * Resolv::DNS::Resource::IN::NS
# * Resolv::DNS::Resource::IN::PTR
# * Resolv::DNS::Resource::IN::SOA
# * Resolv::DNS::Resource::IN::TXT
# * Resolv::DNS::Resource::IN::WKS
# Returned resource is represented as a Resolv::DNS::Resource instance,
# i.e. Resolv::DNS::Resource::IN::A.
def getresource(name, typeclass)
each_resource(name, typeclass) {|resource| return resource}
raise ResolvError.new("DNS result has no information for #{name}")
# Looks up all +typeclass+ DNS resources for +name+. See #getresource for
def getresources(name, typeclass)
each_resource(name, typeclass) {|resource| ret << resource}