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();