Linux Sockets: Internet Domains getnameinfo()

 

Linux Sockets: Internet Domains
Chapter 59 Part 6 — File 2 of 4  |  getnameinfo()
Topic
Reverse Lookup
API
getnameinfo
Flags
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.

Key Terms
getnameinfo reverse DNS NI_DGRAM NI_NAMEREQD NI_NOFQDN NI_NUMERICHOST NI_NUMERICSERV NI_MAXHOST NI_MAXSERV FQDN

59.10.4   The getnameinfo() Function

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
“http”

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.

Buffer Size Constants — NI_MAXHOST & NI_MAXSERV
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 NI_* Flags — Controlling getnameinfo Behaviour

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

Code Examples

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;
}
sockaddr_storage is the universal container NI_NUMERICHOST avoids DNS latency At least one of host/service must be non-NULL

Interview Questions — getnameinfo()

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.

Next: TCP Client-Server Example

File 3 covers the full sequence-number server and client design.

File 3 → ← File 1

Leave a Reply

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