Sockets: Internet Domains   inet_pton() & inet_ntop() – Binary ↔ Presentation Address Conversion

 

Chapter 59 – Sockets: Internet Domains
Part 1 of 3  |  inet_pton() & inet_ntop() – Binary ↔ Presentation Address Conversion
📡 Topic
Address Conversion
🎯 Level
Intermediate
📘 Source
TLPI Ch. 59

What Is This About?

When you write a socket program, IP addresses exist in two forms:

  • Binary form – what the kernel and network stack actually use (stored in struct in_addr or struct in6_addr)
  • Presentation form – what humans read, like 192.168.1.10 or ::1

You need to convert between these two forms constantly. The two modern functions for this are inet_pton() (presentation → network binary) and inet_ntop() (network binary → presentation). The “p” means presentation; the “n” means network.

These functions handle both IPv4 and IPv6, unlike the older inet_aton() / inet_ntoa() pair which only handled IPv4.

Key Terms

inet_pton inet_ntop AF_INET AF_INET6 in_addr in6_addr INET_ADDRSTRLEN INET6_ADDRSTRLEN dotted-decimal hex-string network byte order presentation format

1. Presentation vs Binary – What Is the Difference?

An IPv4 address like 204.152.189.116 is easy for you to type and read. But internally, the Linux kernel stores it as a 32-bit integer in network byte order (big-endian). An IPv6 address like ::1 is stored as a 128-bit value in struct in6_addr.

The three presentation formats you will encounter:

Format Example IP Version
Dotted-decimal 204.152.189.116 IPv4
Colon-hex ::1 IPv6
IPv4-mapped IPv6 ::FFFF:204.152.189.116 IPv6 (wraps IPv4)

The flow of conversion:

Presentation String
“192.168.1.10”
inet_pton() Binary (in_addr)
0xC0A8010A
Binary (in_addr)
0xC0A8010A
inet_ntop() Presentation String
“192.168.1.10”

2. Function Signatures and Return Values

Both functions are declared in <arpa/inet.h>.

#include <arpa/inet.h>

/* Presentation string --> Binary */
int inet_pton(int domain, const char *src_str, void *addrptr);
/*
 * domain  : AF_INET (IPv4) or AF_INET6 (IPv6)
 * src_str : e.g. "192.168.1.10" or "::1"
 * addrptr : pointer to struct in_addr  (for AF_INET)
 *           pointer to struct in6_addr (for AF_INET6)
 *
 * Returns:
 *   1  -> success
 *   0  -> src_str not in valid presentation format
 *  -1  -> error (check errno)
 */

/* Binary --> Presentation string */
const char *inet_ntop(int domain, const void *addrptr,
                      char *dst_str, size_t len);
/*
 * domain  : AF_INET or AF_INET6
 * addrptr : pointer to struct in_addr or struct in6_addr
 * dst_str : output buffer for the string
 * len     : size of dst_str buffer
 *
 * Returns:
 *   pointer to dst_str -> success
 *   NULL               -> error (errno = ENOSPC if buffer too small)
 */

Notice that inet_pton() returns three different values: 1, 0, or -1. Always check all three. Returning 0 means the string was not a valid IP address — that is not an error in the Unix sense, errno is not set in that case.

3. Buffer Size Constants – INET_ADDRSTRLEN & INET6_ADDRSTRLEN

When using inet_ntop() you must provide an output buffer. To avoid guessing the size, use the two constants defined in <netinet/in.h>:

#include <netinet/in.h>

#define INET_ADDRSTRLEN  16   /* Max IPv4 dotted-decimal string length */
                              /* e.g. "255.255.255.255\0" = 16 bytes   */

#define INET6_ADDRSTRLEN 46   /* Max IPv6 hex string length            */
                              /* includes colons, \0 terminator        */
Constant Value Covers Use With
INET_ADDRSTRLEN 16 IPv4 dotted-decimal + null AF_INET
INET6_ADDRSTRLEN 46 IPv6 colon-hex + null AF_INET6

Always use these constants instead of hardcoding numbers like char buf[50]. It makes your code portable and self-documenting.

4. Complete Coding Examples

Example 1 – inet_pton() with IPv4

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>

int main(void)
{
    struct in_addr addr;   /* 32-bit IPv4 binary */
    int result;

    const char *ipstr = "192.168.1.10";

    result = inet_pton(AF_INET, ipstr, &addr);

    if (result == 1) {
        /* addr.s_addr now holds the IPv4 address in network byte order */
        printf("Conversion OK. Binary (hex) = 0x%08X\n",
               ntohl(addr.s_addr));   /* ntohl for display only */
    } else if (result == 0) {
        printf("Not a valid IPv4 presentation string: %s\n", ipstr);
    } else {
        perror("inet_pton");
    }

    return 0;
}
/* Output:
 * Conversion OK. Binary (hex) = 0xC0A8010A
 */

Example 2 – inet_ntop() with IPv4

#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(void)
{
    struct in_addr addr;
    char buf[INET_ADDRSTRLEN];   /* Use the constant, not a magic number */

    /* Manually set binary address: 8.8.8.8 */
    addr.s_addr = htonl(0x08080808);

    if (inet_ntop(AF_INET, &addr, buf, sizeof(buf)) == NULL) {
        perror("inet_ntop");
        return 1;
    }

    printf("IP address string: %s\n", buf);
    /* Output: IP address string: 8.8.8.8 */

    return 0;
}

Example 3 – inet_pton() and inet_ntop() with IPv6

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(void)
{
    struct in6_addr addr6;
    char buf[INET6_ADDRSTRLEN];

    const char *ipv6str = "2001:db8::1";

    /* Step 1: presentation --> binary */
    if (inet_pton(AF_INET6, ipv6str, &addr6) != 1) {
        fprintf(stderr, "inet_pton failed\n");
        return 1;
    }

    /* Step 2: binary --> presentation (round-trip check) */
    if (inet_ntop(AF_INET6, &addr6, buf, sizeof(buf)) == NULL) {
        perror("inet_ntop");
        return 1;
    }

    printf("Original : %s\n", ipv6str);
    printf("Round-trip: %s\n", buf);
    /* Note: kernel may normalize, e.g. "2001:db8::1" stays "2001:db8::1" */

    return 0;
}

Example 4 – Practical logging use case

A common real-world use: you receive a connection and want to log the client’s IP without doing a DNS lookup (which is slow).

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void log_client_address(struct sockaddr_in *client_addr)
{
    char ip_str[INET_ADDRSTRLEN];

    if (inet_ntop(AF_INET,
                  &client_addr->sin_addr,
                  ip_str,
                  sizeof(ip_str)) == NULL) {
        /* Fall back gracefully */
        snprintf(ip_str, sizeof(ip_str), "(unknown)");
    }

    printf("[LOG] Client connected from %s port %d\n",
           ip_str,
           ntohs(client_addr->sin_port));
}

This is exactly the pattern used in the TLPI i6d_ucase_sv.c server shown in the next part of this chapter.

5. Why Prefer inet_pton/ntop Over the Old inet_aton/ntoa?
Function IPv4 Only? Thread-Safe? Status
inet_aton() Yes Yes Old, limited
inet_ntoa() Yes No (static buffer) Old, avoid
inet_pton() No (IPv4 + IPv6) Yes Modern ✔
inet_ntop() No (IPv4 + IPv6) Yes (caller buffer) Modern ✔

inet_ntoa() is particularly dangerous in multi-threaded programs because it uses an internal static buffer — two threads calling it simultaneously will corrupt each other’s result. inet_ntop() takes a caller-supplied buffer, so it is completely thread-safe.

6. When to Use inet_ntop vs getaddrinfo / getnameinfo?
Situation Use Reason
Log an IP address quickly inet_ntop() No DNS lookup, fast
No DNS PTR record exists inet_ntop() getnameinfo would fail
Need hostname from IP getnameinfo() Does reverse DNS lookup
Connect to a named host getaddrinfo() Resolves name to binary address

7. Interview Questions & Answers
Q1. What does the “p” and “n” stand for in inet_pton and inet_ntop?

A: “p” stands for presentation (human-readable string like “192.168.1.1”) and “n” stands for network (binary form in network byte order). inet_pton converts presentation → network; inet_ntop converts network → presentation.

Q2. What are the three possible return values of inet_pton() and what do they mean?

A:
1 → Conversion successful.
0 → The string was not a valid presentation-format address (errno is NOT set).
-1 → An error occurred (errno is set, e.g., invalid domain).

Q3. Why is inet_ntoa() not safe in multi-threaded programs?

A: inet_ntoa() stores its result in a static (shared) internal buffer. If two threads call it at the same time, one will overwrite the other’s result, causing a race condition. inet_ntop() is safe because the caller provides the output buffer.

Q4. What is the value of INET_ADDRSTRLEN and INET6_ADDRSTRLEN? Why do we need them?

A: INET_ADDRSTRLEN = 16 (max IPv4 dotted-decimal + null terminator). INET6_ADDRSTRLEN = 46 (max IPv6 colon-hex + null terminator). They are used to correctly size the buffer passed to inet_ntop(), avoiding buffer overflows and magic numbers in code.

Q5. Which header files are needed for inet_pton() and inet_ntop()?

A: #include <arpa/inet.h> for the function declarations. #include <netinet/in.h> for struct in_addr, struct in6_addr, and the INET_ADDRSTRLEN / INET6_ADDRSTRLEN constants.

Q6. When should you prefer inet_ntop() over getnameinfo() for logging?

A: Use inet_ntop() when: (1) speed matters — DNS reverse lookup can take hundreds of milliseconds; (2) no DNS PTR record exists for the IP address; (3) DNS is unavailable. inet_ntop() is a pure local conversion with no network I/O.

Q7. What structure does addrptr point to for AF_INET vs AF_INET6?

A: For AF_INET, addrptr points to struct in_addr (contains s_addr, a 32-bit IPv4 address). For AF_INET6, it points to struct in6_addr (contains s6_addr, a 128-bit IPv6 address as a byte array).

Q8. What happens if the buffer passed to inet_ntop() is too small?

A: inet_ntop() returns NULL and sets errno to ENOSPC. The function does not write a partial result to the buffer.

8. Quick Reference Summary
Item Detail
Header <arpa/inet.h>
inet_pton direction String → Binary
inet_ntop direction Binary → String
IPv4 buffer size INET_ADDRSTRLEN = 16
IPv6 buffer size INET6_ADDRSTRLEN = 46
Domains supported AF_INET, AF_INET6
Thread-safe? Yes (caller provides buffer)

Next: Host & Service Name Resolution APIs

Learn why gethostbyname() is obsolete and how getaddrinfo() replaces it.

Part 2 → embeddedpathashala.com

Leave a Reply

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