Ruby Resolv Library: Building a Quick IP Hostname Scanner
Building a custom IP hostname scanner in Ruby using the built-in Resolv library is a reliable way to perform bulk reverse DNS lookups without relying on external gems. By leveraging Ruby's standard library, you can query pointer (PTR) records efficiently to map IP addresses back to their fully qualified domain names. This approach is ideal for network auditing, security log analysis, and system administration tasks where speed and portability matter.
Before you start scanning large subnets, you can test individual pointer records using the Reverse DNS Lookup tool on XiaTools to instantly check what nameserver responses look like for your target addresses.
Understanding Ruby Resolv and PTR Records
Reverse DNS lookup translates an IP address into a human-readable hostname by querying PTR records stored in the Domain Name System (DNS). While standard lookups (A or AAAA records) map domains to IPs, reverse lookups require querying an inverted IP address structure appended with in-addr.arpa for IPv4 or ip6.arpa for IPv6.
The Ruby standard library includes the resolv library, which provides a pure Ruby DNS resolver capable of handling these queries asynchronously or synchronously. Unlike system-dependent tools, Resolv works identically across operating systems, making your scanning scripts fully cross-platform.
Core Classes in Resolv
Resolv::DNS: The primary interface for making DNS queries.Resolv::IPv4andResolv::IPv6: Utility classes for validating and manipulating IP addresses.Resolv::DNS::Resource::IN::PTR: The specific DNS resource class representing pointer records.
Setting Up Your Environment
No external gems are required to build a basic hostname scanner in Ruby. You only need a standard Ruby installation (version 2.5 or newer recommended) and a terminal.
Create a new file named scanner.rb and require the necessary modules:
require 'resolv'
require 'ipaddr'
The ipaddr library helps generate ranges of IP addresses easily, while resolv performs the actual network queries.
Building the Ruby Resolv Getname Script
Let's write a practical script that takes an IP range, iterates through each address, and attempts to resolve its hostname using Resolv.getname.
Basic Synchronous Scanner
Here is a complete, straightforward script that scans an IPv4 subnet using documentation addresses (192.0.2.0/24) as an example:
require 'resolv'
require 'ipaddr'
# Define the target subnet using documentation IP range
subnet = '192.0.2.0/28'
puts "Scanning subnet: #{subnet}\n"
IPAddr.new(subnet).to_range.each do |ip|
ip_str = ip.to_s
begin
# Use Resolv.getname to fetch the hostname
hostname = Resolv.getname(ip_str)
puts "[+] #{ip_str} -> #{hostname}"
rescue Resolv::ResolvError
# Handle cases where no PTR record exists
puts "[-] #{ip_str} -> No hostname found"
rescue StandardError => e
puts "[!] #{ip_str} -> Error: #{e.message}
end
end
Run the script from your terminal using:
ruby scanner.rb
Sample Output
Scanning subnet: 192.0.2.0/28
[-] 192.0.2.0 -> No hostname found
[+] 192.0.2.1 -> router.example.com
[+] 192.0.2.2 -> ns1.example.com
[-] 192.0.2.3 -> No hostname found
Optimizing Performance with Concurrency
Synchronous DNS lookups can be exceptionally slow because your script waits for a timeout on unresponsive IP addresses before moving to the next one. To scan large networks efficiently, you must introduce concurrency using Ruby threads or fiber-based asynchronous libraries.
Here is an updated version of the script utilizing threads to query multiple IP addresses simultaneously:
require 'resolv'
require 'ipaddr'
subnet = '192.0.2.0/24'
threads = []
puts "Starting concurrent scan on #{subnet}...\n"
IPAddr.new(subnet).to_range.each do |ip|
threads << Thread.new(ip.to_s) do |ip_str|
begin
# Timeout configuration can be added via Resolv::DNS client setup
hostname = Resolv.getname(ip_str)
puts "[+] #{ip_str} => #{hostname}"
rescue Resolv::ResolvError
# Quietly ignore unresolvable IPs in bulk scans
end
end
end
# Wait for all threads to complete
threads.each(&:join)
puts "\nScan complete."
Advanced Customization: Setting DNS Timeouts
By default, Resolv.getname uses system resolvers and default timeouts. If you are scanning unresponsive IP blocks, your script can hang for several seconds per IP. You can override this behavior by instantiating a custom Resolv::DNS client with explicit timeout parameters.
require 'resolv'
# Configure a custom resolver with specific nameservers and timeouts
resolver = Resolv::DNS.new(
nameserver: ['8.8.8.8', '1.1.1.1'],
search: ['example.com'],
ndots: 1
)
ip = '192.0.2.1'
begin
# Using the resource query directly allows finer control
ptr = Resolv::DNS::Resource::IN::PTR.create(ip)
# Alternatively, use getname on the custom resolver instance
hostname = resolver.getname(ip)
puts "Hostname for #{ip}: #{hostname}"
rescue Resolv::ResolvError
puts "Resolution failed for #{ip}"
end
Comparison: Standard Resolv vs. External DNS Gems
| Feature | Ruby Standard Resolv |
External C-Based Gems | Pure Ruby Asynchronous Gems |
|---|---|---|---|
| Dependencies | None (Built-in) | Requires compiler / C extensions | Requires gem installation |
| Portability | Excellent (Works everywhere) | Can fail on restricted environments | Good, depends on event loop |
| Speed | Moderate (Threaded) | Very Fast | Extremely Fast |
| Complexity | Low | Medium | High |
Common Mistakes and How to Fix Them
- Not handling exceptions: Failing to catch
Resolv::ResolvErrorwill crash your script the moment an IP address lacks a PTR record. Always wrap lookup calls inbegin...rescueblocks. - Ignoring network timeouts: Synchronous lookups without explicit timeouts will stall your application indefinitely if a target nameserver drops packets. Configure custom timeouts when scanning public or untrusted ranges.
- Hitting rate limits: Querying thousands of addresses too quickly can trigger rate-limiting or IP bans on public resolvers like Google or Cloudflare. Use local caching nameservers or throttle your thread pool.
- Incorrect IP range parsing: Passing strings directly to iteration loops without
IPAddroften leads to malformed octet sequences. Always parse subnets usingIPAddr.new(subnet).to_range.
Quick Checklist for Your Scanner
- Require both
resolvandipaddrlibraries. - Wrap all
Resolv.getnamecalls in exception handlers. - Implement multi-threading or asynchronous processing for subnets larger than
/28. - Set custom DNS server addresses and timeouts if default system resolvers are slow.
- Verify pointer records manually using online tools before running large automation scripts.
Building a ruby resolv getname script gives you complete control over your network discovery tasks without external package dependencies. Whether you are auditing a private VLAN or analyzing server logs, combining Resolv with IPAddr provides a robust, lightweight solution for automated reverse DNS mapping.