freeaddrinfo
gai_strerror
getnameinfo
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.
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 */
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.
Q15. What is the step-by-step lifecycle of a TCP server using the modern POSIX API?
- Call
getaddrinfo(NULL, port, &hints, &result)withAI_PASSIVEto get a bindable address. - Loop through results: call
socket(), setSO_REUSEADDR, callbind(). Stop at first success. - Call
freeaddrinfo(result). - Call
listen()to mark the socket as passive. - 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.
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 |
| 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 |
You have covered freeaddrinfo, gai_strerror, getnameinfo, and a full TCP server example with interview prep.
