If I am using the struct sockaddr_in, the address family is already specified in any sockets I create.
Furthermore,it seems redundant to use the sin_family member if it must always be AF_INET.
So why does the sin_family member exist?
It's true the sin_family
member of struct sockaddr_in
must always be AF_INET
. The reason for this is so that it's possible to tell the actual type of a generalized socket descriptor.
struct sockaddr_in
is one of several types of socket address descriptors, with different members depending on the network protocol:
struct sockaddr_in {
sa_family_t sin_family; /* always AF_INET */
in_port_t sin_port;
struct in_addr sin_addr;
};
struct sockaddr_in6 {
sa_family_t sin6_family; /* always AF_INET6 */
in_port_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
struct sockaddr_un {
sa_family_t sun_family; /* always AF_UNIX */
char sun_path[108];
};
/* and more ... */
Some POSIX functions like bind
and accept
take a pointer to struct sockaddr
, a dummy type that just has the initial sa_family_t sa_family;
member. So they, or other code, can deal with any one of those structs cast to a [const
] struct sockaddr*
pointer by checking the protocol family, and then casting back to the actual type based on that value.
For example, a logging wrapper around bind
might do:
int loggable_bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen)
{
bool do_log = logging_enabled();
if (do_log) {
switch (addr->sa_family) {
case AF_INET: {
const struct sockaddr_in* addr_in = (const struct sockaddr_in*) addr;
printf("bind: fd=%d family=AF_INET addr=%s port=%u",
sockfd,
inet_ntoa(addr_in->sin_addr),
(unsigned) addr_in->sin_port);
break;
}
case AF_INET6: {
const struct sockaddr_in6* addr_in6 = (const struct sockaddr_in6*) addr;
printf("bind: fd=%d family=AF_INET6 addr=%.*s port=%u",
sockfd,
sizeof(addr_in6->sin6_addr.s6_addr),
(const char*) addr_in6->sin6_addr.s6_addr,
(unsigned) addr6_in->sin6_port);
break;
}
case AF_UNIX: {
const struct sockaddr_un* addr_un = (const struct sockaddr_un*) addr;
if (addrlen == sizeof(sa_family_t)) {
printf("bind: fd=%d family=AF_UNIX Unnamed");
} else if (addr_un->sun_path[0] == '\0') {
printf("bind: fd=%d family=AF_UNIX Abstract, path='");
for (size_t i=1; i<(addrlen-offsetof(struct sockaddr_un, sun_path)); ++i) {
int c = (unsigned char) addr_un->sun_path[i];
if (isprint(c))
putchar(c);
else
printf("\\x%02x", (unsigned) c);
}
putchar('\'');
} else {
printf("bind: fd=%d family=AF_UNIX Named, path=%s",
fd, addr_un->sun_path);
}
break;
}
default:
printf("bind: fd=%d family=%u", fd, (unsigned) addr->sa_family);
break;
}
}
int result = bind(fd, addr, addrlen);
if (do_log) {
int local_err = errno;
printf(" -> %d\n", result);
if (result < 0)
printf(" errno=%d: %s\n", local_err, strerror(local_err));
}
return result;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With