~donmcc/ood

d988a4a0f47fd3c574ee17835e3634bbc383725e — Don McCaughey 10 months ago ab44f36
Add `sa_family()` helper function.

The `sa_family()` function is a NULL-safe way to check the type of a
`sa` union.

Also filled out the `CMakeLists.txt` file for the `sa` lib as a step in
making it stand-alone.  Also added a readme and license file for `sa`
and fixed a typo in the main readme.
7 files changed, 225 insertions(+), 3 deletions(-)

M README.md
M src/sa/CMakeLists.txt
A src/sa/LICENSE
A src/sa/README.md
M src/sa/sa.c
M src/sa/sa.h
M src/sa/sa_tests.c
M README.md => README.md +1 -1
@@ 7,7 7,7 @@
_Ood_ is available under a BSD-style license.  See the [LICENSE](https://git.sr.ht/~donmcc/ood/tree/main/item/LICENSE)
file for details.

## Building From Soure
## Building From Source

_Ood_ is tested on macOS, Linux, FreeBSD and OpenBSD.  Building it requires a
C toolchain and [CMake](https://cmake.org) 3.13 or later.

M src/sa/CMakeLists.txt => src/sa/CMakeLists.txt +36 -0
@@ 1,3 1,36 @@
cmake_minimum_required(VERSION 3.13...3.24)
project(sa
        VERSION 0.0.1
        DESCRIPTION "SA: Socket Addresses"
        HOMEPAGE_URL https://git.sr.ht/~donmcc/sa
        LANGUAGES C
        )
set(CMAKE_C_STANDARD 99)
enable_testing()


# ----- configuration options -----

option(ADDRESS_SANITIZER "Enable the address sanitizer")
if(ADDRESS_SANITIZER)
    add_compile_options(-fsanitize=address -g)
    add_link_options(-fsanitize=address)
endif()

option(COVERAGE "Enable code coverage analysis")
if(COVERAGE)
    add_compile_options(--coverage -g -O0)
    add_link_options(--coverage)
endif()

option(WALL "Enable all warnings")
if(WALL)
    add_compile_options(-Wall -Werror)
endif()


# ----- build -----

add_library(sa STATIC
        sa.h
        sa.c


@@ 6,6 39,9 @@ target_include_directories(sa
        PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}"
        )


# ----- test -----

add_executable(sa_tests
        sa_tests.c
        )

A src/sa/LICENSE => src/sa/LICENSE +23 -0
@@ 0,0 1,23 @@
sa
Copyright (c) 2022, Don McCaughey. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

- Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

A src/sa/README.md => src/sa/README.md +83 -0
@@ 0,0 1,83 @@
# _SA_: Socket Addresses

A wrapper around the polymorphic `sockaddr` family of structs and a set of 
helper functions for common socket address chores.

## Overview

The `sa` union holds storage for each of the `sockaddr`
[type puns](https://en.wikipedia.org/wiki/Type_punning) along with space for
a string representation.  For the `sockaddr_un` address type, the `sun_path`
field is used as the string representation.

The union contains fields `.ipv4`, `.ipv6` and `.local` which correspond to 
structs `sockaddr_in`, `sockaddr_in6` and `sockaddr_un` respectively.
Additionally, `.generic` corresponds to a `sockaddr` struct and `.storage` to
a `sockaddr_storage` struct.

NB: Because the symbol `unix` is #defined in some Unix C compilers, `sa`
uses the name `local` for Unix domain sockets (often referred to as "local"
sockets).

The inline function `sa_family()` returns the discriminant value which tells
which field is currently active.  In an initialized `sa` union, the family will
be either `AF_UNSPEC`, indicating a blank or zeroed-out `sa` union, or one of
`AF_INET`, `AF_INET6` or `AF_UNIX`.

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

    // initialize 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));
    
    // receive a socket address
    union sa client_address;
    socklen_t len;
    sa_prepare(&client_address, &len);
    int client_fd = accept(fd, &client_address.generic, &len);

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

    union sa address;
    sa_from_in_addr_and_port(&address, INADDR_LOOPBACK, 8080);
    printf("%s", sa_str(&address));
    // prints "127.0.0.1:8080"


## License

_SA_ is available under a BSD-style license.  See the 
[LICENSE](https://git.sr.ht/~donmcc/sa/tree/main/item/LICENSE) file for details.

## Building From Source

_SA_ is tested on macOS, Linux, FreeBSD and OpenBSD.  Building it requires a
C toolchain and [CMake](https://cmake.org) 3.13 or later.

    git clone https://git.sr.ht/~donmcc/sa
    cd sa
    cmake -S . -B tmp
    cmake --build tmp --target all test

### Build Options

To build with the [Address Sanitizer][https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#Warning-Options]
enabled, set the `ADDRESS_SANITIZER` option to `ON`.

    cmake -S . -B tmp -DADDRESS_SANITIZER=ON

Mac users note that the `clang` that ships with Xcode accepts the
`-fsanitize=address` flag, but doesn't actually include the Address Sanitizer.

Set the `COVERAGE` option to `ON` to generate coverage files.

    cmake -S . -B tmp -DCOVERAGE=ON

Set the `WALL` option to `ON` turns on [additional warnings][https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#Warning-Options]
using the `-Wall` compiler option and treats warnings as errors.  `WALL` is off
by default but should be turned on for development and integration builds.

    cmake -S . -B tmp -DCOVERAGE=ON -DWALL=ON

M src/sa/sa.c => src/sa/sa.c +4 -0
@@ 158,6 158,10 @@ sa_prepare(union sa *address, socklen_t *len)
}


extern sa_family_t
sa_family(union sa const *address);


socklen_t
sa_len(union sa const *address)
{

M src/sa/sa.h => src/sa/sa.h +8 -2
@@ 32,12 32,12 @@

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

        // initializing a socket address for use
        // initialize 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
        // receive a socket address
        union sa client_address;
        socklen_t len;
        sa_prepare(&client_address, &len);


@@ 111,6 111,12 @@ sa_prepare(union sa *address, socklen_t *len);

// # Get info about a socket address

inline sa_family_t
sa_family(union sa const *address)
{
    return address ? address->generic.sa_family : AF_UNSPEC;
}

socklen_t
sa_len(union sa const *address);


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


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

    assert(AF_UNSPEC == sa_family(&address));
}


static void
test_sa_family_for_ipv4(void)
{
    union sa address;
    sa_from_in_addr_and_port(&address, INADDR_LOOPBACK, 8080);

    assert(AF_INET == sa_family(&address));
}


static void
test_sa_family_for_ipv6(void)
{
    union sa address;
    sa_from_in6_addr_and_port(&address, in6addr_loopback, 8080);

    assert(AF_INET6 == sa_family(&address));
}


static void
test_sa_family_for_local(void)
{
    union sa address;
    sa_from_path(&address, "/var/dood/http");

    assert(AF_UNIX == sa_family(&address));
}


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

    assert(0xff == sa_family(&address));
}


static void
test_sa_family_for_null(void)
{
    assert(AF_UNSPEC == sa_family(NULL));
}


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


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

    test_sa_family_for_unspecified();
    test_sa_family_for_ipv4();
    test_sa_family_for_ipv6();
    test_sa_family_for_local();
    test_sa_family_for_unsupported();
    test_sa_family_for_null();

    test_sa_len_for_unspecified();
    test_sa_len_for_ipv4();
    test_sa_len_for_ipv6();