Reverse Lookup
getnameinfo
NI_* constants
What Is getnameinfo?
getaddrinfo() converts a hostname and service name into socket addresses. getnameinfo() does the opposite — it takes a socket address structure (the kind you get from accept(), recvfrom(), or getpeername()) and gives you back a human-readable hostname and service name. This is called reverse lookup.
It is the modern replacement for the older gethostbyaddr() and getservbyport() functions. It works for both IPv4 and IPv6.
Function Signature
#include <sys/socket.h>
#include <netdb.h>
int getnameinfo(const struct sockaddr *addr, /* socket address to convert */
socklen_t addrlen, /* size of that structure */
char *host, /* output buffer: hostname */
size_t hostlen, /* size of host buffer */
char *service, /* output buffer: service name */
size_t servlen, /* size of service buffer */
int flags); /* behaviour flags (NI_*) */
/* Returns 0 on success, nonzero EAI_* error code on failure */
| struct sockaddr * from accept() / recvfrom() / getpeername() |
→ | getnameinfo() DNS reverse lookup + /etc/services lookup |
→ | char *host “example.com” char *service |
Arguments Explained
| Argument | Type | Description |
|---|---|---|
| addr | const struct sockaddr * | The socket address to look up. Works for IPv4 and IPv6. |
| addrlen | socklen_t | Size of the addr structure (sizeof(struct sockaddr_in) or sizeof(struct sockaddr_in6)). |
| host | char * | Caller-allocated buffer to receive hostname. Pass NULL if not needed. |
| hostlen | size_t | Size of the host buffer. Use NI_MAXHOST (1025). Set to 0 if host is NULL. |
| service | char * | Caller-allocated buffer to receive service name. Pass NULL if not needed. |
| servlen | size_t | Size of the service buffer. Use NI_MAXSERV (32). Set to 0 if service is NULL. |
| flags | int | Bitmask of NI_* constants that control behaviour. |
Critical Rule: At least one of host and service must be non-NULL. You cannot pass NULL for both.
| Constant | Value | Purpose |
|---|---|---|
| NI_MAXHOST | 1025 bytes | Maximum bytes for a returned hostname string (including NUL terminator) |
| NI_MAXSERV | 32 bytes | Maximum bytes for a returned service name string (including NUL terminator) |
These constants are defined in <netdb.h> but are not in SUSv3. On Linux (glibc ≥ 2.8) you need to define _GNU_SOURCE, _BSD_SOURCE, or _SVID_SOURCE before including <netdb.h> to get them.
#define _GNU_SOURCE
#include <netdb.h>
char host[NI_MAXHOST]; /* 1025 bytes — always large enough for any hostname */
char serv[NI_MAXSERV]; /* 32 bytes — always large enough for any service */
The flags argument is a bitmask. You can OR multiple flags together. Here is what each flag does:
| Flag | Effect When Set | When To Use |
|---|---|---|
| NI_DGRAM | Return UDP service name instead of TCP | When your socket is SOCK_DGRAM (UDP). Port 512 is “exec” for TCP but “biff” for UDP — they differ. |
| NI_NAMEREQD | Fail with EAI_NONAME if hostname cannot be resolved | When you must have a real hostname, never a numeric fallback — e.g., security logging. |
| NI_NOFQDN | Return only the short hostname (no domain suffix) for local hosts | When displaying names to local users and the full domain adds no value. |
| NI_NUMERICHOST | Always return numeric IP address string, skip DNS | Performance-sensitive code where a DNS round-trip is unacceptable. |
| NI_NUMERICSERV | Always return decimal port number string, skip /etc/services | Ephemeral ports assigned by the kernel — no service name exists for them. |
Flag Decision Guide
| Need fastest response? | Use NI_NUMERICHOST | NI_NUMERICSERV — no DNS, no /etc/services lookup |
| Socket is UDP? | Always add NI_DGRAM to get the correct UDP service name |
| Logging connections? | Use NI_NUMERICHOST | NI_NUMERICSERV — numeric is unambiguous and instant |
| Must have a real hostname? | Add NI_NAMEREQD — function returns an error instead of a numeric fallback |
| Showing to local users? | Add NI_NOFQDN — strips .corp.example.com suffix for cleaner display |
Example 1 — Basic: Who Connected?
A server calls accept() and wants to print the connecting client’s IP address and port.
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void print_peer_info(int conn_fd)
{
struct sockaddr_storage peer_addr;
socklen_t peer_len = sizeof(peer_addr);
char host[NI_MAXHOST];
char serv[NI_MAXSERV];
/* Get the peer's address */
if (getpeername(conn_fd,
(struct sockaddr *)&peer_addr,
&peer_len) == -1) {
perror("getpeername");
return;
}
/* Convert to strings — skip DNS for speed */
int ret = getnameinfo((struct sockaddr *)&peer_addr,
peer_len,
host, sizeof(host),
serv, sizeof(serv),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ret != 0) {
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(ret));
return;
}
printf("Connection from %s port %s\n", host, serv);
}
Example 2 — Reverse DNS Lookup
Given an IPv4 address in string form, find its hostname.
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main(void)
{
const char *ip = "8.8.8.8";
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &addr.sin_addr);
char host[NI_MAXHOST];
/* Request only the hostname — service is not needed */
int ret = getnameinfo((struct sockaddr *)&addr,
sizeof(addr),
host, sizeof(host),
NULL, 0, /* no service buffer */
0); /* default: try DNS, fall back to numeric */
if (ret != 0) {
fprintf(stderr, "getnameinfo: %s\n", gai_strerror(ret));
return 1;
}
printf("%s resolves to: %s\n", ip, host);
return 0;
}
Example 3 — Require a Real Hostname (NI_NAMEREQD)
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main(void)
{
/* 192.0.2.1 is a documentation IP — unlikely to have reverse DNS */
const char *ip = "192.0.2.1";
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
inet_pton(AF_INET, ip, &addr.sin_addr);
char host[NI_MAXHOST];
int ret = getnameinfo((struct sockaddr *)&addr,
sizeof(addr),
host, sizeof(host),
NULL, 0,
NI_NAMEREQD); /* FAIL if no DNS entry */
if (ret != 0) {
/* EAI_NONAME will be returned for unresolvable addresses */
fprintf(stderr, "No hostname for %s: %s\n", ip, gai_strerror(ret));
return 1;
}
printf("Hostname: %s\n", host);
return 0;
}
Example 4 — UDP Service Name (NI_DGRAM)
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void show_service(uint16_t port_net, int use_udp)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = port_net; /* already in network byte order */
char serv[NI_MAXSERV];
int flags = NI_NUMERICHOST; /* skip DNS for the IP part */
if (use_udp)
flags |= NI_DGRAM; /* get UDP service name */
getnameinfo((struct sockaddr *)&addr,
sizeof(addr),
NULL, 0, /* skip host */
serv, sizeof(serv),
flags);
printf("Port 512 as %s service: %s\n",
use_udp ? "UDP" : "TCP", serv);
}
int main(void)
{
/* Port 512: "exec" for TCP, "biff" for UDP */
uint16_t port = htons(512);
show_service(port, 0); /* TCP */
show_service(port, 1); /* UDP */
return 0;
}
Q1. What is the difference between getaddrinfo() and getnameinfo()?
getaddrinfo() converts a hostname and service name into socket address structures (forward lookup). getnameinfo() does the reverse — it converts a socket address structure into a hostname and service name string (reverse lookup). Together they form a protocol-independent name resolution API that handles both IPv4 and IPv6.
Q2. When would you use NI_NUMERICHOST and when would you not?
Use NI_NUMERICHOST when you want the numeric IP address string (e.g., “192.168.1.5”) and want to avoid the delay of a DNS reverse lookup. Skip it when you genuinely need a hostname — for example, to display a friendly name to a human operator. In performance-sensitive server loops (like logging thousands of connections per second), always prefer NI_NUMERICHOST | NI_NUMERICSERV.
Q3. Why does port 512 have different service names for TCP and UDP?
Port 512/TCP is the “exec” service (remote process execution). Port 512/UDP is the “biff” service (mail notification). They were assigned to completely different applications. Without NI_DGRAM, getnameinfo() returns the TCP service name by default, which would be wrong for a UDP socket.
Q4. What is sockaddr_storage and why do servers use it with getnameinfo()?
struct sockaddr_storage is large enough to hold any socket address — whether IPv4 (sockaddr_in) or IPv6 (sockaddr_in6). Servers use it as the buffer passed to accept() or recvfrom() so they can receive connections from either protocol family. The same pointer is then passed directly to getnameinfo().
Q5. What happens if NI_NAMEREQD is set and the reverse DNS lookup fails?
getnameinfo() returns the error code EAI_NONAME. Without this flag, it would fall back to returning a numeric address string — which is usually fine but occasionally undesirable (e.g., when you are enforcing that only hosts with proper DNS entries may connect).
Q6. Why is NI_NOFQDN only relevant for hosts on the local network?
For remote hosts, the function always returns a fully qualified name regardless. For a host on the same network, the DNS would normally return “server.corp.example.com”. With NI_NOFQDN, only “server” is returned. This makes displayed names shorter and more readable in local contexts.
