Saturday, October 24, 2009

Recipe 14.4. Performing DNS Queries










Recipe 14.4. 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 'resolv'

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.


Here's 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 'resolv-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


  • Chapter 20 uses a DNS lookup of an MX record to check whether the domain of an email address is valid

  • A DNS lookup is the classic example of a high-latency operation; much of Chapter 20 deals with ways of making high-latency operations run more quickly: see especially Recipe 20.3, "Doing Two Things at Once with Threads," and Recipe 20.6, "Running a Code Block on Many Objects Simultaneously"













No comments:

Post a Comment