Beej's Guide to Network Programming
12
What's important is that you can see the address family in the ss_family field—check this to see
if it's AF_INET or AF_INET6 (for IPv4 or IPv6). Then you can cast it to a struct sockaddr_in or
struct sockaddr_in6 if you wanna.
3.4. IP Addresses, Part Deux
Fortunately for you, there are a bunch of functions that allow you to manipulate IP addresses. No
need to figure them out by hand and stuff them in a long with the << operator.
First, let's say you have a struct sockaddr_in ina, and you have an IP address
“10.12.110.57” or “2001:db8:63b3:1::3490” that you want to store into it. The function you
want to use, inet_pton(), converts an IP address in numbers-and-dots notation into either a struct
in_addr or a struct in6_addr depending on whether you specify AF_INET or AF_INET6. (“pton”
stands for “presentation to network”—you can call it “printable to network” if that's easier to remember.)
The conversion can be made as follows:
struct sockaddr_in sa; // IPv4
struct sockaddr_in6 sa6; // IPv6
inet_pton(AF_INET, "192.0.2.1", &(sa.sin_addr)); // IPv4
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IPv6
(Quick note: the old way of doing things used a function called inet_addr() or another function
called inet_aton(); these are now obsolete and don't work with IPv6.)
Now, the above code snippet isn't very robust because there is no error checking. See,
inet_pton() returns -1 on error, or 0 if the address is messed up. So check to make sure the result is
greater than 0 before using!
All right, now you can convert string IP addresses to their binary representations. What about the
other way around? What if you have a struct in_addr and you want to print it in numbers-and-
dots notation? (Or a struct in6_addr that you want in, uh, “hex-and-colons” notation.) In this case,
you'll want to use the function inet_ntop() (“ntop” means “network to presentation”—you can call it
“network to printable” if that's easier to remember), like this:
// IPv4:
char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string
struct sockaddr_in sa; // pretend this is loaded with something
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is: %s\n", ip4);
// IPv6:
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 sa6; // pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The address is: %s\n", ip6);
When you call it, you'll pass the address type (IPv4 or IPv6), the address, a pointer to a string
to hold the result, and the maximum length of that string. (Two macros conveniently hold the
size of the string you'll need to hold the largest IPv4 or IPv6 address: INET_ADDRSTRLEN and
INET6_ADDRSTRLEN.)
(Another quick note to mention once again the old way of doing things: the historical function to do
this conversion was called inet_ntoa(). It's also obsolete and won't work with IPv6.)
Lastly, these functions only work with numeric IP addresses—they won't do any nameserver DNS
lookup on a hostname, like “www.example.com”. You will use getaddrinfo() to do that, as you'll see
later on.