Overview
The Domain Name System (DNS) operates as a distributed database that maps domain names to IP addresses and other resource records. DNS eliminates the need for users to memorize numeric IP addresses by providing a hierarchical naming structure that scales across the internet. Every web request, email delivery, and network service lookup depends on DNS resolution.
DNS originated in the early 1980s when the ARPANET's host table grew too large for manual maintenance. Paul Mockapetris designed DNS in 1983 as specified in RFC 882 and RFC 883, later superseded by RFC 1034 and RFC 1035. The protocol transformed a centralized host file system into a distributed architecture that handles billions of queries daily.
The DNS namespace forms a tree structure with the root at the top, followed by top-level domains (TLDs) like .com and .org, second-level domains like example.com, and subdomains like mail.example.com. This hierarchy distributes administrative responsibility across organizations while maintaining global consistency.
DNS queries follow a client-server model. A client (resolver) sends queries to DNS servers, which either provide answers from their cache or query other servers in the hierarchy. This recursive or iterative process continues until the resolver receives a definitive answer or determines the domain does not exist.
require 'resolv'
# Basic DNS query
ip = Resolv.getaddress("example.com")
# => "93.184.216.34"
# Query returns multiple addresses
addresses = Resolv.getaddresses("google.com")
# => ["142.250.80.46", "2607:f8b0:4004:c07::71"]
DNS defines multiple record types beyond address resolution. MX records direct email delivery, CNAME records create aliases, TXT records store arbitrary text data, and SRV records enable service discovery. Each record type serves specific protocol requirements within the broader internet infrastructure.
Key Principles
The DNS protocol operates on a hierarchical namespace with delegation authority flowing from the root servers through TLDs to authoritative nameservers. This delegation model distributes the administrative burden while maintaining a coherent global namespace. Each level delegates authority to the next level, creating zones of responsibility.
A DNS zone represents a contiguous portion of the DNS namespace under single administrative control. Zone files contain resource records that define the authoritative data for that zone. The Start of Authority (SOA) record marks the beginning of a zone and specifies administrative parameters including serial numbers for change tracking and timing parameters for synchronization.
Resource records form the basic data elements in DNS. Each record contains a name, type, class (almost always IN for Internet), TTL (time to live), and type-specific data. The TTL value determines how long resolvers and caches may store the record before requerying authoritative servers.
require 'resolv'
resolver = Resolv::DNS.new
resources = resolver.getresources("example.com", Resolv::DNS::Resource::IN::A)
resources.each do |resource|
puts "Address: #{resource.address}"
puts "TTL: #{resource.ttl} seconds"
end
DNS queries consist of a header, question section, and optional additional sections. The header contains flags indicating query type, recursion preferences, and response status. Query flags include QR (query/response), AA (authoritative answer), TC (truncated), RD (recursion desired), RA (recursion available), and RCODE (response code).
Resolution follows two primary modes: recursive and iterative. In recursive resolution, the client delegates full responsibility to a resolver, which performs all necessary queries and returns the final answer. In iterative resolution, each server returns either the answer or a referral to another server, requiring the client to follow the referral chain.
Caching improves DNS performance by storing query results for the duration specified by TTL values. Recursive resolvers maintain caches that serve repeated queries without contacting authoritative servers. Negative caching stores information about nonexistent domains to prevent repeated queries for invalid names.
require 'resolv'
# DNS resolver with custom nameservers
resolver = Resolv::DNS.new(nameserver: ['8.8.8.8', '8.8.4.4'])
# Query with custom configuration
begin
mx_records = resolver.getresources('example.com', Resolv::DNS::Resource::IN::MX)
mx_records.sort_by(&:preference).each do |mx|
puts "Priority #{mx.preference}: #{mx.exchange}"
end
rescue Resolv::ResolvError => e
puts "Resolution failed: #{e.message}"
end
The DNS message format uses binary encoding with 16-bit fields for efficiency over UDP transport. Messages contain a 12-byte header followed by variable-length question, answer, authority, and additional sections. Domain names use label compression to reduce message size by replacing repeated names with pointers to earlier occurrences.
DNS typically operates over UDP port 53 for queries under 512 bytes. Larger responses set the TC flag, prompting clients to retry over TCP. EDNS0 (Extension Mechanisms for DNS) extends the protocol to support larger UDP messages, DNSSEC validation, and additional options while maintaining backward compatibility.
Ruby Implementation
Ruby's standard library includes the Resolv module, which provides DNS resolution without external dependencies. Resolv implements a pure Ruby DNS client that handles queries, caching, and multiple resolver backends. The module supports both simple resolution methods and detailed control over query parameters.
The Resolv class acts as a facade over multiple resolver implementations. By default, it combines DNS resolution with hosts file lookup, checking /etc/hosts before querying DNS servers. This mirrors standard system behavior on Unix-like systems.
require 'resolv'
# Simple hostname resolution
address = Resolv.getaddress("www.ruby-lang.org")
puts address
# Reverse DNS lookup
hostname = Resolv.getname("151.101.1.178")
puts hostname
# Handle resolution failures
begin
ip = Resolv.getaddress("nonexistent.invalid")
rescue Resolv::ResolvError
puts "Domain does not exist"
end
The Resolv::DNS class provides direct DNS protocol access with configurable nameservers, timeouts, and search parameters. This class exposes the full DNS record type system through resource classes under Resolv::DNS::Resource::IN.
require 'resolv'
# Configure DNS resolver explicitly
dns = Resolv::DNS.new(
nameserver: ['1.1.1.1', '1.0.0.1'],
search: ['example.com', 'example.org'],
ndots: 1
)
# Query multiple record types
domain = 'example.com'
# A records (IPv4 addresses)
a_records = dns.getresources(domain, Resolv::DNS::Resource::IN::A)
a_records.each { |r| puts "A: #{r.address}" }
# AAAA records (IPv6 addresses)
aaaa_records = dns.getresources(domain, Resolv::DNS::Resource::IN::AAAA)
aaaa_records.each { |r| puts "AAAA: #{r.address}" }
# MX records (mail servers)
mx_records = dns.getresources(domain, Resolv::DNS::Resource::IN::MX)
mx_records.sort_by(&:preference).each do |mx|
puts "MX #{mx.preference}: #{mx.exchange}"
end
# TXT records (arbitrary text)
txt_records = dns.getresources(domain, Resolv::DNS::Resource::IN::TXT)
txt_records.each { |r| puts "TXT: #{r.data}" }
Ruby DNS gems extend functionality beyond the standard library. The dnsruby gem provides comprehensive DNS client and server capabilities including DNSSEC validation, dynamic updates, and zone transfers. It implements the complete DNS protocol specification with support for TSIG authentication and EDNS options.
# Using dnsruby gem for advanced features
require 'dnsruby'
resolver = Dnsruby::Resolver.new(
nameserver: ['8.8.8.8'],
packet_timeout: 5,
retry_times: 3
)
# Perform query with detailed response information
response = resolver.query('example.com', 'A')
response.answer.each do |rr|
puts "#{rr.name} #{rr.ttl} #{rr.type} #{rr.address}"
end
puts "Response code: #{response.rcode}"
puts "Authoritative: #{response.header.aa}"
puts "Query time: #{response.query_time}ms"
Async DNS operations prevent blocking during resolution in high-concurrency applications. The async-dns gem integrates with the async framework to provide non-blocking DNS resolution suitable for event-driven architectures.
# Asynchronous DNS resolution
require 'async'
require 'async/dns'
Async do
resolver = Async::DNS::Resolver.new([[:udp, "8.8.8.8", 53]])
# Resolve multiple domains concurrently
domains = ['example.com', 'example.org', 'example.net']
tasks = domains.map do |domain|
Async do
response = resolver.query(domain, :A)
answer = response.answer.first
[domain, answer&.address]
end
end
results = tasks.map(&:wait)
results.each do |domain, address|
puts "#{domain} => #{address}"
end
end
Ruby applications often require custom DNS configurations for testing or special network environments. The Resolv::DNS class accepts configuration hashes specifying nameservers, search domains, and resolution behavior.
require 'resolv'
# Custom resolver for internal network
internal_dns = Resolv::DNS.new(
nameserver: ['10.0.0.1', '10.0.0.2'],
search: ['internal.example.com'],
ndots: 2,
port: 53,
timeouts: [2, 4]
)
# Fallback to public DNS if internal resolution fails
public_dns = Resolv::DNS.new(nameserver: ['8.8.8.8'])
def resolve_with_fallback(domain, internal, public)
begin
internal.getaddress(domain)
rescue Resolv::ResolvError
public.getaddress(domain)
end
end
ip = resolve_with_fallback('app.internal', internal_dns, public_dns)
Implementation Approaches
DNS implementation strategies vary based on resolver type, caching requirements, and scale demands. Stub resolvers perform basic query forwarding to recursive resolvers, while full recursive resolvers independently traverse the DNS hierarchy. Authoritative servers answer queries for zones under their control without performing recursion.
Stub resolvers send queries to configured recursive resolvers and rely on those servers to handle iterative queries through the DNS hierarchy. Most end-user devices and applications use stub resolvers because they minimize client complexity and benefit from shared caches at the recursive resolver level. Stub resolvers configure nameservers through system settings, DHCP, or static configuration.
Recursive resolvers accept queries from clients and perform complete resolution by iteratively querying authoritative servers. Starting at root nameservers, recursive resolvers follow referrals down the DNS hierarchy until reaching authoritative servers for the target domain. Recursive resolvers maintain extensive caches to minimize query load and reduce latency.
# Implementing a simple recursive query pattern
require 'resolv'
def recursive_query(domain, type, nameservers)
nameservers.each do |ns|
begin
resolver = Resolv::DNS.new(nameserver: [ns])
result = resolver.getresources(domain, type)
return result unless result.empty?
rescue Resolv::ResolvError
next
end
end
raise Resolv::ResolvError, "All nameservers failed"
end
# Query with fallback nameservers
nameservers = ['8.8.8.8', '1.1.1.1', '9.9.9.9']
records = recursive_query('example.com', Resolv::DNS::Resource::IN::A, nameservers)
Caching implementations balance memory usage against query reduction. Simple caches use hash tables with TTL-based expiration. Production caches implement least recently used (LRU) eviction, negative caching for NXDOMAIN responses, and separate caches for different record types. Cache poisoning attacks make security critical in cache design.
Authoritative server implementations serve zone data from memory or database backends. Primary servers load zone files containing authoritative records, while secondary servers replicate zones via AXFR (full transfer) or IXFR (incremental transfer) mechanisms. Hidden primary architectures place primary servers behind firewalls with public-facing secondaries handling queries.
Split-horizon DNS serves different answers based on query source. Internal clients receive private IP addresses while external clients receive public addresses. Implementation requires maintaining separate zone files or view-based configurations that select responses based on source network.
# Split-horizon simulation
require 'resolv'
class SplitHorizonResolver
def initialize(internal_zone, external_zone)
@internal = internal_zone
@external = external_zone
end
def resolve(domain, source_ip)
zone = internal_network?(source_ip) ? @internal : @external
zone[domain] || raise(Resolv::ResolvError, "Domain not found")
end
private
def internal_network?(ip)
IPAddr.new('10.0.0.0/8').include?(ip) ||
IPAddr.new('172.16.0.0/12').include?(ip) ||
IPAddr.new('192.168.0.0/16').include?(ip)
end
end
internal = {
'app.example.com' => '10.0.1.100',
'db.example.com' => '10.0.2.50'
}
external = {
'app.example.com' => '203.0.113.10',
'db.example.com' => nil # Not accessible externally
}
resolver = SplitHorizonResolver.new(internal, external)
puts resolver.resolve('app.example.com', '10.0.1.5') # => 10.0.1.100
puts resolver.resolve('app.example.com', '203.0.113.50') # => 203.0.113.10
Dynamic DNS (DDNS) updates zone records without manual file editing. RFC 2136 defines the UPDATE message format allowing authorized clients to add, delete, or modify records. DDNS implementations commonly use TSIG (Transaction Signature) for authentication. This approach supports dynamic environments where IP addresses change frequently.
Anycast DNS distributes identical nameservers across geographic locations sharing the same IP address. Network routing directs queries to the topologically nearest server, reducing latency and providing resilience. Anycast implementations require careful consideration of cache synchronization and update propagation to maintain consistency.
Security Implications
DNS vulnerabilities expose networks to hijacking, data exfiltration, and denial of service. The protocol's original design prioritized functionality over security, creating attack surfaces exploited by adversaries. Modern DNS security requires defense in depth combining protocol extensions, operational practices, and monitoring.
DNS spoofing attacks inject fraudulent responses into resolver caches, directing users to attacker-controlled servers. Cache poisoning exploits the stateless nature of DNS by racing forged responses against legitimate ones. Successful attacks redirect traffic for extended periods until cached records expire. Query ID randomization, source port randomization, and cryptographic validation defend against spoofing.
DNSSEC (DNS Security Extensions) adds cryptographic signatures to DNS records, enabling validation of response authenticity and integrity. DNSSEC creates a chain of trust from the root zone through TLDs to individual domains. Resolvers validate signatures using public keys distributed through DS (Delegation Signer) and DNSKEY records. DNSSEC prevents cache poisoning but does not provide confidentiality.
# DNSSEC validation with dnsruby
require 'dnsruby'
resolver = Dnsruby::Resolver.new
resolver.dnssec = true # Enable DNSSEC validation
begin
response = resolver.query('dnssec-example.com', 'A')
if response.security_level == Dnsruby::Message::SecurityLevel::SECURE
puts "DNSSEC validation successful"
puts "Response is authenticated"
else
puts "DNSSEC validation failed: #{response.security_error}"
end
rescue Dnsruby::ResolvError => e
puts "Query failed: #{e.message}"
end
DNS amplification attacks exploit open resolvers to magnify traffic directed at victims. Attackers send queries with spoofed source addresses, causing resolvers to send large responses to victims. A small query requesting all records for a domain generates responses 50-100 times larger. Mitigation includes disabling recursion for external clients and implementing rate limiting.
DNS tunneling encapsulates non-DNS protocols within DNS queries and responses, bypassing firewalls and exfiltrating data. Attackers register domains they control and encode data in subdomain labels or TXT record queries. Detection requires analyzing query patterns, payload sizes, and entropy of domain names.
# Detecting suspicious DNS query patterns
require 'resolv'
class DNSMonitor
def initialize
@query_counts = Hash.new(0)
@suspicious_threshold = 100
end
def record_query(domain, source_ip)
@query_counts[source_ip] += 1
flags = []
flags << :high_frequency if high_frequency_queries?(source_ip)
flags << :unusual_subdomain if unusual_subdomain?(domain)
flags << :txt_query_pattern if txt_query_pattern?(domain)
flags
end
private
def high_frequency_queries?(source_ip)
@query_counts[source_ip] > @suspicious_threshold
end
def unusual_subdomain?(domain)
# Check for long random-looking subdomains
labels = domain.split('.')
labels.any? { |label| label.length > 63 || high_entropy?(label) }
end
def high_entropy?(string)
# Simplified entropy calculation
frequencies = string.chars.group_by(&:itself).transform_values(&:count)
entropy = frequencies.values.sum { |f| -(f.to_f / string.length) * Math.log2(f.to_f / string.length) }
entropy > 3.5 # Threshold for random-looking strings
end
def txt_query_pattern?(domain)
# Detect patterns indicating data exfiltration
domain.start_with?(/[a-f0-9]{32,}/) # Long hex strings
end
end
DNS over HTTPS (DoH) and DNS over TLS (DoT) encrypt DNS traffic, preventing eavesdropping and manipulation. DoT wraps DNS in TLS on port 853. DoH embeds DNS messages in HTTPS requests on port 443, making DNS traffic indistinguishable from web traffic. Both protocols protect against passive surveillance and on-path attacks but require resolver support.
Registrar lock and transfer protection mechanisms prevent unauthorized domain transfers. Domain hijacking attacks target registrar accounts or exploit social engineering to transfer domains to attacker control. Two-factor authentication, registry lock services, and strict transfer authorization procedures reduce hijacking risk.
Response rate limiting (RRL) mitigates DNS amplification attacks by detecting and limiting responses to IP addresses generating suspiciously high query rates. RRL implementations maintain state about recent responses and apply exponential backoff or drop responses when thresholds exceed. This prevents amplification while minimally affecting legitimate clients.
Integration & Interoperability
DNS integration patterns appear throughout distributed systems for service discovery, load balancing, and configuration management. Applications resolve hostnames during connection establishment, while service meshes use DNS for dynamic endpoint discovery. Cloud platforms leverage DNS for traffic routing and multi-region failover.
Service discovery through DNS enables dynamic endpoint resolution without hardcoding addresses. SRV records specify protocol, transport, service name, and port alongside hostname and priority information. Applications query SRV records to discover available service instances and select targets based on priority and weight.
require 'resolv'
# Service discovery using SRV records
def discover_service(service_name, protocol, domain)
query = "_#{service_name}._#{protocol}.#{domain}"
resolver = Resolv::DNS.new
srv_records = resolver.getresources(query, Resolv::DNS::Resource::IN::SRV)
# Sort by priority (lower is better), then randomize by weight
services = srv_records.sort_by(&:priority).group_by(&:priority)
services.flat_map do |priority, records|
weighted_random_selection(records)
end
end
def weighted_random_selection(records)
total_weight = records.sum(&:weight)
return records if total_weight == 0
records.sort_by { |r| rand * (total_weight / r.weight.to_f) }
end
# Discover HTTP service instances
http_endpoints = discover_service('http', 'tcp', 'example.com')
http_endpoints.each do |srv|
puts "#{srv.target}:#{srv.port} (priority: #{srv.priority}, weight: #{srv.weight})"
end
DNS-based load balancing distributes traffic across multiple servers by rotating A records in responses. Round-robin DNS returns different address orderings for each query, causing clients to connect to different backends. GeoDNS enhances this by returning addresses for servers geographically closest to the client based on resolver location.
Container orchestration platforms integrate DNS for service naming. Kubernetes creates DNS records for services and pods, enabling containers to resolve other services by name. Service DNS names follow patterns like service-name.namespace.svc.cluster.local, with the DNS server automatically updated as services scale or relocate.
# Kubernetes service discovery pattern
require 'resolv'
class KubernetesServiceResolver
def initialize(namespace, cluster_domain = 'cluster.local')
@namespace = namespace
@cluster_domain = cluster_domain
@resolver = Resolv::DNS.new
end
def resolve_service(service_name)
fqdn = "#{service_name}.#{@namespace}.svc.#{@cluster_domain}"
addresses = @resolver.getaddresses(fqdn)
# Try headless service (returns pod IPs)
if addresses.empty?
addresses = resolve_headless_service(service_name)
end
addresses
end
def resolve_headless_service(service_name)
# Headless services return pod IPs directly
fqdn = "#{service_name}.#{@namespace}.svc.#{@cluster_domain}"
@resolver.getaddresses(fqdn)
end
end
resolver = KubernetesServiceResolver.new('production')
backend_ips = resolver.resolve_service('api-backend')
backend_ips.each { |ip| puts "Backend: #{ip}" }
Multi-cloud and hybrid cloud architectures use DNS for traffic management across environments. Global server load balancing (GSLB) systems control DNS responses based on health checks, capacity, and routing policies. DNS failover automatically removes failed endpoints from rotation, directing traffic to healthy alternatives.
DNS interoperates with content delivery networks (CDNs) through CNAME records pointing to CDN-managed domains. CDN DNS returns edge server addresses optimized for client location and network conditions. This indirection enables CDNs to manage traffic routing without requiring origin servers to update configurations.
API gateways and service meshes register backend services in DNS for client discovery. Consul and etcd provide DNS interfaces for service catalog queries, returning addresses for healthy service instances. This integration enables zero-configuration service discovery where clients use standard DNS resolution.
Common Pitfalls
TTL misinterpretation causes caching issues when administrators expect immediate propagation of DNS changes. Low TTL values reduce caching benefits and increase query load on authoritative servers. High TTL values improve performance but delay change propagation. Setting TTL to 300 seconds (5 minutes) balances these concerns for most use cases. Planning DNS changes requires reducing TTL in advance, making changes, then restoring original TTL after propagation.
DNS resolution failures occur when applications do not handle errors correctly. Network partitions, server failures, and timeouts require robust error handling with retry logic and fallback mechanisms. Applications must distinguish between temporary failures requiring retry and permanent failures like NXDOMAIN.
require 'resolv'
# Robust DNS resolution with retries
def resolve_with_retry(domain, max_attempts: 3, timeout: 5)
attempts = 0
loop do
attempts += 1
begin
resolver = Resolv::DNS.new(timeouts: [timeout])
return resolver.getaddress(domain)
rescue Resolv::ResolvTimeout => e
raise e if attempts >= max_attempts
sleep(2 ** attempts) # Exponential backoff
rescue Resolv::ResolvError => e
# NXDOMAIN or other permanent failure
raise e
end
end
end
begin
ip = resolve_with_retry('flaky.example.com')
puts "Resolved: #{ip}"
rescue Resolv::ResolvTimeout
puts "Resolution timed out after retries"
rescue Resolv::ResolvError => e
puts "Permanent failure: #{e.message}"
end
Circular CNAME records create resolution loops that prevent successful queries. A CNAME pointing to another CNAME that points back to the original creates infinite recursion. DNS servers detect loops by limiting CNAME chain depth, typically to 16 hops. Proper zone configuration prevents chains exceeding 2-3 levels.
Negative caching stores NXDOMAIN responses, preventing queries for nonexistent domains from repeatedly reaching authoritative servers. The SOA record minimum TTL value (now interpreted as negative cache TTL per RFC 2308) controls how long resolvers cache negative responses. Setting this value too high prevents newly created domains from resolving until negative cache entries expire.
Search domain expansion creates unexpected resolution behavior. When querying "server" with search domains "dev.example.com" and "example.com", resolvers try "server.dev.example.com" then "server.example.com". This causes queries to succeed with unintended results when names exist in multiple search domains. Using fully qualified domain names with trailing dots prevents search domain expansion.
require 'resolv'
# Demonstrate search domain effects
resolver = Resolv::DNS.new(search: ['dev.example.com', 'example.com'])
# Query without trailing dot - search domains applied
begin
ip1 = resolver.getaddress('server')
puts "Relative query: #{ip1}"
rescue Resolv::ResolvError
puts "Relative query failed"
end
# Query with trailing dot - fully qualified, no search
begin
ip2 = resolver.getaddress('server.example.com.')
puts "FQDN query: #{ip2}"
rescue Resolv::ResolvError
puts "FQDN query failed"
end
Resolver configuration precedence confuses administrators when multiple configuration sources exist. System resolvers check /etc/hosts before DNS, causing hosts file entries to override DNS records. Application-level resolver configurations override system settings. Cloud environments inject DNS configurations through DHCP or metadata services that supersede static configurations.
PTR record mismatches cause reverse DNS validation failures in email systems and security applications. Forward DNS resolving example.com to 192.0.2.1 requires corresponding PTR record mapping 1.2.0.192.in-addr.arpa to example.com. Asymmetric forward and reverse records trigger warnings in mail transfer agents and access control systems.
Wildcard record (* labels) behave differently than administrators expect. Wildcard records match any label at their position but do not match empty labels or multiple labels. A wildcard *.example.com matches foo.example.com but not example.com or foo.bar.example.com. Explicit records override wildcards at the same level.
DNS over TCP fallback failures occur when firewalls block TCP port 53. Large responses (over 512 bytes without EDNS0) require TCP, causing queries to fail when TCP is blocked. DNSSEC responses frequently exceed UDP limits. Firewall rules must permit TCP DNS traffic alongside UDP.
Reference
Common Record Types
| Type | Name | Purpose | Format Example |
|---|---|---|---|
| A | Address | Maps hostname to IPv4 address | example.com. 300 IN A 192.0.2.1 |
| AAAA | IPv6 Address | Maps hostname to IPv6 address | example.com. 300 IN AAAA 2001:db8::1 |
| CNAME | Canonical Name | Creates alias to another name | www.example.com. 300 IN CNAME example.com. |
| MX | Mail Exchange | Specifies mail server | example.com. 300 IN MX 10 mail.example.com. |
| TXT | Text | Stores arbitrary text data | example.com. 300 IN TXT "v=spf1 -all" |
| NS | Name Server | Delegates zone to nameserver | example.com. 300 IN NS ns1.example.com. |
| SOA | Start of Authority | Defines zone parameters | example.com. 300 IN SOA ns1.example.com. admin.example.com. 1 3600 1800 604800 300 |
| PTR | Pointer | Reverse DNS mapping | 1.2.0.192.in-addr.arpa. 300 IN PTR example.com. |
| SRV | Service | Service location record | _http._tcp.example.com. 300 IN SRV 10 60 80 server.example.com. |
| CAA | Certificate Authority | Specifies authorized CAs | example.com. 300 IN CAA 0 issue "letsencrypt.org" |
Response Codes
| Code | Name | Meaning | Common Cause |
|---|---|---|---|
| 0 | NOERROR | Successful query | Normal resolution |
| 1 | FORMERR | Format error | Malformed query |
| 2 | SERVFAIL | Server failure | Server error or timeout |
| 3 | NXDOMAIN | Name does not exist | Domain not registered |
| 4 | NOTIMP | Not implemented | Unsupported query type |
| 5 | REFUSED | Query refused | Policy restriction |
Query Flags
| Flag | Name | Meaning | Set By |
|---|---|---|---|
| QR | Query/Response | 0 = query, 1 = response | DNS protocol |
| AA | Authoritative Answer | Response from authoritative server | Authoritative servers |
| TC | Truncated | Message truncated, retry via TCP | Server when response exceeds UDP size |
| RD | Recursion Desired | Request recursive resolution | Stub resolvers |
| RA | Recursion Available | Server supports recursion | Recursive servers |
| AD | Authenticated Data | DNSSEC validation successful | DNSSEC validators |
| CD | Checking Disabled | Disable DNSSEC validation | Debugging tools |
Ruby Resolv API Methods
| Method | Purpose | Returns |
|---|---|---|
| Resolv.getaddress(name) | Resolve hostname to IP | String (first address) |
| Resolv.getaddresses(name) | Resolve hostname to all IPs | Array of Strings |
| Resolv.getname(address) | Reverse DNS lookup | String (hostname) |
| Resolv.getnames(address) | All reverse DNS entries | Array of Strings |
| Resolv::DNS#getresource(name, type) | Query specific record type | Resource object |
| Resolv::DNS#getresources(name, type) | Query all records of type | Array of Resource objects |
| Resolv::DNS#each_resource(name, type) | Iterate over records | Yields Resource objects |
Resource Record Classes
| Class | Record Type | Attributes |
|---|---|---|
| Resolv::DNS::Resource::IN::A | IPv4 address | address (String) |
| Resolv::DNS::Resource::IN::AAAA | IPv6 address | address (String) |
| Resolv::DNS::Resource::IN::CNAME | Canonical name | name (Name) |
| Resolv::DNS::Resource::IN::MX | Mail exchange | preference (Integer), exchange (Name) |
| Resolv::DNS::Resource::IN::NS | Name server | name (Name) |
| Resolv::DNS::Resource::IN::PTR | Pointer | name (Name) |
| Resolv::DNS::Resource::IN::SOA | Start of authority | mname, rname, serial, refresh, retry, expire, minimum |
| Resolv::DNS::Resource::IN::SRV | Service | priority, weight, port, target |
| Resolv::DNS::Resource::IN::TXT | Text | data (String) |
Resolver Configuration Parameters
| Parameter | Type | Purpose | Example Value |
|---|---|---|---|
| nameserver | Array or String | DNS server addresses | ['8.8.8.8', '8.8.4.4'] |
| search | Array | Search domains | ['example.com', 'example.org'] |
| ndots | Integer | Minimum dots for absolute query | 1 |
| timeouts | Array | Query timeout values in seconds | [2, 4, 8] |
| port | Integer | DNS server port | 53 |
Timing Parameters
| Parameter | Typical Value | Purpose | Adjustment Guidance |
|---|---|---|---|
| TTL | 300-86400 seconds | Cache lifetime | Lower for frequently changing records, higher for stable records |
| SOA Refresh | 3600 seconds | Secondary zone check interval | Match zone update frequency |
| SOA Retry | 1800 seconds | Retry interval after failed refresh | Typically half of refresh value |
| SOA Expire | 604800 seconds | Secondary expiration time | 1-2 weeks prevents stale data |
| Negative TTL | 300-3600 seconds | NXDOMAIN cache time | Balance between query load and new domain availability |
Well-Known Ports
| Port | Protocol | Purpose |
|---|---|---|
| 53 | UDP/TCP | Standard DNS queries |
| 853 | TCP | DNS over TLS (DoT) |
| 443 | TCP | DNS over HTTPS (DoH) |
| 5353 | UDP | Multicast DNS (mDNS) |