Linux Sockets: Interview Q&A Complete Interview Reference

 

Linux Sockets: Interview Q&A
Chapter 59 Part 6 — File 4 of 4  |  Complete Interview Reference
Topics
freeaddrinfo
Topics
gai_strerror
Topics
getnameinfo
Topics
TCP Server

How to Use This File

This file consolidates all interview questions from Files 1–3 into one place, grouped by topic. Each question includes a complete answer and, where relevant, a code snippet. Use this for last-minute revision or as a quick reference when studying Linux socket programming.

Topics Covered in This Q&A Sheet
freeaddrinfo memory model EAI error codes getnameinfo flags reverse DNS NI_NAMEREQD SO_REUSEADDR SIGPIPE AI_PASSIVE iterative vs concurrent ASCII integers

Section A — freeaddrinfo() & gai_strerror()

Q1. What does getaddrinfo() allocate and how does freeaddrinfo() clean it up?

getaddrinfo() heap-allocates a linked list of struct addrinfo nodes. Each node may also point to a heap-allocated struct sockaddr sub-structure. The caller gets only a pointer to the head of the list — the internal allocation details are hidden. freeaddrinfo() traverses the entire list, freeing every node and its associated socket address structure in one call. Simply calling free(result) would only free the first node and leak the rest.

Q2. What is the safe pattern after calling freeaddrinfo()?

Set the pointer to NULL immediately after freeing to prevent accidental use-after-free or double-free:

freeaddrinfo(result);
result = NULL;   /* guard against accidental reuse */

Q3. getaddrinfo() returned a nonzero value. Can I use perror() or strerror() to describe the error?

No. getaddrinfo() does not set errno (except for the special EAI_SYSTEM code). You must pass the return value to gai_strerror():

int err = getaddrinfo(host, port, &hints, &res);
if (err != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err));
    if (err == EAI_SYSTEM)
        perror("  system error");   /* only here is errno also valid */
}

Q4. What does EAI_AGAIN mean and how should an application react?

EAI_AGAIN is a temporary name resolution failure — the DNS server was unreachable or returned SERVFAIL. The correct response is to wait briefly and retry. It should not be treated as a permanent failure like EAI_NONAME (unknown host). In production code, implement retry with exponential back-off and a maximum attempt limit.

Q5. What is EAI_SYSTEM and how is it different from all other EAI_* codes?

EAI_SYSTEM is the only EAI code that also sets errno. It means a system-level error (a syscall failure) happened inside getaddrinfo(). For every other EAI code, errno is undefined and you must use gai_strerror(). For EAI_SYSTEM, check both: gai_strerror(EAI_SYSTEM) gives the general message, and strerror(errno) or perror() gives the specific OS error.

Q6. Can you call freeaddrinfo() on a partially-iterated list?

Yes. freeaddrinfo() always takes the original head pointer returned by getaddrinfo(), not the current iteration pointer. Even if you iterated halfway through, passing the original head still frees the entire list. Never pass a mid-list pointer to freeaddrinfo() — that would leak the nodes before it.

struct addrinfo *result, *rp;
getaddrinfo(..., &result);

for (rp = result; rp; rp = rp->ai_next) {
    /* rp advances, but always free result (the head) */
}

freeaddrinfo(result);   /* correct — frees entire list */
/* freeaddrinfo(rp);    WRONG — rp is NULL or mid-list */

Section B — getnameinfo()

Q7. How is getnameinfo() the inverse of getaddrinfo()?

getaddrinfo() takes a human-readable hostname and service name and returns binary socket address structures (forward lookup). getnameinfo() takes a binary socket address structure and returns human-readable hostname and service name strings (reverse lookup). Together they form a protocol-independent name resolution API that works identically for IPv4 and IPv6.

Q8. What are NI_MAXHOST and NI_MAXSERV and why do you need to define _GNU_SOURCE?

NI_MAXHOST (1025) and NI_MAXSERV (32) are the maximum buffer sizes you will ever need for hostname and service strings. They are not in POSIX (SUSv3) but are defined on all major UNIX implementations. On Linux with glibc ≥ 2.8 they require #define _GNU_SOURCE (or _BSD_SOURCE or _SVID_SOURCE) before #include <netdb.h> to be visible.

Q9. When should you use NI_NUMERICHOST?

Use NI_NUMERICHOST whenever you want the IP address as a string and a DNS round-trip would be too slow or unnecessary — for example, in a server that logs thousands of connections per second. Without this flag, getnameinfo() performs a reverse DNS lookup, which can block for seconds. With the flag, it just calls inet_ntop() internally and returns immediately.

Q10. Why is NI_DGRAM necessary? Give a concrete example.

By default, getnameinfo() looks up the service name for the TCP protocol. Some port numbers are assigned to completely different services for TCP vs UDP. For example, port 512 is “exec” in TCP but “biff” (mail notification) in UDP. Without NI_DGRAM on a UDP socket, you would get the wrong service name. Always include NI_DGRAM when your socket type is SOCK_DGRAM.

Q11. What is the difference in behaviour when NI_NAMEREQD is set vs when it is not?

Without NI_NAMEREQD: if reverse DNS fails for the address, getnameinfo() silently falls back to returning a numeric address string in the host buffer. The call succeeds. With NI_NAMEREQD: if reverse DNS fails, the call returns EAI_NONAME and the host buffer is unchanged. Use NI_NAMEREQD when you must have a real hostname — e.g., access-control rules based on DNS names.

Q12. Can you pass NULL for both host and service arguments?

No. At least one of host and service must be non-NULL (with a corresponding nonzero length). Passing NULL for both is undefined behaviour and will typically fail. You may pass NULL for one to indicate you are not interested in that piece of information — for example, host=NULL, hostlen=0 when you only want the service name.

Q13. Why do servers use struct sockaddr_storage instead of struct sockaddr_in with accept()?

struct sockaddr_in can only hold an IPv4 address. struct sockaddr_storage is defined to be large enough for any address family — IPv4, IPv6, or UNIX domain. A server that uses sockaddr_storage works correctly whether a client connects over IPv4 or IPv6, without needing to know in advance which the client will use. The pointer is simply cast to struct sockaddr * when passed to accept() or getnameinfo().

Q14. What is the effect of NI_NOFQDN?

NI_NOFQDN trims the fully qualified domain name to just the first component for hosts on the local network. For example, instead of “server.corp.example.com”, only “server” is returned. This has no effect on remote hosts (where the full name is always returned). It is useful for displaying hostnames in local admin tools where the full domain name adds visual noise without adding information.

Section C — TCP Client-Server Design

Q15. What is the step-by-step lifecycle of a TCP server using the modern POSIX API?

  1. Call getaddrinfo(NULL, port, &hints, &result) with AI_PASSIVE to get a bindable address.
  2. Loop through results: call socket(), set SO_REUSEADDR, call bind(). Stop at first success.
  3. Call freeaddrinfo(result).
  4. Call listen() to mark the socket as passive.
  5. Loop: call accept() to get a connected socket. Serve the client. close() the connected socket.

Q16. Why does the server loop through the list returned by getaddrinfo() instead of just using the first entry?

On a dual-stack host, getaddrinfo() may return an IPv6 address entry followed by an IPv4 entry (or vice versa). The first entry might fail — for example, if the kernel has not loaded IPv6 support or if the port is already in use on one family. Iterating through all entries and binding the first that succeeds makes the server portable and resilient across different system configurations.

Q17. What is SO_REUSEADDR and why does every TCP server need it?

When a TCP connection ends, the kernel keeps the port in TIME_WAIT state for up to 2×MSL (about 60–120 seconds) to absorb delayed packets. During this time, bind() to that port fails with EADDRINUSE. Setting SO_REUSEADDR before bind() overrides this restriction and lets the server rebind immediately. Without it, restarting a crashed server during development would require waiting over a minute — highly inconvenient. In production, it prevents unnecessary downtime.

int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
/* Must be called BEFORE bind() */

Q18. What is SIGPIPE, when is it generated, and why does the sequence-number server ignore it?

SIGPIPE is generated when a process writes to a socket (or pipe) whose read end has been closed. By default, SIGPIPE terminates the process. For the sequence-number server, a single client crashing mid-session would kill the entire server — making it unavailable to all other clients. By ignoring SIGPIPE (signal(SIGPIPE, SIG_IGN)), the write() call instead returns -1 with errno set to EPIPE, which the server can detect and recover from gracefully.

Q19. Why are integers sent as ASCII strings in this client-server example?

Binary integers have endianness — a 4-byte integer 0x00000001 is stored as bytes 00 00 00 01 on big-endian hardware but as 01 00 00 00 on little-endian. If the client and server run on machines with different byte orders, one would misinterpret the other’s integer. Using ASCII (“1\n”) means the integer is exactly the same sequence of characters on all hardware, at the cost of a small parsing step (atoi()) on the receiver.

Q20. What is the limitation of an iterative server and when would you switch to a concurrent model?

An iterative server blocks in accept() (or in a client read() while serving the current client), so new clients queue up in the kernel’s listen backlog and eventually time out if the queue fills. This is acceptable only if per-client service time is very short and predictable. Switch to a concurrent model (fork, threads, epoll) when: service time is long, client count is high, or one slow client must not delay others. The sequence-number server is a good fit for iterative because each request takes microseconds.

Q21. What does AI_PASSIVE do and what is the resulting socket address?

When the first argument to getaddrinfo() is NULL and AI_PASSIVE is set in hints.ai_flags, the returned socket address structures use the wildcard address: 0.0.0.0 for IPv4 or :: for IPv6. A server that binds to the wildcard address accepts incoming connections arriving on any of the host’s network interfaces (loopback, Ethernet, Wi-Fi, etc.). Without AI_PASSIVE, the NULL hostname would resolve to the loopback address (127.0.0.1), restricting the server to local connections only.

Q22. When can a server safely call freeaddrinfo() and why is it safe even though it just bound a socket?

freeaddrinfo() can be called immediately after the bind() loop succeeds. The bind() system call copies the socket address into the kernel. After that, the user-space addrinfo list is no longer needed — the socket is a kernel object and will continue to function correctly. Freeing the addrinfo list does not affect the already-bound socket in any way.

Quick Reference Cheat Sheet

API Summary

Function Purpose Header Returns
getaddrinfo() hostname+service → sockaddr list netdb.h 0 or EAI_*
freeaddrinfo() free the getaddrinfo result list netdb.h void
gai_strerror() EAI_* code → error string netdb.h const char*
getnameinfo() sockaddr → hostname+service strings netdb.h 0 or EAI_*

NI_* Flags Quick Lookup

Flag Effect Use When
NI_NUMERICHOST Return IP address string, skip DNS Speed critical / no DNS needed
NI_NUMERICSERV Return port number string, skip /etc/services Ephemeral ports / speed
NI_DGRAM Use UDP service table instead of TCP Socket is SOCK_DGRAM
NI_NAMEREQD Fail (EAI_NONAME) if hostname unresolvable Access control by DNS name
NI_NOFQDN Return short name for local hosts Human-readable local UI

TCP Server Checklist

# Step Common Mistake If Skipped
1 signal(SIGPIPE, SIG_IGN) One bad client write kills the server
2 getaddrinfo with AI_PASSIVE Without AI_PASSIVE, gets loopback — server only reachable locally
3 setsockopt SO_REUSEADDR before bind Cannot restart server for 60+ seconds after crash
4 Loop through addrinfo list to bind Fails on systems where first entry is unsupported
5 freeaddrinfo after bind Memory leak (small, but wrong)
6 listen() Socket stays CLOSED — accept() would fail
7 close(cfd) after each client File descriptor leak — server runs out of FDs

Chapter 59 Part 6 — File Index
File Topic Key Functions / Concepts
File 1 freeaddrinfo & gai_strerror Memory management, EAI_* error codes
File 2 getnameinfo() Reverse lookup, NI_* flags, NI_MAXHOST, NI_MAXSERV
File 3 TCP Client-Server Example AI_PASSIVE, SO_REUSEADDR, SIGPIPE, iterative server
File 4 Interview Q&A Reference 22 questions, cheat sheets, API summary

Chapter 59 Part 6 Complete!

You have covered freeaddrinfo, gai_strerror, getnameinfo, and a full TCP server example with interview prep.

← File 1 ← File 3 EmbeddedPathashala

Leave a Reply

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