Socket · Linux userspace / kernel ABI

getaddrinfo and name resolution

Separates connection attempts from NSS policy, IPv4/IPv6 address lists, service names, and the blocking resolver rather than treating resolution as DNS alone.

Series
35 / 38
Build
cc -std=c17 -Wall -Wextra -O2 resolve.c -o resolve
Run
./resolve localhost 80
Kernel
Linux 6.18.37 LTS

Is connecting only to the first getaddrinfo result sufficient?

getaddrinfo is not a DNS-only function. According to /etc/nsswitch.conf policy, it consults sources such as files, DNS, and mDNS and creates an addrinfo list matching address-family and socket-type constraints.

The returned order reflects destination-address selection policy, but the first address is not guaranteed reachable. Try the results while managing a total timeout, or parallelize IPv6 and IPv4 candidates.

Structure diagram

Figure 1. Connection candidates produced from one name
orderfamilysockaddrattempt stateowned fd 1AF_INET6[2001:db8::20]:443SYN-SENT · 250 msfd 62AF_INET192.0.2.20:443start after 50 msfd 73AF_INET6[2001:db8::21]:443waiting candidatenonewinnerAF_INET192.0.2.20:443ESTABLISHEDfd 7cleanupAF_INET6cancel first attemptcloserelease fd 6

addrinfo results form a candidate list. If the first row fails or stalls, try another family/address within the overall deadline.

Call path

Figure 2. From userspace code to observable results
host/service string input
NSS query files/DNS, etc.
addrinfo list family/type/protocol
connect attempts use each sockaddr
selected peer successful fd + canonical information

Maintain name-resolution results and actual network-connection results in different caches/state. One address list does not mean one service instance.

Figure 3. Major points along the kernel-internal path
resolver libc NSS module
DNS socket UDP/TCP query possible
routing route per candidate
connect socket per family
peer finalize 4/6 tuple

This diagram is not for memorizing function names. Follow which return value, file descriptor, memory mapping, or wait queue is passed to the next stage.

Linux 6.18.37 LTS source locations

Go beyond the glibc function to the files where the syscall implementation meets kernel objects. Each link points to the original file at the same tag.

FileFunction / structureWhat to inspect
net/socket.c __sys_connect() Use a resolver-produced sockaddr in a real socket operation
net/ipv6/af_inet6.c inet6_create(), inet6_bind() IPv6 socket-family path
net/ipv4/af_inet.c inet_create() IPv4 socket-family path and protocol selection

Complete runnable example

The code below is not pseudocode with explanatory lines omitted. It is a minimal example that can be built and run as a file.

Buildcc -std=c17 -Wall -Wextra -O2 resolve.c -o resolve
01#define _POSIX_C_SOURCE 200809L
02#include <arpa/inet.h>
03#include <netdb.h>
04#include <stdio.h>
05#include <string.h>
06
07int main(int argc, char **argv)
08{
09    if (argc != 3)
10        return 2;
11    struct addrinfo hints;
12    memset(&hints, 0, sizeof(hints));
13    hints.ai_family = AF_UNSPEC;
14    hints.ai_socktype = SOCK_STREAM;
15    hints.ai_protocol = IPPROTO_TCP;
16
17    struct addrinfo *results;
18    int error = getaddrinfo(argv[1], argv[2], &hints, &results);
19    if (error != 0) {
20        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
21        return 1;
22    }
23    for (const struct addrinfo *item = results; item != NULL; item = item->ai_next) {
24        char host[NI_MAXHOST], service[NI_MAXSERV];
25        int rc = getnameinfo(item->ai_addr, item->ai_addrlen,
26            host, sizeof(host), service, sizeof(service),
27            NI_NUMERICHOST | NI_NUMERICSERV);
28        if (rc == 0)
29            printf("family=%d %s:%s\n", item->ai_family, host, service);
30    }
31    freeaddrinfo(results);
32    return 0;
33}

Code notes

Source line 12memset(&hints, 0

Starts unused addrinfo fields and padding at 0 so only explicitly requested constraints reach the resolver.

Source line 13AF_UNSPEC

Allows both IPv4 and IPv6. Flags such as AI_ADDRCONFIG can reduce results according to host-interface configuration, so choose them to match requirements.

Source line 18int error = getaddrinfo

The return value is an EAI_* code, not errno. Interpret it with gai_strerror; errno has additional meaning only for EAI_SYSTEM.

Source line 23item = item->ai_next

Walks the full list of connectable candidates. Pass ai_addr and ai_addrlen unchanged to connect on the matching family socket.

Source line 27NI_NUMERICHOST | NI_NUMERICSERV

Formats only numeric addresses and ports without performing reverse DNS and service lookup again during output.

Detailed behavior

01

NSS lookup can be a blocking operation

The thread calling getaddrinfo may wait for resolver timeouts, NSS modules, and network responses. Calling it directly in an event-loop thread stops all connection handling.

Choose a dedicated resolver pool, an asynchronous extension such as getaddrinfo_a, or an application DNS client according to latency requirements.

02

AI_PASSIVE and wildcard addresses

For a server bind, a NULL node with AI_PASSIVE creates a wildcard address. Without AI_PASSIVE it may produce loopback. Do not mix client and server hints in the same helper.

Check IPv4-mapped behavior of an IPv6 wildcard socket against IPV6_V6ONLY settings and OS policy.

03

Cache TTL and connection lifetime differ

Expiration of a DNS-record TTL does not automatically move an established TCP connection to a new address. Give resolver cache, connection pool, and retry policy separate lifetimes.

Define how to drain stale pools under negative caching and deployment address rotation.

Objects and lifetimes

ObjectCreation and releaseValues to inspect
addrinfo listAllocated by getaddrinfo and released as a whole by freeaddrinfofamily, socktype, protocol, sockaddr
NSS query stateCreated per source during the resolver call and cleaned after the result/errortimeout, search domain, cache
connection candidateA socket/deadline is created per addrinfo entry and closed on success or failureaddress, attempt time, error

Failure conditions and common misconceptions

Observed symptomLikely causesHow to verify
EAI_AGAINA transient resolver failure/timeoutInspect NSS sources and retry budget
The first address stalls for a long timeCandidate connects are serialInspect attempt timelines by family
event loop stallBlocking getaddrinfo was called in the loop threadInspect thread stacks and resolver latency

Verify it yourself

  1. Use strace on localhost and the real hostname to compare /etc/hosts, nsswitch, and DNS syscalls.
  2. Compare results for AF_INET/AF_INET6/AF_UNSPEC hints and record each candidate's connect error.
  3. Create a resolver worker thread and result eventfd so the main epoll loop does not block.
Run./resolve localhost 80
Tracestrace -f -e trace=openat,read,connect,sendto,recvfrom ./resolve localhost 80

Primary sources