Search code examples
c++unionssockaddr

sockaddr structures as union


I'm looking for a summary of the socket address structures interpreted as a union to have a complete overview. The only Q&A similar to this I've found is Sockaddr union and getaddrinfo() but it doesn't summarizes the structures.

How does a union of the socket address structures looks like and what address structures in detail are used?


Solution

  • I'm missing a handy summary for the socket address structures. Yes, I know there are a great amount of examples on the web but couldn't find one that gives me all in one place.

    The socket address structures are made to overlap each other in memory. So they can be declared as a union type. By specification, the sockaddr_storage structure is big enough to hold all structures so it determines the size of the union. With using the union there is no need for type casting anymore (except (sockaddr*) for function arguments).

    using sockaddr_t = union {
        sockaddr_storage ss;
        sockaddr_in6     sin6;
        sockaddr_in      sin;
        sockaddr         sa;
    };
    
    struct sockaddr_storage {
        sa_family_t      ss_family;
        // Following field(s) are implementation specific
        // only for padding to mostly 128 bytes.
        // No information usable.
    };
    
    struct sockaddr_in6 {
        sa_family_t      sin6_family;   // AF_INET6.
        in_port_t        sin6_port;     // Port number.
        uint32_t         sin6_flowinfo; // IPv6 traffic class and flow info.
        struct in6_addr  sin6_addr;     // IPv6 address.
        uint32_t         sin6_scope_id; // Set of interfaces for a scope.
    };
    
    struct sockaddr_in {
        sa_family_t      sin_family;    // AF_INET.
        in_port_t        sin_port;      // Port number.
        struct in_addr   sin_addr;      // IP address.
    };
    
    struct sockaddr {
        sa_family_t      sa_family;     // Address family.
        // char          sa_data[];     // Socket address (variable-length data).
    };
    
    struct in6_addr {
        uint8_t s6_addr[16];
    };
    
    struct in_addr {
        in_addr_t s_addr;
    };
    
    using sa_family_t = unsigned short int;
    using in_addr_t = uint32_t;
    using in_port_t = uint16_t;
    

    Examples:

    #include <netdb.h> // for sockaddr structures
    #include <cstring> // for memcmp()
    #include <cassert>
    
    int main() {
        sockaddr_t saddr{};
    
        assert(saddr.ss.ss_family == saddr.sin6.sin6_family);
        assert(saddr.ss.ss_family == saddr.sin.sin_family);
        assert(saddr.ss.ss_family == saddr.sa.sa_family);
    
        if (saddr.ss.ss_family == AF_INET6) {
            in_port_t port6 = saddr.sin6.sin6_port;
            in6_addr addr6 = saddr.sin6.sin6_addr;
    
            // Check if sin6_addr is null
            uint8_t s6_addr0[16]{};
            bool res =
                (memcmp(saddr.sin6.sin6_addr.s6_addr, s6_addr0, sizeof(s6_addr0)) == 0);
        }
    
       if (saddr.ss.ss_family == AF_INET) {
            in_port_t port = saddr.sin.sin_port;
            in_addr_t addr = saddr.sin.sin_addr.s_addr;
        }
    
        // sockaddr is only used for type casting on function arguments
        if (saddr.ss.ss_family == AF_INET6) {
            int sockfd = socket(saddr.ss.ss_family, SOCK_DGRAM, 0);
            socklen_t len = sizeof(saddr.sin6);
            int ret = getsockname(sockfd, (sockaddr*)&saddr, &len);
        }
    }
    

    A more useful example you can find at Sockaddr union and getaddrinfo().

    References: