What Does getnameinfo() Do?
getnameinfo() is the reverse of getaddrinfo(). While getaddrinfo() converts a hostname + service name into a binary socket address structure, getnameinfo() converts a binary socket address structure back into human-readable hostname and service name strings.
It is the modern, thread-safe, IPv4/IPv6-compatible replacement for the obsolete gethostbyaddr() and getservbyport() functions. It is also defined in POSIX.1g and documented in RFC 3493.
Key Terms
#include <sys/socket.h>
#include <netdb.h>
int getnameinfo(const struct sockaddr *addr, /* Input: socket address */
socklen_t addrlen, /* Input: size of addr */
char *host, /* Output: hostname buffer */
socklen_t hostlen, /* Input: size of host buffer */
char *service, /* Output: service name buffer */
socklen_t servlen, /* Input: size of service buffer */
int flags); /* Input: NI_* flags */
/* Returns 0 on success, or nonzero error code on failure */
Parameters Explained
| Parameter | Direction | What it is |
|---|---|---|
| addr | INPUT | Pointer to a sockaddr structure (sockaddr_in for IPv4 or sockaddr_in6 for IPv6). This is the binary address to convert. |
| addrlen | INPUT | Size of the addr structure in bytes. Use sizeof(struct sockaddr_in) for IPv4 or sizeof(struct sockaddr_in6) for IPv6. |
| host | OUTPUT | Buffer where the hostname string will be written. Allocate NI_MAXHOST bytes. Pass NULL if you don’t want the hostname. |
| hostlen | INPUT | Size of the host buffer. Typically NI_MAXHOST (defined in <netdb.h>, usually 1025). |
| service | OUTPUT | Buffer where the service name string will be written. Allocate NI_MAXSERV bytes. Pass NULL if you don’t want the service name. |
| servlen | INPUT | Size of the service buffer. Typically NI_MAXSERV (defined in <netdb.h>, usually 32). |
| flags | INPUT | Bitwise OR of NI_* flag constants to control output format. Use 0 for defaults. |
getaddrinfo() vs getnameinfo() โ Side by Side
|
โ |
|
The flags argument is formed by ORing together NI_* constants. These control whether you get back names or numeric strings, and how the hostname is formatted.
๐ฉ NI_NUMERICHOST โ Return Numeric IP, Not Hostname
By default, getnameinfo() tries to convert the IP address to a hostname via reverse DNS lookup. If you set NI_NUMERICHOST, it skips DNS and returns the numeric IP address string instead.
When to use it: When you want to display or log the IP address rather than the hostname. Also useful when reverse DNS is unavailable or when you need a fast, non-blocking call.
/* With NI_NUMERICHOST: host buffer gets "93.184.216.34" instead of "www.example.com" */
getnameinfo(addr, addrlen, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICHOST);
printf("Client IP: %s\n", host); /* Prints: "93.184.216.34" */
๐ฉ NI_NUMERICSERV โ Return Port Number, Not Service Name
By default, getnameinfo() tries to look up the port number in /etc/services to return a service name (like “http”). NI_NUMERICSERV skips this lookup and returns the port number as a decimal string instead.
/* With NI_NUMERICSERV: service buffer gets "80" instead of "http" */
getnameinfo(addr, addrlen, host, NI_MAXHOST, service, NI_MAXSERV, NI_NUMERICSERV);
printf("Port: %s\n", service); /* Prints: "80" */
๐ฉ NI_NOFQDN โ Return Only Short Hostname, Not FQDN
FQDN = Fully Qualified Domain Name. For example: “mail.example.com” is an FQDN. “mail” is just the short (local) hostname. NI_NOFQDN tells getnameinfo() to return only the short hostname if the address belongs to the local domain.
/* Without NI_NOFQDN: "mail.internal.corp.com" */
/* With NI_NOFQDN: "mail" (if in the same local domain) */
getnameinfo(addr, addrlen, host, NI_MAXHOST, NULL, 0, NI_NOFQDN);
printf("Host: %s\n", host);
๐ฉ NI_NAMEREQD โ Fail If Name Cannot Be Resolved
By default, if getnameinfo() cannot resolve the IP address to a hostname (no reverse DNS entry), it falls back to returning the numeric IP string. Setting NI_NAMEREQD changes this โ if a hostname cannot be found, the function fails and returns an error (EAI_NONAME) rather than falling back to a numeric address.
When to use it: In security-sensitive applications where a numeric IP in the host field would be misleading, or when you strictly require a hostname.
int err = getnameinfo(addr, addrlen, host, NI_MAXHOST,
service, NI_MAXSERV, NI_NAMEREQD);
if (err == EAI_NONAME) {
printf("No hostname found for this IP address\n");
} else if (err != 0) {
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(err));
}
๐ฉ NI_DGRAM โ Look Up Service as UDP
By default, getnameinfo() looks up the port number in /etc/services using the TCP protocol. Setting NI_DGRAM switches the service lookup to use UDP instead. This matters for the rare case where the same port number means different services on TCP vs UDP (for example, port 514: TCP=rsh, UDP=syslog).
/* Port 514, no flag: service name returned = "shell" (rsh, TCP 514) */
getnameinfo(addr, addrlen, NULL, 0, service, NI_MAXSERV, 0);
/* Port 514, NI_DGRAM: service name returned = "syslog" (UDP 514) */
getnameinfo(addr, addrlen, NULL, 0, service, NI_MAXSERV, NI_DGRAM);
NI_* Flags Summary Table
| Flag | Effect | Default without flag |
|---|---|---|
| NI_NUMERICHOST | Return numeric IP string (skip reverse DNS) | Attempt reverse DNS lookup; fall back to numeric if not found |
| NI_NUMERICSERV | Return port number as decimal string | Look up port in /etc/services and return service name |
| NI_NOFQDN | Return short hostname only (not FQDN) if local domain | Return fully qualified domain name |
| NI_NAMEREQD | Fail (EAI_NONAME) if hostname cannot be resolved | Fall back to numeric address string if name not found |
| NI_DGRAM | Look up service as UDP instead of TCP | Look up service as TCP |
Example 1: Print Client Info After accept()
The most common real-world use of getnameinfo() is in a server โ after accepting a connection, you want to log the connecting client’s IP address and port.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
/* Utility: print the IP and port of a sockaddr */
void print_client_info(const struct sockaddr *addr, socklen_t addrlen) {
char host[NI_MAXHOST];
char service[NI_MAXSERV];
int err;
/* NI_NUMERICHOST: give IP address, not hostname (fast, no DNS)
NI_NUMERICSERV: give port number, not service name */
err = getnameinfo(addr, addrlen,
host, NI_MAXHOST,
service, NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV);
if (err != 0) {
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(err));
return;
}
printf("Client connected from IP: %s Port: %s\n", host, service);
}
int main() {
struct addrinfo hints, *res;
int listenfd, connfd;
struct sockaddr_storage client_addr; /* Large enough for IPv4 or IPv6 */
socklen_t client_addrlen;
/* Set up listening socket (simplified) */
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(NULL, "9000", &hints, &res) != 0) {
perror("getaddrinfo"); exit(1);
}
listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
bind(listenfd, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
listen(listenfd, 5);
printf("Listening on port 9000...\n");
/* Accept one connection */
client_addrlen = sizeof(client_addr);
connfd = accept(listenfd,
(struct sockaddr *)&client_addr,
&client_addrlen);
if (connfd == -1) { perror("accept"); exit(1); }
/* Use getnameinfo() to display client info */
print_client_info((struct sockaddr *)&client_addr, client_addrlen);
close(connfd);
close(listenfd);
return 0;
}
struct sockaddr_storage is a generic structure large enough to hold either an IPv4 or IPv6 socket address. It is the correct type to use for the address buffer in accept() when you don’t know which protocol will connect.Example 2: Both Reverse Lookup and Numeric Fallback
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
void resolve_ipv4(const char *ip_str) {
struct sockaddr_in addr;
char host[NI_MAXHOST];
char service[NI_MAXSERV];
int err;
/* Build an IPv4 sockaddr from a dotted-decimal string */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
inet_pton(AF_INET, ip_str, &addr.sin_addr);
/* Try to get the hostname (reverse DNS) */
err = getnameinfo((struct sockaddr *)&addr, sizeof(addr),
host, NI_MAXHOST,
service, NI_MAXSERV,
0); /* No flags: try hostname, try service name */
if (err == 0) {
printf("IP %s -> hostname: %s, service: %s\n", ip_str, host, service);
} else {
fprintf(stderr, "getnameinfo failed: %s\n", gai_strerror(err));
}
/* Now get numeric values explicitly */
err = getnameinfo((struct sockaddr *)&addr, sizeof(addr),
host, NI_MAXHOST,
service, NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV);
if (err == 0) {
printf("Numeric: IP=%s Port=%s\n", host, service);
}
}
int main() {
resolve_ipv4("8.8.8.8"); /* Google DNS */
return 0;
}
Example output:
IP 8.8.8.8 -> hostname: dns.google, service: http
Numeric: IP=8.8.8.8 Port=80
| Constant | Defined in | Typical value | Purpose |
|---|---|---|---|
| NI_MAXHOST | <netdb.h> | 1025 | Maximum buffer size needed for the hostname string returned by getnameinfo() |
| NI_MAXSERV | <netdb.h> | 32 | Maximum buffer size needed for the service name string returned by getnameinfo() |
| INET_ADDRSTRLEN | <arpa/inet.h> | 16 | Buffer size for an IPv4 address string (“255.255.255.255\0”) |
| INET6_ADDRSTRLEN | <arpa/inet.h> | 46 | Buffer size for an IPv6 address string (full expanded form) |
/* Correct buffer declarations for getnameinfo() */
char host[NI_MAXHOST]; /* 1025 bytes โ safe for any hostname */
char service[NI_MAXSERV]; /* 32 bytes โ safe for any service name */
getnameinfo(addr, addrlen, host, sizeof(host), service, sizeof(service), flags);
Like getaddrinfo(), getnameinfo() does NOT set errno. It returns its own error code. Use gai_strerror() to convert the error code to a string.
| Error Code | Meaning |
|---|---|
| EAI_AGAIN | Temporary DNS failure โ try again later. |
| EAI_BADFLAGS | Invalid flags in the flags argument. |
| EAI_FAIL | Permanent DNS failure. |
| EAI_FAMILY | Address family not supported. |
| EAI_MEMORY | Out of memory. |
| EAI_NONAME | No name found for the given address, and NI_NAMEREQD was set โ OR the host or service is NULL. |
| EAI_OVERFLOW | The provided host or service buffer is too small. |
char host[NI_MAXHOST], service[NI_MAXSERV];
int err;
err = getnameinfo(addr, addrlen, host, sizeof(host),
service, sizeof(service), 0);
switch (err) {
case 0:
printf("Host: %s Service: %s\n", host, service);
break;
case EAI_NONAME:
printf("No name found for this address\n");
break;
case EAI_AGAIN:
printf("Temporary failure โ try again\n");
break;
default:
fprintf(stderr, "getnameinfo error: %s\n", gai_strerror(err));
break;
}
Section A: getnameinfo() Questions
Q1. What is getnameinfo() and what does it do?
Answer: getnameinfo() converts a binary socket address structure (either IPv4 sockaddr_in or IPv6 sockaddr_in6) into human-readable strings โ a hostname and a service name. It is the modern replacement for the obsolete gethostbyaddr() and getservbyport() functions. It is protocol-independent (works for both IPv4 and IPv6) and thread-safe (reentrant).
Q2. What is the difference between NI_NUMERICHOST and NI_NAMEREQD?
Answer: NI_NUMERICHOST always returns the numeric IP address string without attempting a reverse DNS lookup. NI_NAMEREQD means “I require a hostname โ if you cannot find one, return an error (EAI_NONAME) rather than falling back to a numeric address”. They serve opposite goals: NI_NUMERICHOST actively avoids name resolution; NI_NAMEREQD makes name resolution mandatory.
Q3. Why would you use NI_DGRAM flag?
Answer: By default, getnameinfo() looks up the port number using the TCP protocol mapping in /etc/services. NI_DGRAM switches this lookup to use UDP. This matters for the rare ports where TCP and UDP have different service names โ for example, port 514 is “shell” (rsh) on TCP but “syslog” on UDP. If you know the socket is UDP-based, you should set NI_DGRAM to get the correct service name.
Q4. What is struct sockaddr_storage and when should you use it?
Answer: sockaddr_storage is a generic socket address structure that is large enough to hold any type of socket address โ either IPv4 (sockaddr_in, 16 bytes) or IPv6 (sockaddr_in6, 28 bytes). You should use it as the buffer type for accept() when writing protocol-independent code, because you don’t know at compile time whether the connecting client will use IPv4 or IPv6. After the address is filled in, you cast it to (struct sockaddr *) before passing to getnameinfo() or other functions.
Section B: Combined API Comparison Questions
Q5. What is the relationship between getaddrinfo() and getnameinfo()?
Answer: They are inverse functions. getaddrinfo() converts a hostname and service name (text) into socket address structures (binary). getnameinfo() converts a socket address structure (binary) back into hostname and service name (text). Together they form a complete, protocol-independent name resolution API that replaces four older functions: gethostbyname(), gethostbyaddr(), getservbyname(), and getservbyport().
Q6. Both getaddrinfo() and getnameinfo() may perform DNS lookups. What are the implications?
Answer: DNS lookups involve network communication and can take an unpredictable amount of time (milliseconds to several seconds). Both functions are blocking calls โ your program’s thread cannot do anything else while the DNS query is in progress. In performance-sensitive code (like a high-throughput server), you should: (1) cache results rather than calling these functions repeatedly for the same address, (2) use AI_NUMERICHOST / NI_NUMERICHOST flags when you don’t need name resolution, (3) consider doing name resolution in a separate thread.
Q7. What error-reporting functions should you use for getaddrinfo() and getnameinfo() errors?
Answer: Use gai_strerror(errorCode) for both functions. Neither getaddrinfo() nor getnameinfo() sets errno โ they return their own error codes (EAI_* constants). Using perror() would be wrong because it reads errno, which may be stale or set to something completely unrelated.
Section C: Conceptual and Design Questions
Q8. Walk me through writing a TCP server that is protocol-independent (works for both IPv4 and IPv6).
Answer: The steps are:
/* 1. Set up hints for a server socket */
struct addrinfo hints, *result;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* Accept IPv4 or IPv6 clients */
hints.ai_socktype = SOCK_STREAM; /* TCP */
hints.ai_flags = AI_PASSIVE; /* Server: bind to wildcard address */
/* 2. Resolve the port */
getaddrinfo(NULL, "port_number", &hints, &result);
/* 3. Create socket, set SO_REUSEADDR, bind, listen */
int sfd = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
int opt = 1;
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bind(sfd, result->ai_addr, result->ai_addrlen);
freeaddrinfo(result);
listen(sfd, BACKLOG);
/* 4. Accept connections โ use sockaddr_storage for client addr */
struct sockaddr_storage client_addr;
socklen_t client_len = sizeof(client_addr);
int connfd = accept(sfd, (struct sockaddr *)&client_addr, &client_len);
/* 5. Log client info with getnameinfo() */
char host[NI_MAXHOST], serv[NI_MAXSERV];
getnameinfo((struct sockaddr *)&client_addr, client_len,
host, NI_MAXHOST, serv, NI_MAXSERV,
NI_NUMERICHOST | NI_NUMERICSERV);
printf("Client: %s:%s\n", host, serv);
Q9. What is the difference between the wildcard address and the loopback address in the context of server sockets?
Answer: The wildcard address (0.0.0.0 for IPv4, :: for IPv6) means “accept connections arriving on any network interface.” A server bound to the wildcard address can receive connections from any client โ local or remote. The loopback address (127.0.0.1 for IPv4, ::1 for IPv6) means “only accept connections from the same machine.” A server bound to loopback cannot be reached from other machines on the network. getaddrinfo() returns the wildcard address when AI_PASSIVE is set with host = NULL, and the loopback address when AI_PASSIVE is NOT set with host = NULL.
Q10. Why is it important to loop through all getaddrinfo() results when trying to connect?
Answer: getaddrinfo() may return multiple addresses โ for example, an IPv6 address followed by an IPv4 address. The first address in the list may not be reachable (maybe the server has an IPv6 address but the network path between client and server has no IPv6 support). By looping and trying each address until one succeeds, your program automatically uses the best available address and falls back gracefully to alternatives. Trying only the first result could cause failures that would succeed if you tried a later result.
Q11. What header files are needed to use getaddrinfo() and getnameinfo()?
Answer: You need:
#include <sys/socket.h> /* sockaddr, socket() */
#include <netdb.h> /* getaddrinfo(), getnameinfo(), struct addrinfo, NI_* constants, gai_strerror() */
/* Also commonly needed: */
#include <arpa/inet.h> /* inet_pton(), inet_ntop(), htons(), ntohs() */
#include <string.h> /* memset() */
Q12. What does it mean to say getaddrinfo() is “reentrant”? Why does this matter?
Answer: A reentrant (thread-safe) function does not use shared global or static memory for its results. getaddrinfo() allocates its result structures dynamically on the heap (using malloc) and returns the pointer to the caller, who owns the memory and must free it with freeaddrinfo(). This means two threads can call getaddrinfo() simultaneously without interfering with each other’s results. The older gethostbyname() stored its result in a single static buffer inside the library โ if two threads called it at the same time, one thread would corrupt the other’s result. In multi-threaded servers (which is the norm today), this makes getaddrinfo() essential.
Q13. How does the AI_ADDRCONFIG flag interact with AI_V4MAPPED?
Answer: They are often used together: AI_ADDRCONFIG | AI_V4MAPPED. AI_ADDRCONFIG filters results to only return address types the local system actually has configured. AI_V4MAPPED provides a fallback: if AF_INET6 is requested and no native IPv6 addresses are found, return IPv4 addresses in IPv4-mapped format (::ffff:x.x.x.x). The combined effect on an IPv6-capable dual-stack system: return native IPv6 addresses. On an IPv4-only system: return IPv4 addresses in mapped format. This combination allows a single codebase to handle both network configurations gracefully.
| Function | Direction | Returns | Thread Safe? | DNS Lookup? |
|---|---|---|---|---|
| getaddrinfo() | name โ address | Linked list of addrinfo | โ Yes | Yes (for hostnames) |
| getnameinfo() | address โ name | Strings in caller buffers | โ Yes | Yes (for reverse lookup) |
| name โ address | Pointer to static struct | โ No | Yes (IPv4 only) | |
| address โ name | Pointer to static struct | โ No | Yes (IPv4 only) | |
| name โ port | Pointer to static struct | โ No | No (reads /etc/services) | |
| port โ name | Pointer to static struct | โ No | No (reads /etc/services) |
| AI_* Flags (getaddrinfo hints.ai_flags) | ||
|---|---|---|
| Flag | Purpose | Typical Use |
| AI_PASSIVE | Wildcard address for servers | Server bind() setup |
| AI_CANONNAME | Get official hostname | Logging/debugging |
| AI_NUMERICHOST | Host must be numeric IP | Skip DNS, known IP |
| AI_NUMERICSERV | Service must be numeric port | Skip /etc/services |
| AI_ADDRCONFIG | Only configured address types | Avoid unreachable IPv6 |
| AI_V4MAPPED | IPv4-mapped fallback for IPv6 queries | IPv6-only code reaching IPv4 |
| AI_ALL | With AI_V4MAPPED: return both IPv6 and mapped IPv4 | Dual-protocol queries |
| NI_* Flags (getnameinfo flags argument) | ||
|---|---|---|
| Flag | Effect on host | Effect on service |
| NI_NUMERICHOST | Return numeric IP string | No effect |
| NI_NUMERICSERV | No effect | Return port number string |
| NI_NOFQDN | Short hostname only (local domain) | No effect |
| NI_NAMEREQD | Fail if no hostname found | No effect |
| NI_DGRAM | No effect | Lookup as UDP service |
Chapter 59 Series Complete!
You have covered: /etc/services, getaddrinfo(), hints & AI_* flags, and getnameinfo()
โ Back to Part 1: /etc/services โ Part 3: AI_* Flags embeddedpathashala.com
