~donmcc/ood

4225005c618bd497bb16cdf345774ed125d1dd34 — Don McCaughey 1 year, 10 months ago 043d40c
Add `sa_update()` function.

The `sa_update()` function resets the string representation if needed
after a `sa` union is modified.  This is useful when passing an `sa`
union to `accept()`, `getpeername()` or `getsockname()`.

Updated the example in the readme file to call `sa_update()` and
extracted common static helper functions `update_ipv4_str()` and
`update_ipv6_str()` from `sa_from_sockaddr_in()` and
`sa_from_sockaddr_in6()` respectively.
6 files changed, 160 insertions(+), 16 deletions(-)

M TODO.md
M src/libood/ood/inet/socket.c
M src/sa/README.md
M src/sa/sa.c
M src/sa/sa.h
M src/sa/sa_tests.c
M TODO.md => TODO.md +1 -1
@@ 1,6 1,6 @@
# TODO

- in "sa", replace alloc functions with init functions
- check address family in `sa_from_sockaddr_in()` and friends
- check if `bind()` works with sizeof struct sockaddr_storage 
- test listening on unix and ipv6 sockets
- handle linux "unnamed" and "abstract" unix sockets

M src/libood/ood/inet/socket.c => src/libood/ood/inet/socket.c +1 -0
@@ 44,6 44,7 @@ ood_inet_socket_accept(struct ood_inet_socket *listener, struct ood_inet_socket 
    errno = 0;
    int fd = accept(listener->fd, &client_address.generic, &len);
    if (fd >= 0) {
        sa_update(&client_address);
        *connection_out = alloc_connection(fd, &client_address);
        if (*connection_out) {
            return ood_okay;

M src/sa/README.md => src/sa/README.md +1 -0
@@ 55,6 55,7 @@ Typical usage of `sa` looks like this (sans error handling):
    socklen_t len;
    sa_prepare(&client_address, &len);
    int client_fd = accept(fd, &client_address.generic, &len);
    sa_update(&client_address);

Socket addresses are frequently logged, so `sa` creates a string representation
at initialization time.  Use the `sa_str()` function to get the correct string

M src/sa/sa.c => src/sa/sa.c +47 -14
@@ 6,6 6,36 @@
#include <stdio.h>


static bool
update_ipv4_str(union sa *address)
{
    const char *ipv4_str = inet_ntop(AF_INET, &address->ipv4.sin_addr,
                                     address->ipv4_str, SA_IPV4_STR_SIZE);
    if (!ipv4_str) return false;

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

    return true;
}


static bool
update_ipv6_str(union sa *address)
{
    char const *ipv6_str = inet_ntop(AF_INET6, &address->ipv6.sin6_addr,
                                     address->ipv6_str, SA_IPV6_STR_SIZE);
    if (!ipv6_str) return false;

    char *s = address->ipv6_str + strlen(address->ipv6_str);
    int bytes_written = sprintf(s, ":%i", ntohs(address->ipv6.sin6_port));
    if (bytes_written < 0) return false;

    return true;
}


bool
sa_unspec(union sa *address)
{


@@ 102,13 132,7 @@ sa_from_sockaddr_in(union sa *address, struct sockaddr_in const *ipv4_address)

    address->ipv4 = *ipv4_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;

    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;
    if (!update_ipv4_str(address)) return false;

    return true;
}


@@ 123,13 147,7 @@ sa_from_sockaddr_in6(union sa *address, struct sockaddr_in6 const *ipv6_address)

    address->ipv6 = *ipv6_address;

    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 bytes_written = sprintf(s, ":%i", ntohs(ipv6_address->sin6_port));
    if (bytes_written < 0) return false;
    if (!update_ipv6_str(address)) return false;

    return true;
}


@@ 158,6 176,21 @@ sa_prepare(union sa *address, socklen_t *len)
}


bool
sa_update(union sa *address)
{
    if (!address) return false;

    if (AF_INET == address->generic.sa_family) {
        if (!update_ipv4_str(address)) return false;
    } else if (AF_INET6 == address->generic.sa_family) {
        if (!update_ipv6_str(address)) return false;
    }

    return true;
}


extern sa_family_t
sa_family(union sa const *address);


M src/sa/sa.h => src/sa/sa.h +7 -1
@@ 103,12 103,18 @@ bool
sa_from_sockaddr_un(union sa *address, struct sockaddr_un const *local_address);


// # Initialize an `sa` union and length for receiving a socket address
// # Prepare an `sa` union and length for receiving a socket address

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


// # Update an `sa` union after receiving a socket address

bool
sa_update(union sa *address);


// # Get info about a socket address

inline sa_family_t

M src/sa/sa_tests.c => src/sa/sa_tests.c +103 -0
@@ 547,6 547,102 @@ test_sa_prepare_for_null(void)


static void
test_sa_update_for_unspecified()
{
    union sa address = {
            .generic = {
                    .sa_family = AF_UNSPEC,
            }
    };

    bool success = sa_update(&address);

    assert(success);
    assert(STR_EQ("(unspecified)", sa_str(&address)));
}


static void
test_sa_update_for_ipv4()
{
    union sa address = {
            .ipv4 = {
                    .sin_family=AF_INET,
                    .sin_addr=(struct in_addr){
                            .s_addr=htonl(INADDR_LOOPBACK)
                    },
                    .sin_port=htons(8080),
            }
    };

    bool success = sa_update(&address);

    assert(success);
    assert(STR_EQ("127.0.0.1:8080", sa_str(&address)));
}


static void
test_sa_update_for_ipv6()
{
    union sa address = {
            .ipv6 = {
                    .sin6_family=AF_INET6,
                    .sin6_addr=in6addr_loopback,
                    .sin6_port=htons(8080),
            }
    };

    bool success = sa_update(&address);

    assert(success);
    assert(STR_EQ("::1:8080", sa_str(&address)));
}


static void
test_sa_update_for_local()
{
    union sa address = {
            .local = {
                    .sun_family = AF_UNIX,
                    .sun_path = "/var/dood/http",
            }
    };

    bool success = sa_update(&address);

    assert(success);
    assert(STR_EQ("/var/dood/http", sa_str(&address)));
}


static void
test_sa_update_for_unsupported()
{
    union sa address = {
            .generic = {
                    .sa_family = 0xff,
            }
    };

    bool success = sa_update(&address);

    assert(success);
    assert(STR_EQ("(unsupported)", sa_str(&address)));
}


static void
test_sa_update_for_null()
{
    bool success = sa_update(NULL);

    assert(!success);
}


static void
test_sa_family_for_unspecified(void)
{
    union sa address = {


@@ 748,6 844,13 @@ main(int argc, char *argv[])
    test_sa_prepare();
    test_sa_prepare_for_null();

    test_sa_update_for_unspecified();
    test_sa_update_for_ipv4();
    test_sa_update_for_ipv6();
    test_sa_update_for_local();
    test_sa_update_for_unsupported();
    test_sa_update_for_null();

    test_sa_family_for_unspecified();
    test_sa_family_for_ipv4();
    test_sa_family_for_ipv6();