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).
|