DNS
getaddrinfo()
TLPI Ch.59
What is DNS and why does it matter to programmers?
When you write a socket program and want to connect to www.google.com, the kernel needs a numeric IP address, not a name. The job of translating hostnames → IP addresses belongs to the Domain Name System (DNS).
As a C programmer you call getaddrinfo("www.google.com", ...) and it returns IP addresses. Under the hood, it talks to DNS. Understanding how DNS works helps you debug network problems, understand latency, and write more robust applications.
Key Terms
In the early days of the Internet, every machine kept a local file called /etc/hosts that mapped hostnames to IP addresses:
# IP-address canonical hostname [aliases]
127.0.0.1 localhost
192.168.1.10 myserver server1
The old function gethostbyname() (now obsolete) simply searched this file. This worked when there were only a few hundred machines on the Internet.
Why it failed: As the Internet grew to millions of hosts, maintaining a single flat file became impossible. Every time a host was added or its IP changed, every machine on the Internet would need an updated copy. DNS was invented to solve this scaling problem.
| /etc/hosts (old way)
One flat file |
→ | DNS (modern way)
Hierarchical tree |
DNS organises hostnames into a tree (hierarchy). Think of it like a file system path but read right-to-left. The root is at the top.
| Anonymous Root (.) | |||||||||
| Top-Level Domains (TLDs) | |||||||||
com |
edu |
net |
org |
de |
eu |
nz |
us |
int |
gov |
| Second-Level Domains (under their TLD) | |||||||||
google.com |
kernel.org |
gnu.org |
ac.nz |
otago.ac.nz |
|||||
| Hostnames (leaf nodes) | |||||||||
www.google.com |
www.kernel.org |
ftp.gnu.org |
www.otago.ac.nz |
mail.otago.ac.nz |
|||||
Key rule: A domain name is read right to left from root. www.kernel.org. means: start at root (.), go to org, then to kernel, then to www. The trailing period is the root — it is often omitted but always implied.
| Term | Meaning | Example |
|---|---|---|
| Label | One segment between dots, max 63 chars | kernel, org, www |
| Domain name | Labels from node up to root, joined by dots | kernel.org |
| FQDN | Fully Qualified Domain Name — complete path to root, ends with . |
www.kernel.org. |
| TLD | Top-Level Domain — directly under root | com, org, nz |
| Zone | A subtree managed by one DNS server | The otago.ac.nz zone |
TLDs come in two flavours: generic and country.
| Type | Description | Examples |
|---|---|---|
| Generic TLDs (gTLD) | Mostly international. Original 7 defined in 1984. New ones added since. | com, edu, net, org, int, mil, gov; newer: info, name, museum |
| Country TLDs (ccTLD) | Two-character code per nation (ISO 3166-1). Some countries add second-level divisions. | de (Germany), nz (New Zealand), us (USA), eu (European Union) |
Example — New Zealand’s second-level divisions under .nz:
| Domain | Used by |
|---|---|
ac.nz |
Academic institutions (universities) |
co.nz |
Commercial organisations |
govt.nz |
Government agencies |
The DNS hierarchy is managed in a distributed way — no single machine knows all mappings. The tree is divided into zones, and each zone is managed by its own DNS server.
| Root Zone — 13 root server groups worldwide | ||
| delegates to ▼ | ||
| Zone: org Managed by Public Interest Registry |
Zone: nz Managed by InternetNZ |
Zone: com Managed by Verisign |
| delegates to ▼ | ||
| Zone: kernel.org Managed by kernel.org admins |
Zone: otago.ac.nz Managed by Otago University IT |
Zone: google.com Managed by Google |
Each zone has a primary master name server and one or more slave (secondary) name servers for redundancy.
On Linux, the DNS server software is BIND (Berkeley Internet Name Domain), implemented by the named(8) daemon, maintained by the Internet Systems Consortium (ISC). Its configuration lives at /etc/named.conf.
When your C program calls getaddrinfo("www.otago.ac.nz", ...), here is what actually happens step by step:
| Step | Who sends | Who receives | Result |
|---|---|---|---|
| 1 | Your app (getaddrinfo) | Local DNS server | Recursive request: “Please resolve www.otago.ac.nz for me completely” |
| 2 | Local DNS server | Root name server | Iterative: Root replies “I don’t know, ask the nz server” |
| 3 | Local DNS server | nz server |
Iterative: nz replies “I don’t know, ask the ac.nz server” |
| 4 | Local DNS server | ac.nz server |
Iterative: ac.nz replies “ask the otago.ac.nz server” |
| 5 | Local DNS server | otago.ac.nz server |
Answer! Returns IP address of www.otago.ac.nz |
| 6 | Local DNS server | Your app | Returns final IP address to your program + caches the result |
| Your App getaddrinfo() |
⟺ recursive ⟺ | Local DNS resolver |
⟺ iterative ⟺ | Root name server |
→ | nz server | → | otago.ac.nz |
Key insight: Your app makes ONE recursive call to the local DNS server. The local server then does all the iterative work. Caching means repeated lookups for the same domain are answered instantly by the local server — no network trips needed.
If you give an incomplete name (no dots) to gethostbyname() or getaddrinfo(), the resolver tries to complete it before sending the query. The rules for how to complete it are in /etc/resolv.conf.
Example: If you are logged into the machine oghma.otago.ac.nz and type:
ssh octavo
The resolver looks up octavo.otago.ac.nz — it appends the local domain suffix automatically.
# /etc/resolv.conf example
nameserver 192.168.1.1 # IP of your local DNS server
search otago.ac.nz # domain suffix to append for short names
domain otago.ac.nz # default domain
The correct modern API is getaddrinfo(). It talks to the resolver library which then talks to DNS. Here is a minimal example that resolves a hostname:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
struct addrinfo hints, *result, *rp;
char addr_str[INET6_ADDRSTRLEN];
int s;
if (argc != 2) {
fprintf(stderr, "Usage: %s hostname\n", argv[0]);
exit(1);
}
/* Tell getaddrinfo we accept both IPv4 and IPv6, any socket type */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* TCP */
/* Perform DNS lookup — this calls the resolver library → DNS */
s = getaddrinfo(argv[1], NULL, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(1);
}
printf("Addresses for %s:\n", argv[1]);
/* Walk through all returned addresses */
for (rp = result; rp != NULL; rp = rp->ai_next) {
void *addr_ptr;
if (rp->ai_family == AF_INET) {
/* IPv4 */
struct sockaddr_in *ipv4 = (struct sockaddr_in *)rp->ai_addr;
addr_ptr = &(ipv4->sin_addr);
printf(" IPv4: ");
} else {
/* IPv6 */
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)rp->ai_addr;
addr_ptr = &(ipv6->sin6_addr);
printf(" IPv6: ");
}
/* Convert binary address back to text for display */
inet_ntop(rp->ai_family, addr_ptr, addr_str, sizeof(addr_str));
printf("%s\n", addr_str);
}
freeaddrinfo(result); /* must free the result list */
return 0;
}
Compile and run:
gcc -o dns_lookup dns_lookup.c
./dns_lookup www.google.com
Expected output (varies):
Addresses for www.google.com:
IPv4: 142.250.195.36
IPv6: 2404:6800:4007:80e::2004
DNS servers cache results to avoid repeated network round trips. Every DNS record has a TTL (Time To Live) value in seconds. The local DNS server keeps the answer cached for that long.
| Situation | What Happens |
|---|---|
| First lookup for a domain | Full iterative resolution — can take 100–500 ms |
| Same domain lookup within TTL | Answered from cache — microseconds |
| TTL expired | Full resolution again from authoritative server |
| Admin changes IP address | Old IP stays in cache until TTL expires — can cause temporary outage |
You can check DNS TTL values using the dig command:
# Check TTL for www.kernel.org
dig www.kernel.org
# Check the root name servers
dig . NS
Q1. What is DNS and why was it needed?
DNS (Domain Name System) translates human-readable hostnames like www.google.com into IP addresses. Before DNS, machines used a flat file /etc/hosts which did not scale. DNS uses a distributed hierarchical database managed across thousands of servers worldwide, allowing billions of entries without any central bottleneck.
Q2. What is the difference between a recursive and an iterative DNS query?
In a recursive query, the client asks the DNS server to fully resolve the name — the server does all the work. Your application sends a recursive query to the local DNS server. In an iterative query, the server just refers the client to another server. The local DNS server uses iterative queries when talking to root and other DNS servers — it does all the iterative steps itself and gives your app the final answer.
Q3. What is a Fully Qualified Domain Name (FQDN)?
An FQDN is the complete domain name ending with a trailing dot that represents the root. Example: www.kernel.org. — the final dot is the anonymous root. It uniquely identifies a host in the global DNS hierarchy without any ambiguity.
Q4. What is a DNS zone?
A zone is a subtree of the DNS hierarchy managed by a specific DNS server (or group of servers). For example, the otago.ac.nz zone is managed by Otago University’s IT team. They can add or change hosts within that zone without affecting any other zone.
Q5. What is the difference between getaddrinfo() and gethostbyname()?
gethostbyname() is the old API — it only returns IPv4 addresses, is not thread-safe (uses static storage), and is now deprecated. getaddrinfo() is the modern replacement — it supports both IPv4 and IPv6, is thread-safe, returns a linked list of results, and can also resolve service names to port numbers.
Q6. Why can DNS resolution sometimes be slow?
The first resolution of an uncached domain requires multiple round trips through the hierarchy (root → TLD → authoritative server), each adding network latency. If any server is slow or temporarily unreachable, the resolver has to wait for timeouts. After the first lookup, caching eliminates these delays for subsequent queries within the TTL period.
Q7. What is /etc/resolv.conf and what does it contain?
/etc/resolv.conf configures the local resolver library. It specifies the IP address of the local DNS server (nameserver directive) and default domain suffixes to append when resolving short names (search / domain directives). The resolver reads this file when getaddrinfo() or similar functions are called.
Q8. What is BIND and what is the named daemon on Linux?
BIND (Berkeley Internet Name Domain) is the most widely used DNS server software on Linux. The named(8) daemon implements BIND. It reads its configuration from /etc/named.conf and responds to DNS queries from clients on the network.
Q9. Can DNS return multiple IP addresses for one hostname? Why?
Yes. This is called DNS round-robin or multi-homing. A hostname like www.google.com can map to dozens of IP addresses across different data centers. Each call to getaddrinfo() may return a different ordering, distributing load across servers. That is why getaddrinfo() returns a linked list, not a single address.
Continue Learning
Next: The /etc/services File (Part 3 of 3)
