When dealing with multiple DNS responses, the selection process occurs at two distinct levels:
- DNS server selection during recursive resolution
- IP address selection from the final response
The core specifications are covered in several RFCs:
- RFC 1034: Defines DNS concepts and suggests round-robin for load balancing
- RFC 1794: Discusses DNS round-robin scheduling
- RFC 3484 (obsoleted by RFC 6724): Address selection algorithms
- RFC 6724: Current standard for destination address selection
Most implementations follow these selection patterns:
// Pseudocode for common selection logic
function selectIP(addresses) {
// 1. Filter by reachability (if known)
let candidates = addresses.filter(addr => isReachable(addr));
// 2. Apply RFC 6724 sorting if implemented
if (supportsRFC6724) {
return sortByRFC6724(candidates)[0];
}
// 3. Fallback to simple round-robin or random selection
return candidates[lastIndex++ % candidates.length];
}
Linux glibc implementation:
/* From glibc's getaddrinfo.c */
static int
rfc3484_sort (const struct addrinfo *results)
{
// Implementation of RFC 6724 rules
// Including:
// - Prefer same address family
// - Avoid deprecated addresses
// - Prefer smaller scope
// - Use longest matching prefix
}
Windows DNS Client behavior:
- Implements RFC 6724 with additional heuristics
- Caches reachability information
- Includes subnet prioritization for local networks
When building applications that handle DNS:
// Python example with explicit selection control
import socket
def get_ordered_ips(hostname):
try:
info = socket.getaddrinfo(hostname, None)
# Apply custom sorting here
return sorted([item[4][0] for item in info],
key=lambda x: (x.count('.'), x))
except socket.gaierror:
return []
Common issues and verification techniques:
- Use
dig +short example.com
to see raw DNS responses - Check
/etc/gai.conf
on Linux for RFC 6724 overrides - Windows:
netsh interface ipv4 show addresses
reveals priority settings
For systems requiring custom behavior:
# Linux /etc/gai.conf example
# Prefer IPv4 over IPv6
precedence ::ffff:0:0/96 100
# macOS equivalent (requires root)
sudo sysctl -w net.inet6.ip6.precedence=0
When dealing with DNS resolution, professionals often encounter scenarios where:
- DNS servers receive multiple nameserver options during recursive resolution
- Clients obtain multiple IP addresses for a single hostname
RFC 1034 and RFC 1035 establish the foundation, while later RFCs like 2181 provide clarifications:
// Pseudo-code for nameserver selection
function selectNameserver(availableServers) {
// 1. Prefer servers with lowest RTT (measured from previous queries)
// 2. If equal, use server with successful query history
// 3. Fall back to round-robin if no metrics available
return sortedServers[0];
}
Modern implementations typically follow these steps:
- RFC 6724 (Default Address Selection) defines the current standard
- Most clients implement a variation of this algorithm
Here's how a resolver might implement RFC-compliant selection in Python:
import socket
import random
def sort_addresses(hostname):
try:
# Get all addresses
addrinfo = socket.getaddrinfo(hostname, None)
ips = [info[4][0] for info in addrinfo]
# Basic RFC 6724-inspired sorting
ipv6_addrs = [ip for ip in ips if ':' in ip]
ipv4_addrs = [ip for ip in ips if '.' in ip]
# Prefer IPv6 per RFC (unless configured otherwise)
sorted_ips = ipv6_addrs + ipv4_addrs
# Implement round-robin for load balancing
return sorted_ips[1:] + sorted_ips[:1] if sorted_ips else []
except socket.gaierror:
return []
Consider these production scenarios:
Scenario | Typical Behavior |
---|---|
Cloud load balancers | Round-robin with health checks |
CDN endpoints | Geo-based priority sorting |
Dual-stack hosts | Happy Eyeballs algorithm (RFC 8305) |
For specialized applications, you might override default behavior:
// JavaScript example using DNS-over-HTTPS
async function customResolver(domain) {
const response = await fetch(https://cloudflare-dns.com/dns-query?name=${domain}, {
headers: { 'Accept': 'application/dns-json' }
});
const data = await response.json();
// Custom sorting:
// 1. Prefer low-latency DCs
// 2. Avoid previously failed IPs
// 3. Filter by protocol preference
return sortByLatency(data.Answer);
}
Debugging techniques include:
- Using
dig +short example.com
to see raw responses - Checking TTL values for cache behavior
- Verifying client-side sorting with packet captures