~donmcc/ood

ab44f3628e9ee09390f462acef02a64ee99d7b3f — Don McCaughey 1 year, 10 months ago f4284ef
Don't dynamically allocate `sa` unions.

In `ood_inet_socket`, change the `address` field for a pointer to an
`sa` union.

In the `sa` library, replace the various `sa_alloc_from_x()` functions with
corresponding `sa_from_x()` functions that take an `sa` pointer as their
first parameter and return a boolean to indicate success or failure.

In place of `sa_alloc()`, add `sa_unspec()` to clear an `sa` union and
set its address family to `AF_UNSPEC`.

Drop the functions `sa_accept_alloc()`, sa_alloc_getpeername()`,
`sa_alloc_getsockname()`, `sa_bind()` and `sa_connect()` in favor of
using the corresponding standard functions directly.

Add `sa_prepare()` to initialize an `sa` union and a `socklen_t` length
variable for receiving a socket address from functions like `connect()`
and `getpeername()`.
M src/dood/main.c => src/dood/main.c +3 -3
@@ 34,7 34,7 @@ connection_on_error(struct ood_inet_socket *connection)
    assert(connection->data);
    struct server *server = connection->data;

    fprintf(stderr, "dood: error on connection to %s\n", sa_str(connection->address));
    fprintf(stderr, "dood: error on connection to %s\n", sa_str(&connection->address));
    server_close_socket(server, connection);
}



@@ 109,7 109,7 @@ listener_on_error(struct ood_inet_socket *listener)
    assert(listener->data);
    struct server *server = listener->data;

    fprintf(stderr, "dood: error listening on %s\n", sa_str(listener->address));
    fprintf(stderr, "dood: error listening on %s\n", sa_str(&listener->address));
    server_close_socket(server, listener);
}



@@ 134,7 134,7 @@ listener_on_read(struct ood_inet_socket *listener)
        connection->data = server;

        server_add_socket(server, connection);
        printf("dood: accepted connection from %s\n", sa_str(connection->address));
        printf("dood: accepted connection from %s\n", sa_str(&connection->address));
    }
}


M src/dood/server.c => src/dood/server.c +2 -2
@@ 34,7 34,7 @@ server_add_listener(struct server *server,
        return ood_error;
    }

    printf("dood: listening on %s\n", sa_str(listener->address));
    printf("dood: listening on %s\n", sa_str(&listener->address));
    return ood_okay;
}



@@ 85,7 85,7 @@ server_close_all_listeners(struct server *server)
        if (!server->sockets->items[i]) continue;
        struct ood_inet_socket *socket = (struct ood_inet_socket *)server->sockets->items[i];
        if (OOD_INET_SOCKET_FLAGS_LISTENING & socket->flags) {
            printf("dood: no longer listening on %s\n", sa_str(socket->address));
            printf("dood: no longer listening on %s\n", sa_str(&socket->address));
            ood_inet_socket_free(socket);
            server->sockets->items = NULL;
            --server->sockets->count;

M src/libood/ood/inet/socket.c => src/libood/ood/inet/socket.c +8 -11
@@ 17,7 17,7 @@ alloc_connection(int socket, union sa *address)
    if (!connection) return NULL;

    connection->fd = socket;
    connection->address = address;
    connection->address = *address;

    return connection;
}


@@ 37,12 37,14 @@ ood_inet_socket_accept(struct ood_inet_socket *listener, struct ood_inet_socket 
    assert(listener);
    assert(connection_out);

    union sa *client_address;
    union sa client_address;
    socklen_t len;
    sa_prepare(&client_address, &len);

    errno = 0;
    int fd = sa_accept_alloc(listener->fd, &client_address);
    int fd = accept(listener->fd, &client_address.generic, &len);
    if (fd >= 0) {
        *connection_out = alloc_connection(fd, client_address);
        *connection_out = alloc_connection(fd, &client_address);
        if (*connection_out) {
            return ood_okay;
        } else {


@@ 66,11 68,7 @@ ood_inet_socket_alloc(in_addr_t in_addr, in_port_t in_port)
    if (!listener) return NULL;

    listener->fd = -1;
    listener->address = sa_alloc_from_in_addr_and_port(in_addr, in_port);
    if (!listener->address) {
        free(listener);
        return NULL;
    }
    sa_from_in_addr_and_port(&listener->address, in_addr, in_port);

    return listener;
}


@@ 81,7 79,6 @@ ood_inet_socket_free(struct ood_inet_socket *socket)
{
    if (socket) {
        if (socket->fd >= 0) close(socket->fd);
        free(socket->address);
        free(socket);
    }
}


@@ 111,7 108,7 @@ ood_inet_socket_listen(struct ood_inet_socket *listener, int backlog)
        return ood_error;
    }

    int result = sa_bind(listener->fd, listener->address);
    int result = bind(listener->fd, &listener->address.generic, sa_len(&listener->address));
    if (result < 0) {
        close_socket(listener);
        return ood_error;

M src/libood/ood/inet/socket.h => src/libood/ood/inet/socket.h +2 -1
@@ 3,6 3,7 @@


#include <netinet/in.h>
#include <sa.h>


#define OOD_INET_SOCKET_FLAGS_LISTENING 0x00000001


@@ 19,7 20,7 @@ typedef void
struct ood_inet_socket {
    int fd;
    uint32_t flags;
    union sa *address;
    union sa address;
    void *data;
    ood_inet_socket_fn *on_error;
    ood_inet_socket_fn *on_read;

M src/libood/ood/inet/socket_test.c => src/libood/ood/inet/socket_test.c +3 -3
@@ 11,9 11,9 @@ test_inet_socket_alloc(void)

    assert(listener);
    assert(-1 == listener->fd);
    assert(htonl(INADDR_LOOPBACK) == listener->address->ipv4.sin_addr.s_addr);
    assert(htons(1080) == listener->address->ipv4.sin_port);
    assert(0 == strcmp("127.0.0.1:1080", sa_str(listener->address)));
    assert(htonl(INADDR_LOOPBACK) == listener->address.ipv4.sin_addr.s_addr);
    assert(htons(1080) == listener->address.ipv4.sin_port);
    assert(0 == strcmp("127.0.0.1:1080", sa_str(&listener->address)));

    ood_inet_socket_free(listener);
}

M src/sa/sa.c => src/sa/sa.c +63 -121
@@ 6,52 6,54 @@
#include <stdio.h>


union sa *
sa_alloc(void)
bool
sa_unspec(union sa *address)
{
    return calloc(1, sizeof(union sa));
    if (!address) return false;
    memset(address, 0, sizeof(union sa));
    return true;
}


union sa *
sa_alloc_from_in_addr_and_port(in_addr_t in_addr, in_port_t port)
bool
sa_from_in_addr_and_port(union sa *address, in_addr_t in_addr, in_port_t port)
{
    struct sockaddr_in ipv4_address = {
            .sin_family = AF_INET,
            .sin_addr.s_addr = htonl(in_addr),
            .sin_port = htons(port),
    };
    return sa_alloc_from_sockaddr_in(&ipv4_address);
    return sa_from_sockaddr_in(address, &ipv4_address);
}


union sa *
sa_alloc_from_in6_addr_and_port(struct in6_addr in6_addr, in_port_t port)
bool
sa_from_in6_addr_and_port(union sa *address, struct in6_addr in6_addr, in_port_t port)
{
    struct sockaddr_in6 inet6 = {
            .sin6_family=AF_INET6,
            .sin6_addr=in6_addr,
            .sin6_port=htons(port),
    };
    return sa_alloc_from_sockaddr_in6(&inet6);
    return sa_from_sockaddr_in6(address, &inet6);
}


union sa *
sa_alloc_from_ipv4_str_and_port(char const *ipv4_str, in_port_t port)
bool
sa_from_ipv4_str_and_port(union sa *address, char const *ipv4_str, in_port_t port)
{
    if (!ipv4_str) return NULL;
    if (!ipv4_str) return false;

    in_addr_t in_addr;
    int result = inet_pton(AF_INET, ipv4_str, &in_addr);
    if (1 != result) return NULL;
    if (1 != result) return false;

    return sa_alloc_from_in_addr_and_port(ntohl(in_addr), port);
    return sa_from_in_addr_and_port(address, ntohl(in_addr), port);
}


union sa *
sa_alloc_from_ipv6_str_and_port(char const *ipv6_str, in_port_t port)
bool
sa_from_ipv6_str_and_port(union sa *address, char const *ipv6_str, in_port_t port)
{
    if (!ipv6_str) return NULL;



@@ 59,152 61,100 @@ sa_alloc_from_ipv6_str_and_port(char const *ipv6_str, in_port_t port)
    int result = inet_pton(AF_INET6, ipv6_str, &in6_addr);
    if (1 != result) return NULL;

    return sa_alloc_from_in6_addr_and_port(in6_addr, port);
    return sa_from_in6_addr_and_port(address, in6_addr, port);
}


union sa *
sa_alloc_from_path(char const *path)
bool
sa_from_path(union sa *address, char const *path)
{
    if (!path) return NULL;
    if (strlen(path) >= UNIX_PATH_MAX) return NULL;
    if (!path) return false;
    if (strlen(path) >= UNIX_PATH_MAX) return false;

    struct sockaddr_un local_address = {
            .sun_family=AF_UNIX,
    };
    strcpy(local_address.sun_path, path);
    return sa_alloc_from_sockaddr_un(&local_address);
    return sa_from_sockaddr_un(address, &local_address);
}


union sa *
sa_alloc_from_sockaddr(struct sockaddr const *generic_address)
bool
sa_from_sockaddr(union sa *address, struct sockaddr const *generic_address)
{
    if (!generic_address) return NULL;
    if (!generic_address) return false;

    switch (generic_address->sa_family) {
        case AF_UNIX: return sa_alloc_from_sockaddr_un((struct sockaddr_un const *)generic_address);
        case AF_INET: return sa_alloc_from_sockaddr_in((struct sockaddr_in const *)generic_address);
        case AF_INET6: return sa_alloc_from_sockaddr_in6((struct sockaddr_in6 const *)generic_address);
        default: return NULL;
        case AF_UNIX: return sa_from_sockaddr_un(address, (struct sockaddr_un const *)generic_address);
        case AF_INET: return sa_from_sockaddr_in(address, (struct sockaddr_in const *)generic_address);
        case AF_INET6: return sa_from_sockaddr_in6(address, (struct sockaddr_in6 const *)generic_address);
        default: return false;
    }
}


union sa *
sa_alloc_from_sockaddr_in(struct sockaddr_in const *ipv4_address)
bool
sa_from_sockaddr_in(union sa *address, struct sockaddr_in const *ipv4_address)
{
    if (!ipv4_address) return NULL;
    if (!ipv4_address) return false;

    union sa *address = calloc(1, sizeof(union sa));
    if (!address) return NULL;
    if (!sa_unspec(address)) return false;

    address->ipv4 = *ipv4_address;
    const char *wrote_address = inet_ntop(AF_INET, &ipv4_address->sin_addr,
                                          address->ipv4_str, SA_IPV4_STR_SIZE);
    if (!wrote_address) goto fail;

    char *s = address->ipv4_str + strlen(address->ipv4_str);
    int wrote_port = sprintf(s, ":%i", ntohs(ipv4_address->sin_port));
    if (wrote_port < 0) goto fail;

    return address;
    const char *ipv4_str = inet_ntop(AF_INET, &ipv4_address->sin_addr,
                                     address->ipv4_str, SA_IPV4_STR_SIZE);
    if (!ipv4_str) return false;

    fail:
    free(address);
    return NULL;
    char *s = address->ipv4_str + strlen(address->ipv4_str);
    int bytes_written = sprintf(s, ":%i", ntohs(ipv4_address->sin_port));
    if (bytes_written < 0) return false;

    return true;
}


union sa *
sa_alloc_from_sockaddr_in6(struct sockaddr_in6 const *ipv6_address)
bool
sa_from_sockaddr_in6(union sa *address, struct sockaddr_in6 const *ipv6_address)
{
    if (!ipv6_address) return NULL;
    if (!ipv6_address) return false;

    union sa *address = calloc(1, sizeof(union sa));
    if (!address) return NULL;
    if (!sa_unspec(address)) return false;

    address->ipv6 = *ipv6_address;

    char const *wrote_address = inet_ntop(AF_INET6, &ipv6_address->sin6_addr,
                                          address->ipv6_str, SA_IPV6_STR_SIZE);
    if (!wrote_address) goto fail;
    char const *ipv6_str = inet_ntop(AF_INET6, &ipv6_address->sin6_addr,
                                     address->ipv6_str, SA_IPV6_STR_SIZE);
    if (!ipv6_str) return false;

    char *s = address->ipv6_str + strlen(address->ipv6_str);
    int wrote_port = sprintf(s, ":%i", ntohs(ipv6_address->sin6_port));
    if (wrote_port < 0) goto fail;

    return address;
    int bytes_written = sprintf(s, ":%i", ntohs(ipv6_address->sin6_port));
    if (bytes_written < 0) return false;

    fail:
    free(address);
    return NULL;
    return true;
}


union sa *
sa_alloc_from_sockaddr_un(struct sockaddr_un const *local_address)
bool
sa_from_sockaddr_un(union sa *address, struct sockaddr_un const *local_address)
{
    if (!local_address) return NULL;
    if (!local_address) return false;

    union sa *address = calloc(1, sizeof(union sa));
    if (!address) return NULL;
    if (!sa_unspec(address)) return false;

    address->local = *local_address;

    return address;
}


int
sa_accept_alloc(int socket, union sa **address_out)
{
    union sa *address = sa_alloc();
    if (!address) return -1;

    socklen_t len = sizeof(struct sockaddr_storage);
    int accepted = accept(socket, &address->generic, &len);
    if (accepted >= 0) {
        *address_out = address;
    } else {
        free(address);
    }
    return accepted;
}


union sa *
sa_alloc_getpeername(int socket)
{
    union sa *address = sa_alloc();
    if (!address) return NULL;

    socklen_t len = sizeof(struct sockaddr_storage);
    int result = getpeername(socket, &address->generic, &len);
    if (0 != result) {
        free(address);
        return NULL;
    }

    return address;
    return true;
}


union sa *
sa_alloc_getsockname(int socket)
bool
sa_prepare(union sa *address, socklen_t *len)
{
    union sa *address = sa_alloc();
    if (!address) return NULL;

    socklen_t len = sizeof(struct sockaddr_storage);
    int result = getsockname(socket, &address->generic, &len);
    if (0 != result) {
        free(address);
        return NULL;
    }

    return address;
    if (!sa_unspec(address)) return false;
    if (!len) return false;
    *len = sa_len(address);
    return true;
}




@@ 236,11 186,3 @@ sa_str(union sa const *address)
        default: return "(unsupported)";
    }
}


extern int
sa_bind(int socket, union sa const *address);


extern int
sa_connect(int socket, union sa const *address);

M src/sa/sa.h => src/sa/sa.h +44 -49
@@ 2,6 2,7 @@
#define SA_H_INCLUDED


#include <stdbool.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>


@@ 29,9 30,22 @@
   a string representation.  For the `sockaddr_un` address type, the `sun_path`
   field is used as the string representation.

   Typical usage of `sa` looks like this (sans error handling):

        // initializing a socket address for use
        union sa address;
        sa_from_ipv4_str_and_port(&address, "127.0.0.1", 8080);
        bind(fd, &address.generic, sa_len(&address));

        // receiving a socket address
        union sa client_address;
        socklen_t len;
        sa_prepare(&client_address, &len);
        int client_fd = accept(fd, &client_address.generic, &len);

   Because the symbol `unix` is #defined in some Unix C compilers, `sa`
   uses the name `local` to refer to Unix domain sockets, which are sometimes
   referred to as "local" sockets. */
   uses the name `local` for Unix domain sockets (often referred to as "local"
   sockets). */
union sa {
    struct sockaddr generic;
    struct {


@@ 47,56 61,52 @@ union sa {
};


// # Allocate socket address from components
// # Initialize an unspecified socket address

union sa *
sa_alloc(void);
bool
sa_unspec(union sa *address);

// `in_addr` and `port` are in host byte order
union sa *
sa_alloc_from_in_addr_and_port(in_addr_t in_addr, in_port_t port);

// `port` is in host byte order
union sa *
sa_alloc_from_in6_addr_and_port(struct in6_addr in6_addr, in_port_t port);
// # Initialize a socket address from components

// `port` is in host byte order
union sa *
sa_alloc_from_ipv4_str_and_port(char const *ipv4_str, in_port_t port);
bool
sa_from_in_addr_and_port(union sa *address, in_addr_t in_addr, in_port_t port);

// `port` is in host byte order
union sa *
sa_alloc_from_ipv6_str_and_port(char const *ipv6_str, in_port_t port);
bool
sa_from_in6_addr_and_port(union sa *address, struct in6_addr in6_addr, in_port_t port);

union sa *
sa_alloc_from_path(char const *path);
// `port` is in host byte order
bool
sa_from_ipv4_str_and_port(union sa *address, char const *ipv4_str, in_port_t port);

// `port` is in host byte order
bool
sa_from_ipv6_str_and_port(union sa *address, char const *ipv6_str, in_port_t port);

// # Allocate socket address from a `sockaddr`-type struct
bool
sa_from_path(union sa *address, char const *path);

union sa *
sa_alloc_from_sockaddr(struct sockaddr const *generic_address);

union sa *
sa_alloc_from_sockaddr_in(struct sockaddr_in const *ipv4_address);
// # Initialize socket address from a `sockaddr`-type struct

union sa *
sa_alloc_from_sockaddr_in6(struct sockaddr_in6 const *ipv6_address);
bool
sa_from_sockaddr(union sa *address, struct sockaddr const *generic_address);

union sa *
sa_alloc_from_sockaddr_un(struct sockaddr_un const *local_address);
bool
sa_from_sockaddr_in(union sa *address, struct sockaddr_in const *ipv4_address);

bool
sa_from_sockaddr_in6(union sa *address, struct sockaddr_in6 const *ipv6_address);

// # Allocate socket address from a socket
bool
sa_from_sockaddr_un(union sa *address, struct sockaddr_un const *local_address);

int
sa_accept_alloc(int socket, union sa **address_out);

union sa *
sa_alloc_getpeername(int socket);
// # Initialize an `sa` union and length for receiving a socket address

union sa *
sa_alloc_getsockname(int socket);
bool
sa_prepare(union sa *address, socklen_t *len);


// # Get info about a socket address


@@ 108,19 118,4 @@ char const *
sa_str(union sa const *address);


// # Wrap socket functions that use `sockaddr` structs

inline int
sa_bind(int socket, union sa const *address)
{
    return bind(socket, &address->generic, sa_len(address));
}

inline int
sa_connect(int socket, union sa const *address)
{
    return connect(socket, &address->generic, sa_len(address));
}


#endif

M src/sa/sa_tests.c => src/sa/sa_tests.c +376 -213
@@ 9,187 9,258 @@


static void
test_sa_alloc(void)
test_sa_unspec(void)
{
    union sa *address = sa_alloc();

    assert(address);
    assert(AF_UNSPEC == address->generic.sa_family);
    assert(AF_UNSPEC == address->ipv4.sin_family);
    assert(AF_UNSPEC == address->ipv6.sin6_family);
    assert(AF_UNSPEC == address->local.sun_family);
    assert(AF_UNSPEC == address->storage.ss_family);
    union sa address;
    memset(&address, 0xff, sizeof address);

    free(address);
    bool success = sa_unspec(&address);

    assert(success);
    assert(AF_UNSPEC == address.generic.sa_family);
    assert(AF_UNSPEC == address.ipv4.sin_family);
    assert(AF_UNSPEC == address.ipv6.sin6_family);
    assert(AF_UNSPEC == address.local.sun_family);
    assert(AF_UNSPEC == address.storage.ss_family);
}


static void
test_sa_unspec_for_null(void)
{
    bool success = sa_unspec(NULL);

    assert(!success);
}


static void
test_sa_alloc_from_in_addr_and_port(void)
test_sa_from_in_addr_and_port(void)
{
    union sa *address = sa_alloc_from_in_addr_and_port(INADDR_LOOPBACK, 8080);
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_in_addr_and_port(&address, INADDR_LOOPBACK, 8080);

    assert(success);
    assert(AF_INET == address.generic.sa_family);
    assert(AF_INET == address.ipv4.sin_family);
    assert(AF_INET == address.ipv6.sin6_family);
    assert(AF_INET == address.local.sun_family);
    assert(AF_INET == address.storage.ss_family);
    assert(htonl(INADDR_LOOPBACK) == address.ipv4.sin_addr.s_addr);
    assert(htons(8080) == address.ipv4.sin_port);
    assert(STR_EQ("127.0.0.1:8080", sa_str(&address)));
}


    assert(address);
    assert(AF_INET == address->generic.sa_family);
    assert(AF_INET == address->ipv4.sin_family);
    assert(AF_INET == address->ipv6.sin6_family);
    assert(AF_INET == address->local.sun_family);
    assert(AF_INET == address->storage.ss_family);
    assert(htonl(INADDR_LOOPBACK) == address->ipv4.sin_addr.s_addr);
    assert(htons(8080) == address->ipv4.sin_port);
    assert(STR_EQ("127.0.0.1:8080", sa_str(address)));
static void
test_sa_from_in_addr_and_port_for_null(void)
{
    bool success = sa_from_in_addr_and_port(NULL, INADDR_LOOPBACK, 8080);

    free(address);
    assert(!success);
}


static void
test_sa_alloc_from_in6_addr_and_port(void)
test_sa_from_in6_addr_and_port(void)
{
    union sa *address = sa_alloc_from_in6_addr_and_port(in6addr_loopback, 8080);
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_in6_addr_and_port(&address, in6addr_loopback, 8080);

    assert(success);
    assert(AF_INET6 == address.generic.sa_family);
    assert(AF_INET6 == address.ipv4.sin_family);
    assert(AF_INET6 == address.ipv6.sin6_family);
    assert(AF_INET6 == address.local.sun_family);
    assert(AF_INET6 == address.storage.ss_family);
    assert(IN6_ADDR_EQ(&in6addr_loopback, &address.ipv6.sin6_addr));
    assert(htons(8080) == address.ipv6.sin6_port);
    assert(STR_EQ("::1:8080", sa_str(&address)));
}


    assert(address);
    assert(AF_INET6 == address->generic.sa_family);
    assert(AF_INET6 == address->ipv4.sin_family);
    assert(AF_INET6 == address->ipv6.sin6_family);
    assert(AF_INET6 == address->local.sun_family);
    assert(AF_INET6 == address->storage.ss_family);
    assert(IN6_ADDR_EQ(&in6addr_loopback, &address->ipv6.sin6_addr));
    assert(htons(8080) == address->ipv6.sin6_port);
    assert(STR_EQ("::1:8080", sa_str(address)));
static void
test_sa_from_in6_addr_and_port_for_null(void)
{
    bool success = sa_from_in6_addr_and_port(NULL, in6addr_loopback, 8080);

    free(address);
    assert(!success);
}


static void
test_sa_alloc_from_ipv4_str_and_port(void)
test_sa_from_ipv4_str_and_port(void)
{
    union sa *address = sa_alloc_from_ipv4_str_and_port("127.0.0.1", 8080);
    union sa address;
    memset(&address, 0xff, sizeof address);

    assert(address);
    assert(AF_INET == address->generic.sa_family);
    assert(AF_INET == address->ipv4.sin_family);
    assert(AF_INET == address->ipv6.sin6_family);
    assert(AF_INET == address->local.sun_family);
    assert(AF_INET == address->storage.ss_family);
    assert(htonl(INADDR_LOOPBACK) == address->ipv4.sin_addr.s_addr);
    assert(htons(8080) == address->ipv4.sin_port);
    assert(STR_EQ("127.0.0.1:8080", sa_str(address)));
    bool success = sa_from_ipv4_str_and_port(&address, "127.0.0.1", 8080);

    free(address);
    assert(success);
    assert(AF_INET == address.generic.sa_family);
    assert(AF_INET == address.ipv4.sin_family);
    assert(AF_INET == address.ipv6.sin6_family);
    assert(AF_INET == address.local.sun_family);
    assert(AF_INET == address.storage.ss_family);
    assert(htonl(INADDR_LOOPBACK) == address.ipv4.sin_addr.s_addr);
    assert(htons(8080) == address.ipv4.sin_port);
    assert(STR_EQ("127.0.0.1:8080", sa_str(&address)));
}


static void
test_sa_alloc_from_ipv4_str_and_port_when_null(void)
test_sa_from_ipv4_str_and_port_when_null(void)
{
    union sa *address = sa_alloc_from_ipv4_str_and_port(NULL, 8080);
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_ipv4_str_and_port(NULL, "127.0.0.1", 8080);

    assert(!address);
    assert(!success);

    success = sa_from_ipv4_str_and_port(&address, NULL, 8080);

    assert(!success);
}


static void
test_sa_alloc_from_ipv4_str_and_port_when_invalid(void)
test_sa_from_ipv4_str_and_port_when_invalid(void)
{
    union sa *address = sa_alloc_from_ipv4_str_and_port("invalid", 8080);
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_ipv4_str_and_port(&address, "invalid", 8080);

    assert(!address);
    assert(!success);
}


static void
test_sa_alloc_from_ipv6_str_and_port(void)
test_sa_from_ipv6_str_and_port(void)
{
    union sa *address = sa_alloc_from_ipv6_str_and_port("::1", 8080);
    union sa address;
    memset(&address, 0xff, sizeof address);

    assert(address);
    assert(AF_INET6 == address->generic.sa_family);
    assert(AF_INET6 == address->ipv4.sin_family);
    assert(AF_INET6 == address->ipv6.sin6_family);
    assert(AF_INET6 == address->local.sun_family);
    assert(AF_INET6 == address->storage.ss_family);
    assert(IN6_ADDR_EQ(&in6addr_loopback, &address->ipv6.sin6_addr));
    assert(htons(8080) == address->ipv6.sin6_port);
    assert(STR_EQ("::1:8080", sa_str(address)));
    bool success = sa_from_ipv6_str_and_port(&address, "::1", 8080);

    free(address);
    assert(success);
    assert(AF_INET6 == address.generic.sa_family);
    assert(AF_INET6 == address.ipv4.sin_family);
    assert(AF_INET6 == address.ipv6.sin6_family);
    assert(AF_INET6 == address.local.sun_family);
    assert(AF_INET6 == address.storage.ss_family);
    assert(IN6_ADDR_EQ(&in6addr_loopback, &address.ipv6.sin6_addr));
    assert(htons(8080) == address.ipv6.sin6_port);
    assert(STR_EQ("::1:8080", sa_str(&address)));
}


static void
test_sa_alloc_from_ipv6_str_and_port_when_null(void)
test_sa_from_ipv6_str_and_port_when_null(void)
{
    union sa *address = sa_alloc_from_ipv6_str_and_port(NULL, 8080);
    union sa address;
    memset(&address, 0xff, sizeof address);

    assert(!address);
    bool success = sa_from_ipv6_str_and_port(NULL, "::1", 8080);

    assert(!success);

    success = sa_from_ipv6_str_and_port(&address, NULL, 8080);

    assert(!success);
}


static void
test_sa_alloc_from_ipv6_str_and_port_when_invalid(void)
test_sa_from_ipv6_str_and_port_when_invalid(void)
{
    union sa *address = sa_alloc_from_ipv6_str_and_port("invalid", 8080);
    union sa address;
    memset(&address, 0xff, sizeof address);

    assert(!address);
    bool success = sa_from_ipv6_str_and_port(NULL, "invalid", 8080);

    assert(!success);
}


static void
test_sa_alloc_from_path(void)
test_sa_from_path(void)
{
    union sa *address = sa_alloc_from_path("/var/dood/http");
    union sa address;
    memset(&address, 0xff, sizeof address);

    assert(address);
    assert(AF_UNIX == address->generic.sa_family);
    assert(AF_UNIX == address->ipv4.sin_family);
    assert(AF_UNIX == address->ipv6.sin6_family);
    assert(AF_UNIX == address->local.sun_family);
    assert(AF_UNIX == address->storage.ss_family);
    assert(STR_EQ("/var/dood/http", address->local.sun_path));
    assert(STR_EQ("/var/dood/http", sa_str(address)));
    bool success = sa_from_path(&address, "/var/dood/http");

    free(address);
    assert(success);
    assert(AF_UNIX == address.generic.sa_family);
    assert(AF_UNIX == address.ipv4.sin_family);
    assert(AF_UNIX == address.ipv6.sin6_family);
    assert(AF_UNIX == address.local.sun_family);
    assert(AF_UNIX == address.storage.ss_family);
    assert(STR_EQ("/var/dood/http", address.local.sun_path));
    assert(STR_EQ("/var/dood/http", sa_str(&address)));
}


static void
test_sa_alloc_from_path_when_null(void)
test_sa_from_path_when_null(void)
{
    union sa *address = sa_alloc_from_path(NULL);
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_path(NULL, "/var/dood/http");

    assert(!success);

    assert(!address);
    success = sa_from_path(&address, NULL);

    assert(!success);
}


static void
test_sa_alloc_from_path_when_too_long(void)
test_sa_from_path_when_too_long(void)
{
    union sa address;
    memset(&address, 0xff, sizeof address);

    char const *too_long = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
    assert(strlen(too_long) > UNIX_PATH_MAX);

    union sa *address = sa_alloc_from_path(too_long);
    bool success = sa_from_path(&address, too_long);

    assert(!address);
    assert(!success);
}


static void
test_sa_alloc_from_sockaddr_for_unspecified(void)
test_sa_from_sockaddr_for_unspecified(void)
{
    union sa address;
    memset(&address, 0xff, sizeof address);

    struct sockaddr generic = {
            .sa_family=AF_UNSPEC,
    };

    union sa *address = sa_alloc_from_sockaddr(&generic);
    bool success = sa_from_sockaddr(&address, &generic);

    assert(!address);
    assert(!success);
}


static void
test_sa_alloc_from_sockaddr_for_ipv4(void)
test_sa_from_sockaddr_for_ipv4(void)
{
    union sa address;
    memset(&address, 0xff, sizeof address);

    struct sockaddr_in ipv4 = {
            .sin_family=AF_INET,
            .sin_addr=(struct in_addr){


@@ 199,25 270,26 @@ test_sa_alloc_from_sockaddr_for_ipv4(void)
    };
    struct sockaddr *generic = (struct sockaddr *)&ipv4;

    union sa *address = sa_alloc_from_sockaddr(generic);

    assert(address);
    assert(AF_INET == address->generic.sa_family);
    assert(AF_INET == address->ipv4.sin_family);
    assert(AF_INET == address->ipv6.sin6_family);
    assert(AF_INET == address->local.sun_family);
    assert(AF_INET == address->storage.ss_family);
    assert(htonl(INADDR_LOOPBACK) == address->ipv4.sin_addr.s_addr);
    assert(htons(8080) == address->ipv4.sin_port);
    assert(STR_EQ("127.0.0.1:8080", sa_str(address)));
    bool success = sa_from_sockaddr(&address, generic);

    free(address);
    assert(success);
    assert(AF_INET == address.generic.sa_family);
    assert(AF_INET == address.ipv4.sin_family);
    assert(AF_INET == address.ipv6.sin6_family);
    assert(AF_INET == address.local.sun_family);
    assert(AF_INET == address.storage.ss_family);
    assert(htonl(INADDR_LOOPBACK) == address.ipv4.sin_addr.s_addr);
    assert(htons(8080) == address.ipv4.sin_port);
    assert(STR_EQ("127.0.0.1:8080", sa_str(&address)));
}


static void
test_sa_alloc_from_sockaddr_for_ipv6(void)
test_sa_from_sockaddr_for_ipv6(void)
{
    union sa address;
    memset(&address, 0xff, sizeof address);

    struct sockaddr_in6 ipv6 = {
            .sin6_family=AF_INET6,
            .sin6_addr=in6addr_loopback,


@@ 225,70 297,113 @@ test_sa_alloc_from_sockaddr_for_ipv6(void)
    };
    struct sockaddr *generic = (struct sockaddr *)&ipv6;

    union sa *address = sa_alloc_from_sockaddr(generic);
    bool success = sa_from_sockaddr(&address, generic);

    assert(address);
    assert(AF_INET6 == address->generic.sa_family);
    assert(AF_INET6 == address->ipv4.sin_family);
    assert(AF_INET6 == address->ipv6.sin6_family);
    assert(AF_INET6 == address->local.sun_family);
    assert(AF_INET6 == address->storage.ss_family);
    assert(IN6_ADDR_EQ(&in6addr_loopback, &address->ipv6.sin6_addr));
    assert(htons(8080) == address->ipv6.sin6_port);
    assert(STR_EQ("::1:8080", sa_str(address)));

    free(address);
    assert(success);
    assert(AF_INET6 == address.generic.sa_family);
    assert(AF_INET6 == address.ipv4.sin_family);
    assert(AF_INET6 == address.ipv6.sin6_family);
    assert(AF_INET6 == address.local.sun_family);
    assert(AF_INET6 == address.storage.ss_family);
    assert(IN6_ADDR_EQ(&in6addr_loopback, &address.ipv6.sin6_addr));
    assert(htons(8080) == address.ipv6.sin6_port);
    assert(STR_EQ("::1:8080", sa_str(&address)));
}


static void
test_sa_alloc_from_sockaddr_for_local(void)
test_sa_from_sockaddr_for_local(void)
{
    union sa address;
    memset(&address, 0xff, sizeof address);

    struct sockaddr_un local = {
            .sun_family = AF_UNIX,
            .sun_path = "/var/dood/http",
    };
    struct sockaddr *generic = (struct sockaddr *)&local;

    union sa *address = sa_alloc_from_sockaddr(generic);

    assert(address);
    assert(AF_UNIX == address->generic.sa_family);
    assert(AF_UNIX == address->ipv4.sin_family);
    assert(AF_UNIX == address->ipv6.sin6_family);
    assert(AF_UNIX == address->local.sun_family);
    assert(AF_UNIX == address->storage.ss_family);
    assert(STR_EQ("/var/dood/http", address->local.sun_path));
    assert(STR_EQ("/var/dood/http", sa_str(address)));
    bool success = sa_from_sockaddr(&address, generic);

    free(address);
    assert(success);
    assert(AF_UNIX == address.generic.sa_family);
    assert(AF_UNIX == address.ipv4.sin_family);
    assert(AF_UNIX == address.ipv6.sin6_family);
    assert(AF_UNIX == address.local.sun_family);
    assert(AF_UNIX == address.storage.ss_family);
    assert(STR_EQ("/var/dood/http", address.local.sun_path));
    assert(STR_EQ("/var/dood/http", sa_str(&address)));
}


static void
test_sa_alloc_from_sockaddr_for_unsupported(void)
test_sa_from_sockaddr_for_unsupported(void)
{
    union sa address;
    memset(&address, 0xff, sizeof address);

    struct sockaddr generic = {
            .sa_family=0xff,
    };

    union sa *address = sa_alloc_from_sockaddr(&generic);
    bool success = sa_from_sockaddr(&address, &generic);

    assert(!address);
    assert(!success);
}


static void
test_sa_alloc_from_sockaddr_when_null(void)
test_sa_from_sockaddr_when_null(void)
{
    union sa *address = sa_alloc_from_sockaddr(NULL);
    union sa address;
    memset(&address, 0xff, sizeof address);

    struct sockaddr_in6 ipv6 = {
            .sin6_family=AF_INET6,
            .sin6_addr=in6addr_loopback,
            .sin6_port=htons(8080),
    };
    struct sockaddr *generic = (struct sockaddr *)&ipv6;

    bool success = sa_from_sockaddr(&address, NULL);

    assert(!address);
    assert(!success);

    success = sa_from_sockaddr(NULL, generic);

    assert(!success);
}


static void
test_sa_from_sockaddr_in(void)
{
    struct sockaddr_in ipv4 = {
            .sin_family=AF_INET,
            .sin_addr=(struct in_addr){
                    .s_addr=htonl(INADDR_LOOPBACK)
            },
            .sin_port=htons(8080),
    };
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_sockaddr_in(&address, &ipv4);

    assert(success);
    assert(AF_INET == address.generic.sa_family);
    assert(AF_INET == address.ipv4.sin_family);
    assert(AF_INET == address.ipv6.sin6_family);
    assert(AF_INET == address.local.sun_family);
    assert(AF_INET == address.storage.ss_family);
    assert(htonl(INADDR_LOOPBACK) == address.ipv4.sin_addr.s_addr);
    assert(htons(8080) == address.ipv4.sin_port);
    assert(STR_EQ("127.0.0.1:8080", sa_str(&address)));
}


static void
test_sa_alloc_from_sockaddr_in(void)
test_sa_from_sockaddr_in_when_null(void)
{
    struct sockaddr_in ipv4 = {
            .sin_family=AF_INET,


@@ 297,95 412,137 @@ test_sa_alloc_from_sockaddr_in(void)
            },
            .sin_port=htons(8080),
    };
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_sockaddr_in(NULL, &ipv4);

    union sa *address = sa_alloc_from_sockaddr_in(&ipv4);
    assert(!success);

    assert(address);
    assert(AF_INET == address->generic.sa_family);
    assert(AF_INET == address->ipv4.sin_family);
    assert(AF_INET == address->ipv6.sin6_family);
    assert(AF_INET == address->local.sun_family);
    assert(AF_INET == address->storage.ss_family);
    assert(htonl(INADDR_LOOPBACK) == address->ipv4.sin_addr.s_addr);
    assert(htons(8080) == address->ipv4.sin_port);
    assert(STR_EQ("127.0.0.1:8080", sa_str(address)));
    success = sa_from_sockaddr_in(&address, NULL);

    free(address);
    assert(!success);
}


static void
test_sa_alloc_from_sockaddr_in_when_null(void)
test_sa_from_sockaddr_in6(void)
{
    union sa *address = sa_alloc_from_sockaddr_in(NULL);
    struct sockaddr_in6 ipv6 = {
            .sin6_family=AF_INET6,
            .sin6_addr=in6addr_loopback,
            .sin6_port=htons(8080),
    };
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_sockaddr_in6(&address, &ipv6);

    assert(!address);
    assert(success);
    assert(AF_INET6 == address.generic.sa_family);
    assert(AF_INET6 == address.ipv4.sin_family);
    assert(AF_INET6 == address.ipv6.sin6_family);
    assert(AF_INET6 == address.local.sun_family);
    assert(AF_INET6 == address.storage.ss_family);
    assert(IN6_ADDR_EQ(&in6addr_loopback, &address.ipv6.sin6_addr));
    assert(htons(8080) == address.ipv6.sin6_port);
    assert(STR_EQ("::1:8080", sa_str(&address)));
}


static void
test_sa_alloc_from_sockaddr_in6(void)
test_sa_from_sockaddr_in6_when_null(void)
{
    struct sockaddr_in6 ipv6 = {
            .sin6_family=AF_INET6,
            .sin6_addr=in6addr_loopback,
            .sin6_port=htons(8080),
    };
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_sockaddr_in6(NULL, &ipv6);

    union sa *address = sa_alloc_from_sockaddr_in6(&ipv6);
    assert(!success);

    assert(address);
    assert(AF_INET6 == address->generic.sa_family);
    assert(AF_INET6 == address->ipv4.sin_family);
    assert(AF_INET6 == address->ipv6.sin6_family);
    assert(AF_INET6 == address->local.sun_family);
    assert(AF_INET6 == address->storage.ss_family);
    assert(IN6_ADDR_EQ(&in6addr_loopback, &address->ipv6.sin6_addr));
    assert(htons(8080) == address->ipv6.sin6_port);
    assert(STR_EQ("::1:8080", sa_str(address)));
    success = sa_from_sockaddr_in6(&address, NULL);

    free(address);
    assert(!success);
}


static void
test_sa_alloc_from_sockaddr_in6_when_null(void)
test_sa_from_sockaddr_un(void)
{
    union sa *address = sa_alloc_from_sockaddr_in6(NULL);
    struct sockaddr_un local = {
            .sun_family = AF_UNIX,
            .sun_path = "/var/dood/http",
    };
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_sockaddr_un(&address, &local);

    assert(!address);
    assert(success);
    assert(AF_UNIX == address.generic.sa_family);
    assert(AF_UNIX == address.ipv4.sin_family);
    assert(AF_UNIX == address.ipv6.sin6_family);
    assert(AF_UNIX == address.local.sun_family);
    assert(AF_UNIX == address.storage.ss_family);
    assert(STR_EQ("/var/dood/http", address.local.sun_path));
    assert(STR_EQ("/var/dood/http", sa_str(&address)));
}


static void
test_sa_alloc_from_sockaddr_un(void)
test_sa_from_sockaddr_un_when_null(void)
{
    struct sockaddr_un local = {
            .sun_family = AF_UNIX,
            .sun_path = "/var/dood/http",
    };
    union sa address;
    memset(&address, 0xff, sizeof address);

    bool success = sa_from_sockaddr_un(NULL, &local);

    union sa *address = sa_alloc_from_sockaddr_un(&local);
    assert(!success);

    assert(address);
    assert(AF_UNIX == address->generic.sa_family);
    assert(AF_UNIX == address->ipv4.sin_family);
    assert(AF_UNIX == address->ipv6.sin6_family);
    assert(AF_UNIX == address->local.sun_family);
    assert(AF_UNIX == address->storage.ss_family);
    assert(STR_EQ("/var/dood/http", address->local.sun_path));
    assert(STR_EQ("/var/dood/http", sa_str(address)));
    success = sa_from_sockaddr_un(&address, NULL);

    free(address);
    assert(!success);
}


static void
test_sa_alloc_from_sockaddr_un_when_null(void)
test_sa_prepare(void)
{
    union sa *address = sa_alloc_from_sockaddr_un(NULL);
    union sa address;
    memset(&address, 0xff, sizeof address);
    socklen_t len = 0xffff;

    assert(!address);
    bool success = sa_prepare(&address, &len);

    assert(success);
    assert(AF_UNSPEC == address.generic.sa_family);
    assert(sizeof(struct sockaddr_storage) == len);
}


static void
test_sa_prepare_for_null(void)
{
    union sa address;
    memset(&address, 0xff, sizeof address);
    socklen_t len = 0xffff;

    bool success = sa_prepare(NULL, &len);

    assert(!success);

    success = sa_prepare(&address, NULL);

    assert(!success);
}




@@ 405,33 562,30 @@ test_sa_len_for_unspecified(void)
static void
test_sa_len_for_ipv4(void)
{
    union sa *address = sa_alloc_from_in_addr_and_port(INADDR_LOOPBACK, 8080);

    assert(sizeof(struct sockaddr_in) == sa_len(address));
    union sa address;
    sa_from_in_addr_and_port(&address, INADDR_LOOPBACK, 8080);

    free(address);
    assert(sizeof(struct sockaddr_in) == sa_len(&address));
}


static void
test_sa_len_for_ipv6(void)
{
    union sa *address = sa_alloc_from_in6_addr_and_port(in6addr_loopback, 8080);

    assert(sizeof(struct sockaddr_in6) == sa_len(address));
    union sa address;
    sa_from_in6_addr_and_port(&address, in6addr_loopback, 8080);

    free(address);
    assert(sizeof(struct sockaddr_in6) == sa_len(&address));
}


static void
test_sa_len_for_local(void)
{
    union sa *address = sa_alloc_from_path("/var/dood/http");
    union sa address;
    sa_from_path(&address, "/var/dood/http");

    assert(sizeof(struct sockaddr_un) == sa_len(address));

    free(address);
    assert(sizeof(struct sockaddr_un) == sa_len(&address));
}




@@ 491,36 645,45 @@ test_sa_str_for_null(void)
int
main(int argc, char *argv[])
{
    test_sa_alloc();
    test_sa_unspec();
    test_sa_unspec_for_null();

    test_sa_from_in_addr_and_port();
    test_sa_from_in_addr_and_port_for_null();

    test_sa_from_in6_addr_and_port();
    test_sa_from_in6_addr_and_port_for_null();

    test_sa_from_ipv4_str_and_port();
    test_sa_from_ipv4_str_and_port_when_null();
    test_sa_from_ipv4_str_and_port_when_invalid();

    test_sa_from_ipv6_str_and_port();
    test_sa_from_ipv6_str_and_port_when_null();
    test_sa_from_ipv6_str_and_port_when_invalid();

    test_sa_alloc_from_in_addr_and_port();
    test_sa_alloc_from_in6_addr_and_port();
    test_sa_from_path();
    test_sa_from_path_when_null();
    test_sa_from_path_when_too_long();

    test_sa_alloc_from_ipv4_str_and_port();
    test_sa_alloc_from_ipv4_str_and_port_when_null();
    test_sa_alloc_from_ipv4_str_and_port_when_invalid();
    test_sa_from_sockaddr_for_unspecified();
    test_sa_from_sockaddr_for_ipv4();
    test_sa_from_sockaddr_for_ipv6();
    test_sa_from_sockaddr_for_local();
    test_sa_from_sockaddr_for_unsupported();
    test_sa_from_sockaddr_when_null();

    test_sa_alloc_from_ipv6_str_and_port();
    test_sa_alloc_from_ipv6_str_and_port_when_null();
    test_sa_alloc_from_ipv6_str_and_port_when_invalid();
    test_sa_from_sockaddr_in();
    test_sa_from_sockaddr_in_when_null();

    test_sa_alloc_from_path();
    test_sa_alloc_from_path_when_null();
    test_sa_alloc_from_path_when_too_long();
    test_sa_from_sockaddr_in6();
    test_sa_from_sockaddr_in6_when_null();

    test_sa_alloc_from_sockaddr_for_unspecified();
    test_sa_alloc_from_sockaddr_for_ipv4();
    test_sa_alloc_from_sockaddr_for_ipv6();
    test_sa_alloc_from_sockaddr_for_local();
    test_sa_alloc_from_sockaddr_for_unsupported();
    test_sa_alloc_from_sockaddr_when_null();
    test_sa_from_sockaddr_un();
    test_sa_from_sockaddr_un_when_null();

    test_sa_alloc_from_sockaddr_in();
    test_sa_alloc_from_sockaddr_in_when_null();
    test_sa_alloc_from_sockaddr_in6();
    test_sa_alloc_from_sockaddr_in6_when_null();
    test_sa_alloc_from_sockaddr_un();
    test_sa_alloc_from_sockaddr_un_when_null();
    test_sa_prepare();
    test_sa_prepare_for_null();

    test_sa_len_for_unspecified();
    test_sa_len_for_ipv4();