# Resolv is a thread-aware DNS resolver library written in Ruby. Resolv can
# handle multiple DNS requests concurrently without blocking. The 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
# p ress.map { |r| r.address }
# 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 < TimeoutError; end
# DNS::Hosts is a hostname resolver that uses the system hosts file.
if /mswin32|mingw|bccwin/ =~ RUBY_PLATFORM
DefaultFileName = Win32::Resolv.get_hosts_path
DefaultFileName = '/etc/hosts'
# Creates a new DNS::Hosts, using +filename+ for its data source.
def initialize(filename = DefaultFileName)
def lazy_initialize # :nodoc:
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)
if @addr2name.include?(address)
@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
# Group of DNS resolver threads (obsolete)
DNSThreadGroup = ThreadGroup.new
# 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.
# Resolv::DNS.new(:nameserver => ['210.251.121.21'],
# :search => ['ruby-lang.org'],
def initialize(config_info=nil)
@config = Config.new(config_info)
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}
# 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}
# Iterates over all +typeclass+ DNS resources for +name+. See
# #getresource for argument details.
def each_resource(name, typeclass, &proc)
requester = make_requester
@config.resolv(name) {|candidate, tout, nameserver|
msg.add_question(candidate, typeclass)
unless sender = senders[[candidate, nameserver]]
sender = senders[[candidate, nameserver]] =
requester.sender(msg, candidate, nameserver)
reply, reply_name = requester.request(sender, tout)
extract_resources(reply, reply_name, typeclass, &proc)
raise Config::NXDomain.new(reply_name.to_s)
raise Config::OtherResolvError.new(reply_name.to_s)