3.8 sock_ntop and Related Functions
A basic problem with inet_ntop is that it requires the caller to pass a pointer to a binary address. This address is normally contained in a socket address structure, requiring the caller to know the format of the structure and the address family. That is, to use it, we must write code of the form
struct sockaddr_in addr; inet_ntop(AF_INET, &addr.sin_addr, str, sizeof(str));
for IPv4, or
struct sockaddr_in6 addr6; inet_ntop(AF_INET6, &addr6.sin6_addr, str, sizeof(str));
for IPv6. This makes our code protocol-dependent.
To solve this, we will write our own function named sock_ntop that takes a pointer to a socket address structure, looks inside the structure, and calls the appropriate function to return the presentation format of the address.
#include "unp.h" |
char *sock_ntop(const struct sockaddr *sockaddr, socklen_t addrlen); |
Returns: non-null pointer if OK, NULL on error |
This is the notation we use for functions of our own (nonstandard system functions) that we use throughout the book: the box around the function prototype and return value is dashed. The header is included at the beginning is usually our unp.h header.
sockaddr points to a socket address structure whose length is addrlen. The function uses its own static buffer to hold the result and a pointer to this buffer is the return value.
-
Notice that using static storage for the result prevents the function from being re-entrant or thread-safe. We will talk more about this in Section 11.18. We made this design decision for this function to allow us to easily call it from the simple examples in the book.
The presentation format is the dotted-decimal form of an IPv4 address or the hex string form of an IPv6 address surrounded by brackets, followed by a terminator (we use a colon, similar to URL syntax), followed by the decimal port number, followed by a null character. Hence, the buffer size must be at least INET_ADDRSTRLEN plus 6 bytes for IPv4 (16 + 6 = 22), or INET6_ADDRSTRLEN plus 8 bytes for IPv6 (46 + 8 = 54).
We show the source code for only the AF_INET case in Figure 3.14.
Figure 3.14 Our sock_ntop function.
lib/sock_ntop.c
5 char * 6 sock_ntop(const struct sockaddr *sa, socklen_t salen) 7 { 8 char portstr[8]; 9 static char str[128]; /* Unix domain is largest */ 10 switch (sa->sa_family) { 11 case AF_INET:{ 12 struct sockaddr_in *sin = (struct sockaddr_in *) sa; 13 if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL) 14 return (NULL); 15 if (ntohs(sin->sin_port) != 0) { 16 snprintf(portstr, sizeof(portstr), ":%d", 17 ntohs(sin->sin_port)); 18 strcat(str, portstr); 19 } 20 return (str); 21 }
There are a few other functions that we define to operate on socket address structures, and these will simplify the portability of our code between IPv4 and IPv6.
#include "unp.h" |
int sock_bind_wild(int sockfd, int family); |
Returns: 0 if OK, -1 on error |
int sock_cmp_addr(const struct sockaddr *sockaddr1, |
const struct sockaddr *sockaddr2, socklen_t addrlen); |
Returns: 0 if addresses are of the same family and ports are equal, else nonzero |
int sock_cmp_port(const struct sockaddr *sockaddr1, |
const struct sockaddr *sockaddr2, socklen_t addrlen); |
Returns: 0 if addresses are of the same family and ports are equal, else nonzero |
int sock_get_port(const struct sockaddr *sockaddr, socklen_t addrlen); |
Returns: non-negative port number for IPv4 or IPv6 address, else -1 |
char *sock_ntop_host(const struct sockaddr *sockaddr, socklen_t addrlen); |
Returns: non-null pointer if OK, NULL on error |
void sock_set_addr(const struct sockaddr *sockaddr, socklen_t addrlen, void *ptr); |
void sock_set_port(const struct sockaddr *sockaddr, socklen_t addrlen, int port); |
void sock_set_wild(struct sockaddr *sockaddr, socklen_t addrlen); |
sock_bind_wild binds the wildcard address and an ephemeral port to a socket. sock_cmp_addr compares the address portion of two socket address structures, and sock_cmp_port compares the port number of two socket address structures. sock_get_port returns just the port number, and sock_ntop_host converts just the host portion of a socket address structure to presentation format (not the port number). sock_set_addr sets just the address portion of a socket address structure to the value pointed to by ptr, and sock_set_port sets just the port number of a socket address structure. sock_set_wild sets the address portion of a socket address structure to the wildcard. As with all the functions in the text, we provide a wrapper function whose name begins with "S" for all of these functions that return values other than void and normally call the wrapper function from our programs. We do not show the source code for all these functions, but it is freely available (see the Preface).