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
- 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"
Категории