Linux Sockets: Internet Domains freeaddrinfo() & gai_strerror()

 

Linux Sockets: Internet Domains
Chapter 59 Part 6 — File 1 of 4  |  freeaddrinfo() & gai_strerror()
Topic
Memory Management
API
freeaddrinfo
API
gai_strerror

What This File Covers

When you call getaddrinfo(), the system dynamically allocates memory on your behalf to hold a linked list of address structures. If you never free that memory, your program will leak it. This file explains the two cleanup helpers: freeaddrinfo() to release memory and gai_strerror() to translate error codes into human-readable messages.

Key Terms
getaddrinfo freeaddrinfo gai_strerror addrinfo dynamic allocation memory leak EAI error codes netdb.h

59.10.2   Freeing addrinfo Lists — freeaddrinfo()

Whenever you call getaddrinfo(), the library allocates a linked list of struct addrinfo structures and sets your result pointer to the head of that list. Each node in the list may also point to a dynamically allocated socket address structure (struct sockaddr_in or struct sockaddr_in6).

All of this memory is yours to use — but also yours to free. If you forget to free it, every call to getaddrinfo() in a long-running program (like a server) causes a memory leak.

result
(your pointer)
addrinfo[0]
ai_addr → sockaddr_in
ai_next →
addrinfo[1]
ai_addr → sockaddr_in6
ai_next →
NULL

Each node in the linked list — plus its ai_addr sub-structure — was heap-allocated by getaddrinfo(). freeaddrinfo() walks the entire chain and frees every node.

Function Signature

#include <sys/socket.h>
#include <netdb.h>

void freeaddrinfo(struct addrinfo *result);

result is the same pointer that getaddrinfo() set. One call frees the entire list — you do not need to loop through nodes manually.

Important Rule: Copy Before Freeing

If you want to keep one of the addrinfo structures (or its socket address) for use after calling freeaddrinfo(), you must copy it first. After the free call, every pointer in the list is invalid.

Complete Working Example

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

int main(void)
{
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    char ipstr[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof(hints));
    hints.ai_family   = AF_UNSPEC;    /* IPv4 or IPv6 */
    hints.ai_socktype = SOCK_STREAM;  /* TCP */

    int ret = getaddrinfo("example.com", "80", &hints, &result);
    if (ret != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret));
        return 1;
    }

    /* Walk the list — use every address */
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        void *addr;
        if (rp->ai_family == AF_INET) {
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)rp->ai_addr;
            addr = &ipv4->sin_addr;
        } else {
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)rp->ai_addr;
            addr = &ipv6->sin6_addr;
        }
        inet_ntop(rp->ai_family, addr, ipstr, sizeof(ipstr));
        printf("IP address: %s\n", ipstr);
    }

    /* ALWAYS free when done — one call frees entire list */
    freeaddrinfo(result);

    return 0;
}

Copying a Node Before Freeing

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netdb.h>

int main(void)
{
    struct addrinfo hints, *result;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family   = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    if (getaddrinfo("localhost", "8080", &hints, &result) != 0)
        return 1;

    /* Make a deep copy of the first node's socket address */
    struct sockaddr_in saved_addr;
    memcpy(&saved_addr, result->ai_addr, sizeof(struct sockaddr_in));

    /* Now safe to free */
    freeaddrinfo(result);

    /* saved_addr is valid here even though result is freed */
    printf("Saved port: %d\n", ntohs(saved_addr.sin_port));

    return 0;
}
void return — no error code Frees entire linked list Copy before free if needed

59.10.3   Diagnosing Errors — gai_strerror()

getaddrinfo() does not set errno on failure. Instead it returns a nonzero integer error code. The gai_strerror() function converts that integer into a human-readable string so you can print a meaningful error message.

Function Signature

#include <netdb.h>

const char *gai_strerror(int errcode);

/* Returns: pointer to a string describing the error */

Error Code Table

Error Constant What It Means Who Uses It
EAI_ADDRFAMILY No addresses for the host exist in the requested address family getaddrinfo only
EAI_AGAIN Temporary DNS failure — try again later Both
EAI_BADFLAGS Invalid flag in ai_flags Both
EAI_FAIL Unrecoverable name server failure Both
EAI_FAMILY Address family not supported Both
EAI_MEMORY Memory allocation failed Both
EAI_NODATA No address associated with hostname (not in SUSv3) getaddrinfo only
EAI_NONAME Unknown host or service name Both
EAI_OVERFLOW Argument buffer overflow Both
EAI_SERVICE Service not supported for socket type getaddrinfo only
EAI_SOCKTYPE hints.ai_socktype is not supported getaddrinfo only
EAI_SYSTEM System error — check errno Both

Usage Pattern

#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>

void resolve_host(const char *host, const char *port)
{
    struct addrinfo hints = {0};
    struct addrinfo *result;

    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    int err = getaddrinfo(host, port, &hints, &result);
    if (err != 0) {
        /* Use gai_strerror to get a readable message */
        fprintf(stderr, "getaddrinfo(%s, %s): %s\n",
                host, port, gai_strerror(err));

        /* Special case: EAI_SYSTEM means check errno too */
        if (err == EAI_SYSTEM)
            perror("  system error");

        return;
    }

    printf("Resolution of '%s' succeeded\n", host);
    freeaddrinfo(result);
}

int main(void)
{
    resolve_host("example.com", "80");
    resolve_host("this.host.does.not.exist.xyz", "80");  /* will fail */
    return 0;
}

Key Points About gai_strerror

Property strerror() (for errno) gai_strerror() (for getaddrinfo)
Header string.h netdb.h
Input errno integer EAI_* integer
Returns const char* string const char* string
Use case System call errors Name resolution errors
Never use errno with getaddrinfo gai_strerror is thread-safe EAI_SYSTEM is the only code that also sets errno

Interview Questions — freeaddrinfo & gai_strerror

Q1. Why does getaddrinfo() require a separate freeaddrinfo() call instead of the caller using free() directly?

The internal data structure is a linked list where each node may in turn point to a socket address structure. The caller has no visibility into exactly what was allocated or in how many separate heap blocks. freeaddrinfo() walks the entire list and frees each node and its associated pointers in the correct order. Using free(result) directly would only free the first node and leak everything else.

Q2. What happens if you call freeaddrinfo() twice on the same pointer?

Undefined behavior — a double-free. This is the same risk as any heap memory in C. The safe pattern is to set the pointer to NULL after calling freeaddrinfo(result); result = NULL;.

Q3. getaddrinfo() failed. Can I use perror() to print the error?

No. perror() reads from errno, but getaddrinfo() does not set errno (except in the special EAI_SYSTEM case). You must pass the integer return value to gai_strerror() to get a meaningful message.

Q4. What does EAI_AGAIN tell you and how should you handle it?

It means the DNS resolver got a temporary failure — the name server was unreachable or returned SERVFAIL. The correct response is to retry after a short delay, not to treat it as a permanent error. In a production server you might retry a few times with exponential back-off before giving up.

Q5. Why is it safe to call freeaddrinfo() even if you are still holding a file descriptor (socket) that was created from one of the addrinfo nodes?

Once you call connect() or bind(), the kernel has already extracted all the information it needs from the socket address structure. The socket is now an operating-system object identified by a file descriptor. Freeing the addrinfo list only releases the user-space copy of the address; the kernel’s internal state is unaffected.

Next: getnameinfo() — Reverse Address Lookup

File 2 covers getnameinfo() and all its flags in detail.

File 2 → EmbeddedPathashala

Leave a Reply

Your email address will not be published. Required fields are marked *