Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the sin_family member exist?

Tags:

c

struct

sockets

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?

like image 285
duper21 Avatar asked Jan 26 '23 21:01

duper21


1 Answers

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;
}
like image 101
aschepler Avatar answered Feb 08 '23 15:02

aschepler