Performing DNS Queries

Problem

You want to find the IP address corresponding to a domain name, or see whether a domain provides a certain service (such as an email server).

Solution

Use the Resolv:: DNS class in the standard resolv library to perform DNS lookups. The most commonly used method is DNS#each_address, which iterates over the IP addresses assigned to a domain name.

require esolv Resolv::DNS.new.each_address("oreilly.com") { |addr| puts addr } # 208.201.239.36 # 208.201.239.37

Discussion

If you need to check the existence of a particular type of DNS record (such as a MX record for a mail server), use DNS#getresources or the iterator DNS#each_resource. Both methods take a domain name and a class denoting a type of DNS record. They perform a DNS lookup and, for each matching DNS record, return an instance of the given class.

These are the three most common classes:

DNS::Resource::IN::A

Indicates a DNS record pointing to an IP address for the domain.

DNS::RESOURCE::IN::NS

Indicates a DNS record pointing to a DNS nameserver.

DNS::Resource::IN::MX

Indicates a DNS record pointing to a mail server.

This code finds the mail servers and name servers responsible for oreilly.com:

dns = Resolv::DNS.new domain = "oreilly.com" dns.each_resource(domain, Resolv::DNS::Resource::IN::MX) do |mail_server| puts mail_server.exchange end # smtp1.oreilly.com # smtp2.oreilly.com dns.each_resource(domain, Resolv::DNS::Resource::IN::NS) do |nameserver| puts nameserver.name end # a.auth-ns.sonic.net # b.auth-ns.sonic.net # c.auth-ns.sonic.net # ns.oreilly.com

If your application needs to do a lot of DNS lookups, you can greatly speed things up by creating a separate thread for each lookup. Most of the time spent doing a DNS lookup is spent connecting to the network, so doing all the lookups in parallel can save a lot of time. If you do this, you should include the resolv-replace library along with resolv, to make sure your DNS lookups are thread-safe.

Heres some code that sees which one-letter .com domains (a.com, b.com, etc.) are mapped to IP addresses. It runs all 26 DNS queries at once, in 26 threads, and summarizes the results.

require esolv-replace def multiple_lookup(*names) dns = Resolv::DNS.new results = {} threads = [] names.each do |name| threads << Thread.new(name) do |name| begin dns.each_address(name) { |a| (results[name] ||= []) << a } rescue Resolv::ResolvError results[name] = nil end end end threads.each { |t| t.join } return results end domains = ("a".."z").collect { |l| l + .com } multiple_lookup(*domains).each do |name, addresses| if addresses puts "#{name}: #{addresses.size} address#{addresses.size == 1 ? "" : "es"}" end end # x.com: 4 addresses # z.com: 1 address # q.com: 1 address

See Also

Категории