diff --git a/.github/docker/wolfsentry-check-all/Dockerfile b/.github/docker/wolfsentry-check-all/Dockerfile
new file mode 100644
index 00000000..fdce8d16
--- /dev/null
+++ b/.github/docker/wolfsentry-check-all/Dockerfile
@@ -0,0 +1,63 @@
+FROM ubuntu:26.04@sha256:157dc3d6e4a3e4f2c68d12df9df9ed6e808b59af6d4c3757b432b4f519916725
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries
+
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends \
+ autoconf \
+ automake \
+ ca-certificates \
+ clang \
+ clang-tidy \
+ cppcheck \
+ curl \
+ doxygen \
+ dwarves \
+ gcc \
+ gcc-15 \
+ gcc-15-multilib \
+ gcc-arm-none-eabi \
+ gcc-multilib \
+ git \
+ graphviz \
+ libnewlib-arm-none-eabi \
+ libclang-rt-21-dev \
+ libnotify-dev \
+ libtool \
+ llvm \
+ make \
+ patch \
+ qemu-user \
+ texlive-fonts-recommended \
+ texlive-latex-base \
+ texlive-latex-extra \
+ texlive-latex-recommended \
+ texlive-plain-generic \
+ util-linux \
+ valgrind \
+ xz-utils \
+ zip \
+ && rm -rf /var/lib/apt/lists/*
+
+ARG ARMEB_TOOLCHAIN=armebv7-eabihf--glibc--stable-2026.08-1
+ARG ARMEB_TOOLCHAIN_SHA256=4d93a7b26c0909ea7574e98246522cf6b77491d43a7ca7aa08ae1c29ecf07b0d
+
+# Buildroot's compiler wrapper finds its real binary from argv[0], so expose the
+# armeb-linux-gnueabihf names as exec shims, and put the sanitizer runtimes in the sysroot qemu loads from.
+RUN curl -fsSL --retry 5 --retry-all-errors -o /tmp/armeb.tar.xz \
+ "https://toolchains.bootlin.com/downloads/releases/toolchains/armebv7-eabihf/tarballs/${ARMEB_TOOLCHAIN}.tar.xz" \
+ && echo "${ARMEB_TOOLCHAIN_SHA256} /tmp/armeb.tar.xz" | sha256sum -c - \
+ && tar -xJf /tmp/armeb.tar.xz -C /opt \
+ && rm /tmp/armeb.tar.xz \
+ && tc="/opt/${ARMEB_TOOLCHAIN}" \
+ && target="${tc}/armeb-buildroot-linux-gnueabihf" \
+ && for f in "${tc}"/bin/armeb-buildroot-linux-gnueabihf-*; do \
+ case "$f" in *.br_real) continue ;; esac; \
+ shim="/usr/local/bin/$(basename "$f" | sed 's/^armeb-buildroot-/armeb-/')"; \
+ printf '#!/bin/sh\nexec %s "$@"\n' "$f" > "$shim"; \
+ chmod +x "$shim"; \
+ done \
+ && cp -a "${target}"/lib/libasan.so* "${target}"/lib/libubsan.so* "${target}"/lib/libatomic.so* "${target}/sysroot/lib/" \
+ && ln -s "${target}/sysroot" /usr/armeb-linux-gnueabihf
diff --git a/.github/docker/wolfsentry-ci/Dockerfile b/.github/docker/wolfsentry-ci/Dockerfile
index d864936d..91643186 100644
--- a/.github/docker/wolfsentry-ci/Dockerfile
+++ b/.github/docker/wolfsentry-ci/Dockerfile
@@ -1,5 +1,7 @@
FROM ghcr.io/wolfssl/wolfcose-ci@sha256:c073c7afb4cd3f296b8c1a305db2f718530b7a8aca899482629aee3a5cf8bfbc
+RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries
+
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
bubblewrap \
diff --git a/.github/workflows/check-all.yml b/.github/workflows/check-all.yml
new file mode 100644
index 00000000..e362d20e
--- /dev/null
+++ b/.github/workflows/check-all.yml
@@ -0,0 +1,90 @@
+name: make check-all
+
+run-name: ${{ inputs.nightly_run_id || github.workflow }}
+
+on:
+ push:
+ branches: [ 'master', 'release/**' ]
+ pull_request:
+ branches: [ '**' ]
+ workflow_dispatch:
+ inputs:
+ nightly_run_id:
+ description: Nightly coordinator run ID
+ required: false
+ type: string
+
+permissions:
+ contents: read
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+defaults:
+ run:
+ shell: bash -e {0}
+
+jobs:
+ check-all:
+ name: make check-all
+ runs-on: ubuntu-latest
+ timeout-minutes: 240
+ env:
+ # built from .github/docker/wolfsentry-check-all by publish-ci-image.yml
+ CHECK_ALL_IMAGE: ghcr.io/wolfssl/wolfsentry-check-all@sha256:99be2efa32df4ea09a87706286b914e949e4b636787759dd4ae806f4b1aaae84
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.pull_request.head.sha || github.sha }}
+ path: wolfsentry
+ fetch-depth: 0
+ - uses: actions/checkout@v4
+ with:
+ repository: FreeRTOS/FreeRTOS-Kernel
+ ref: dbf70559b27d39c1fdb68dfb9a32140b6a6777a0
+ path: third/FreeRTOS/FreeRTOS/Source
+ - uses: actions/checkout@v4
+ with:
+ repository: lwip-tcpip/lwip
+ ref: 3fe8d2fc43a9b69f7ed28c63d44a7744f9c0def9
+ path: third/lwip
+ - uses: actions/checkout@v4
+ with:
+ repository: json-schema-org/JSON-Schema-Test-Suite
+ ref: 245a45abc90190c0bff04d97e6d2b541b4bb9f12
+ path: JSON-Schema-Test-Suite
+ - uses: actions/checkout@v4
+ with:
+ repository: wolfSSL/wolfssl
+ ref: master
+ path: wolfssl
+ - name: Apply the lwIP packet filter patch
+ run: patch --directory third/lwip --strip 1 < wolfsentry/lwip/LWIP_PACKET_FILTER_API.patch
+ # stand-in until the maintainer's local scripts/analyzer-config.sh is available; cppcheck needs the file to exist
+ - name: Provide scripts/analyzer-config.sh
+ run: touch wolfsentry/scripts/analyzer-config.sh
+ - name: Lower mmap randomization for ThreadSanitizer
+ run: sudo sysctl -w vm.mmap_rnd_bits=28
+ - name: Pull the check-all image
+ run: |
+ retry() { for attempt in 1 2 3; do "$@" && return 0; [ "$attempt" -lt 3 ] && sleep $((attempt * 15)); done; return 1; }
+ retry docker pull "$CHECK_ALL_IMAGE"
+ # make pre-push-check runs scripts/pre-push.sh, which runs make check-all on this commit.
+ # gcc 14 arm-none-eabi needs larger FreeRTOS image limits; the JSON corpus sits on tmpfs because
+ # qemu-user cannot pass ext4's 64-bit directory cookies to the 32-bit armeb tests.
+ - name: make pre-push-check
+ run: |
+ docker run --rm \
+ --volume "$GITHUB_WORKSPACE:/workspace" \
+ --workdir /workspace/wolfsentry \
+ --mount type=tmpfs,destination=/workspace/wolfsentry/tests/JSON-Schema-Test-Suite \
+ --env TMPDIR=/tmp \
+ --env CLANG_PATH=/usr/bin \
+ --env NOTIFICATION_SANDBOX= \
+ --env FREERTOS_ARM32_MAX_IMAGE_SIZE=270000 \
+ --env FREERTOS_ARM32_MAX_SECTIONS_SIZE=145000 \
+ --env FREERTOS_ARM32_THUMB_MAX_SECTIONS_SIZE=145000 \
+ "$CHECK_ALL_IMAGE" \
+ bash -ec 'cp -a /workspace/JSON-Schema-Test-Suite/. tests/JSON-Schema-Test-Suite/; git config --global --add safe.directory "*"; make pre-push-check'
diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml
index 5020f7f6..19bc7606 100644
--- a/.github/workflows/examples.yml
+++ b/.github/workflows/examples.yml
@@ -36,7 +36,9 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Build and start the lwIP network
- run: docker compose up --build --detach
+ run: |
+ retry() { for attempt in 1 2 3; do "$@" && return 0; [ "$attempt" -lt 3 ] && sleep $((attempt * 15)); done; return 1; }
+ retry docker compose up --build --detach
- name: Exercise allowed and rejected traffic
run: |
for attempt in $(seq 1 30); do
@@ -94,7 +96,8 @@ jobs:
path: wolfip
- name: Build the wolfSentry CI image
run: |
- docker build \
+ retry() { for attempt in 1 2 3; do "$@" && return 0; [ "$attempt" -lt 3 ] && sleep $((attempt * 15)); done; return 1; }
+ retry docker build \
--file wolfsentry/.github/docker/wolfsentry-ci/Dockerfile \
--tag wolfsentry-ci:local wolfsentry
- name: Build and run the wolfIP example
@@ -146,7 +149,8 @@ jobs:
path: wolfssl
- name: Build the wolfSentry CI image
run: |
- docker build \
+ retry() { for attempt in 1 2 3; do "$@" && return 0; [ "$attempt" -lt 3 ] && sleep $((attempt * 15)); done; return 1; }
+ retry docker build \
--file wolfsentry/.github/docker/wolfsentry-ci/Dockerfile \
--tag wolfsentry-ci:local wolfsentry
- name: Build and exercise the notification demo
@@ -195,7 +199,8 @@ jobs:
path: lwip
- name: Check out lwIP contrib
run: |
- git clone --branch STABLE-2_1_0_RELEASE --depth 1 \
+ retry() { for attempt in 1 2 3; do "$@" && return 0; [ "$attempt" -lt 3 ] && sleep $((attempt * 15)); done; return 1; }
+ retry git clone --branch STABLE-2_1_0_RELEASE --depth 1 \
https://git.savannah.nongnu.org/git/lwip/lwip-contrib.git \
lwip/contrib
test "$(git -C lwip/contrib rev-parse HEAD)" = \
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 9ef5f4f1..a2bff711 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -33,6 +33,7 @@ jobs:
const POLL_TIMEOUT_MS = 120 * 60 * 1000;
const dispatchToken = `nightly-${context.runId}-${context.runAttempt}`;
const workflows = [
+ 'check-all.yml',
'codeql.yml',
'coverage.yml',
'examples.yml',
diff --git a/.github/workflows/publish-ci-image.yml b/.github/workflows/publish-ci-image.yml
index 0ea0986c..85f8fb30 100644
--- a/.github/workflows/publish-ci-image.yml
+++ b/.github/workflows/publish-ci-image.yml
@@ -5,6 +5,7 @@ on:
branches: [ '**' ]
paths:
- '.github/docker/wolfsentry-ci/**'
+ - '.github/docker/wolfsentry-check-all/**'
- '.github/workflows/publish-ci-image.yml'
workflow_dispatch:
@@ -80,3 +81,68 @@ jobs:
scan-build --help > /dev/null
/usr/bin/python3 -c 'import dbus, gi'
make -j2 test
+
+ publish-check-all:
+ name: Publish check-all image
+ if: github.repository == 'wolfSSL/wolfsentry'
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ permissions:
+ contents: read
+ packages: write
+ outputs:
+ digest: ${{ steps.build.outputs.digest }}
+ steps:
+ - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
+ - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3
+ - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Build and push image
+ id: build
+ uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
+ with:
+ context: .
+ file: .github/docker/wolfsentry-check-all/Dockerfile
+ platforms: linux/amd64
+ pull: true
+ push: true
+ tags: |
+ ghcr.io/wolfssl/wolfsentry-check-all:sha-${{ github.sha }}
+ ghcr.io/wolfssl/wolfsentry-check-all:${{ github.ref == 'refs/heads/master' && 'latest' || 'edge' }}
+ labels: |
+ org.opencontainers.image.source=https://github.com/${{ github.repository }}
+ org.opencontainers.image.revision=${{ github.sha }}
+ cache-from: type=registry,ref=ghcr.io/wolfssl/wolfsentry-check-all:latest
+ cache-to: type=inline
+
+ smoke-check-all:
+ name: Test published check-all image
+ needs: publish-check-all
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ permissions:
+ contents: read
+ packages: read
+ container:
+ image: ghcr.io/wolfssl/wolfsentry-check-all@${{ needs.publish-check-all.outputs.digest }}
+ credentials:
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ defaults:
+ run:
+ shell: bash -e {0}
+ steps:
+ - name: Exercise image
+ run: |
+ echo "digest: ${{ needs.publish-check-all.outputs.digest }}"
+ gcc-15 --version
+ clang --version
+ clang-tidy --version
+ cppcheck --version
+ arm-none-eabi-gcc --version
+ armeb-linux-gnueabihf-gcc --version
+ qemu-armeb --version
+ test -e /usr/armeb-linux-gnueabihf/lib/libasan.so.8
diff --git a/.github/workflows/wolfssl-versions.yml b/.github/workflows/wolfssl-versions.yml
index 3d9f8787..ed46b96f 100644
--- a/.github/workflows/wolfssl-versions.yml
+++ b/.github/workflows/wolfssl-versions.yml
@@ -37,9 +37,10 @@ jobs:
- name: Build matrix
id: versions
run: |
- LATEST=$(git ls-remote --tags --refs \
- https://github.com/wolfSSL/wolfssl.git 'v*-stable' \
- | awk -F/ '{print $NF}' | sort -V | tail -n 1)
+ retry() { for attempt in 1 2 3; do "$@" && return 0; [ "$attempt" -lt 3 ] && sleep $((attempt * 15)); done; return 1; }
+ TAGS=$(retry git ls-remote --tags --refs \
+ https://github.com/wolfSSL/wolfssl.git 'v*-stable')
+ LATEST=$(awk -F/ '{print $NF}' <<<"$TAGS" | sort -V | tail -n 1)
test -n "$LATEST"
if [[ ! "$LATEST" =~ ^v[0-9]+(\.[0-9]+)+-stable$ ]]; then
echo "Unexpected wolfSSL stable tag: $LATEST" >&2
@@ -83,7 +84,8 @@ jobs:
env:
WOLFSSL_REF: ${{ matrix.ref }}
run: |
- git clone --depth 1 --branch "$WOLFSSL_REF" \
+ retry() { for attempt in 1 2 3; do "$@" && return 0; [ "$attempt" -lt 3 ] && sleep $((attempt * 15)); done; return 1; }
+ retry git clone --depth 1 --branch "$WOLFSSL_REF" \
https://github.com/wolfSSL/wolfssl.git "$HOME/wolfssl"
cd "$HOME/wolfssl"
./autogen.sh
diff --git a/Makefile.analyzers b/Makefile.analyzers
index fb91cb1c..ed895101 100644
--- a/Makefile.analyzers
+++ b/Makefile.analyzers
@@ -432,7 +432,7 @@ minimal-threaded-build-test:
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-minimal-threaded-builds" clean
@echo "passed: minimal threaded build test."
-LIBRARY_DEPENDENCY_SUPPRESSIONS_BASE := U wolfsentry_|U json_|U (inet_ntop|inet_pton|memchr|memcmp|memcpy|__memcpy_chk|memmove|memset|__popcountdi2|snprintf|__snprintf_chk|strchr|strcmp|strlen|strncasecmp|strncmp|strtod|strtol|strtoll|strtoul|strtoull)$$
+LIBRARY_DEPENDENCY_SUPPRESSIONS_BASE := U wolfsentry_|U json_|U (inet_ntop|inet_pton|memchr|memcmp|memcpy|__memcpy_chk|memmove|memset|__popcountdi2|snprintf|__snprintf_chk|strchr|strcmp|strlen|strncasecmp|strncmp|strtod|strtol|strtoll|strtoul|strtoull|__isoc23_strtol|__isoc23_strtoll|__isoc23_strtoul|__isoc23_strtoull)$$
.PHONY: library-dependency-singlethreaded-build-test
library-dependency-singlethreaded-build-test:
@@ -566,6 +566,9 @@ else
LWIP_TOP_FOR_TEST := $(SRC_TOP)/../third/lwip
endif
+FREERTOS_ARM32_MAX_IMAGE_SIZE ?= 240000
+FREERTOS_ARM32_THUMB_MAX_SECTIONS_SIZE ?= 110000
+
.PHONY: freertos-arm32-thumb-build-test
freertos-arm32-thumb-build-test:
@[ -d $(FREERTOS_TOP_FOR_TEST)/FreeRTOS/Source/. ] || ( echo '$@: $(FREERTOS_TOP_FOR_TEST)/FreeRTOS/Source not found.' >&2; exit 1)
@@ -574,16 +577,18 @@ freertos-arm32-thumb-build-test:
@rm -rf "$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds"
# first build without FUNCTION_SECTIONS=1 to detect any bloat or unresolved symbols anywhere in the library.
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds" HOST=arm-none-eabi RUNTIME=FreeRTOS-lwIP FREERTOS_TOP=$(FREERTOS_TOP_FOR_TEST) LWIP_TOP=$(LWIP_TOP_FOR_TEST) EXTRA_CFLAGS+='-mthumb -mcpu=cortex-m7 -specs=nano.specs -Wno-inline' NO_STDIO_STREAMS=1 '$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip' UNITTEST_LIST=test_lwip OPTIM='-Os' STRIPPED=1
- @TEST_LWIP_SZ=$$(stat --format='%s' '$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip'); if [[ "$$TEST_LWIP_SZ" -gt 230000 ]]; then echo "$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip is unexpectedly large ($$TEST_LWIP_SZ bytes)."; exit 1; fi
+ @TEST_LWIP_SZ=$$(stat --format='%s' '$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip'); if [[ "$$TEST_LWIP_SZ" -gt $(FREERTOS_ARM32_MAX_IMAGE_SIZE) ]]; then echo "$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip is unexpectedly large ($$TEST_LWIP_SZ bytes)."; exit 1; fi
@rm -rf '$(BUILD_PARENT)/'wolfsentry-freertos-arm32-thumb-test-builds/tests/{freertos,lwip}
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds" HOST=arm-none-eabi RUNTIME=FreeRTOS-lwIP FREERTOS_TOP=$(FREERTOS_TOP_FOR_TEST) LWIP_TOP=$(LWIP_TOP_FOR_TEST) EXTRA_CFLAGS+='-mthumb -mcpu=cortex-m7' UNITTEST_LIST=test_lwip clean
# now rebuild with FUNCTION_SECTIONS=1, to test expected text size on a parsimonious link.
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds" HOST=arm-none-eabi RUNTIME=FreeRTOS-lwIP FREERTOS_TOP=$(FREERTOS_TOP_FOR_TEST) LWIP_TOP=$(LWIP_TOP_FOR_TEST) EXTRA_CFLAGS+='-mthumb -mcpu=cortex-m7 -specs=nano.specs -Wno-inline' NO_STDIO_STREAMS=1 '$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip' UNITTEST_LIST=test_lwip OPTIM='-Os' STRIPPED=1 FUNCTION_SECTIONS=1
- @TEST_LWIP_SZ=$$(stat --format='%s' '$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip'); if [[ "$$TEST_LWIP_SZ" -gt 110000 ]]; then echo "$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip is unexpectedly large ($$TEST_LWIP_SZ bytes)."; exit 1; fi
+ @TEST_LWIP_SZ=$$(stat --format='%s' '$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip'); if [[ "$$TEST_LWIP_SZ" -gt $(FREERTOS_ARM32_THUMB_MAX_SECTIONS_SIZE) ]]; then echo "$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds/tests/test_lwip is unexpectedly large ($$TEST_LWIP_SZ bytes)."; exit 1; fi
@rm -rf '$(BUILD_PARENT)/'wolfsentry-freertos-arm32-thumb-test-builds/tests/{freertos,lwip}
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-freertos-arm32-thumb-test-builds" HOST=arm-none-eabi RUNTIME=FreeRTOS-lwIP FREERTOS_TOP=$(FREERTOS_TOP_FOR_TEST) LWIP_TOP=$(LWIP_TOP_FOR_TEST) EXTRA_CFLAGS+='-mthumb -mcpu=cortex-m7' UNITTEST_LIST=test_lwip clean
@echo "passed: freertos-arm32-thumb-build test."
+FREERTOS_ARM32_MAX_SECTIONS_SIZE ?= 100000
+
.PHONY: freertos-arm32-build-test
freertos-arm32-build-test:
@[ -d $(FREERTOS_TOP_FOR_TEST)/FreeRTOS/Source/. ] || ( echo '$@: $(FREERTOS_TOP_FOR_TEST)/FreeRTOS/Source not found.' >&2; exit 1)
@@ -592,12 +597,12 @@ freertos-arm32-build-test:
@rm -rf "$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds"
# first build without FUNCTION_SECTIONS=1 to detect any bloat or unresolved symbols anywhere in the library.
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds" HOST=arm-none-eabi RUNTIME=FreeRTOS-lwIP FREERTOS_TOP=$(FREERTOS_TOP_FOR_TEST) LWIP_TOP=$(LWIP_TOP_FOR_TEST) EXTRA_CFLAGS+='-mcpu=cortex-m3 -specs=nano.specs -Wno-inline' NO_STDIO_STREAMS=1 '$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip' UNITTEST_LIST=test_lwip OPTIM='-Os' STRIPPED=1
- @TEST_LWIP_SZ=$$(stat --format='%s' '$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip'); if [[ "$$TEST_LWIP_SZ" -gt 230000 ]]; then echo "$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip is unexpectedly large ($$TEST_LWIP_SZ bytes)."; exit 1; fi
+ @TEST_LWIP_SZ=$$(stat --format='%s' '$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip'); if [[ "$$TEST_LWIP_SZ" -gt $(FREERTOS_ARM32_MAX_IMAGE_SIZE) ]]; then echo "$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip is unexpectedly large ($$TEST_LWIP_SZ bytes)."; exit 1; fi
@rm -rf '$(BUILD_PARENT)/'wolfsentry-freertos-arm32-test-builds/tests/{freertos,lwip}
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds" HOST=arm-none-eabi RUNTIME=FreeRTOS-lwIP FREERTOS_TOP=$(FREERTOS_TOP_FOR_TEST) LWIP_TOP=$(LWIP_TOP_FOR_TEST) EXTRA_CFLAGS+='-mcpu=cortex-m3' UNITTEST_LIST=test_lwip clean
# now rebuild with FUNCTION_SECTIONS=1, to test expected text size on a parsimonious link.
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds" HOST=arm-none-eabi RUNTIME=FreeRTOS-lwIP FREERTOS_TOP=$(FREERTOS_TOP_FOR_TEST) LWIP_TOP=$(LWIP_TOP_FOR_TEST) EXTRA_CFLAGS+='-mcpu=cortex-m3 -specs=nano.specs -Wno-inline' NO_STDIO_STREAMS=1 '$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip' UNITTEST_LIST=test_lwip OPTIM='-Os' STRIPPED=1 FUNCTION_SECTIONS=1
- @TEST_LWIP_SZ=$$(stat --format='%s' '$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip'); if [[ "$$TEST_LWIP_SZ" -gt 100000 ]]; then echo "$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip is unexpectedly large ($$TEST_LWIP_SZ bytes)."; exit 1; fi
+ @TEST_LWIP_SZ=$$(stat --format='%s' '$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip'); if [[ "$$TEST_LWIP_SZ" -gt $(FREERTOS_ARM32_MAX_SECTIONS_SIZE) ]]; then echo "$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds/tests/test_lwip is unexpectedly large ($$TEST_LWIP_SZ bytes)."; exit 1; fi
@rm -rf '$(BUILD_PARENT)/'wolfsentry-freertos-arm32-test-builds/tests/{freertos,lwip}
@$(MAKE) $(EXTRA_MAKE_FLAGS) $(QUIET_FLAG) -f $(THIS_MAKEFILE) VERY_QUIET=1 BUILD_TOP="$(BUILD_PARENT)/wolfsentry-freertos-arm32-test-builds" HOST=arm-none-eabi RUNTIME=FreeRTOS-lwIP FREERTOS_TOP=$(FREERTOS_TOP_FOR_TEST) LWIP_TOP=$(LWIP_TOP_FOR_TEST) EXTRA_CFLAGS+='-mcpu=cortex-m3' UNITTEST_LIST=test_lwip clean
@echo "passed: freertos-arm32-build test."
diff --git a/doc/json_configuration.md b/doc/json_configuration.md
index 9aa1bc86..dcb00239 100644
--- a/doc/json_configuration.md
+++ b/doc/json_configuration.md
@@ -239,7 +239,7 @@ Each event is composed of the following elements, all of which are optional exce
* `label` -- The name by which the event is identified. See the definition of `label` in the ABNF grammar below for permissible values.
-* `priority` -- The priority of routes that have this event as their `parent-event` (see `routes` below). Lower number means higher priority.
+* `priority` -- The priority of routes that have this event as their `parent-event` (see `routes` below). Lower number means higher priority. If omitted, the priority is 0, the highest priority.
* `config` -- The configuration to associate with routes with this `parent-event`, as above for `config-update`.
diff --git a/examples/Linux-LWIP/Dockerfile b/examples/Linux-LWIP/Dockerfile
index 372da55a..4bc61b62 100644
--- a/examples/Linux-LWIP/Dockerfile
+++ b/examples/Linux-LWIP/Dockerfile
@@ -1,5 +1,7 @@
FROM debian:12 AS builder
+RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries
+
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential ca-certificates cmake git libpcap-dev netbase patch \
&& rm -rf /var/lib/apt/lists/*
@@ -8,9 +10,10 @@ WORKDIR /src
COPY . wolfsentry
-RUN git clone --depth 1 --branch STABLE-2_1_3_RELEASE \
+RUN retry() { for attempt in 1 2 3; do "$@" && return 0; [ "$attempt" -lt 3 ] && sleep $((attempt * 15)); done; return 1; } \
+ && retry git clone --depth 1 --branch STABLE-2_1_3_RELEASE \
https://github.com/lwip-tcpip/lwip.git \
- && git clone --depth 1 --branch STABLE-2_1_0_RELEASE \
+ && retry git clone --depth 1 --branch STABLE-2_1_0_RELEASE \
https://git.savannah.nongnu.org/git/lwip/lwip-contrib.git \
lwip/contrib \
&& cd lwip \
diff --git a/examples/Linux-LWIP/echo.c b/examples/Linux-LWIP/echo.c
index 95e88816..288ed165 100644
--- a/examples/Linux-LWIP/echo.c
+++ b/examples/Linux-LWIP/echo.c
@@ -95,16 +95,32 @@ static err_t echo_msgaccept(void *arg, struct tcp_pcb *pcb, err_t err)
int echo_init(void)
{
struct tcp_pcb *pcb;
+ struct tcp_pcb *listen_pcb;
+ int r;
/* Create lwIP TCP instance */
pcb = tcp_new();
LWIP_DEBUGF(ECHO_DEBUG, ("echo_init: pcb: %x\n", pcb));
+ if (pcb == NULL)
+ return -1;
/* Bind port 11111 */
- int r = tcp_bind(pcb, IP_ADDR_ANY, 11111);
+ r = tcp_bind(pcb, IP_ADDR_ANY, 11111);
LWIP_DEBUGF(ECHO_DEBUG, ("echo_init: tcp_bind: %d\n", r));
- /* Enable listening */
- pcb = tcp_listen(pcb);
- LWIP_DEBUGF(ECHO_DEBUG, ("echo_init: listen-pcb: %x\n", pcb));
+ if (r != ERR_OK) {
+ if (tcp_close(pcb) != ERR_OK)
+ tcp_abort(pcb);
+ return -1;
+ }
+ /* Enable listening. tcp_listen() frees the original pcb only on success;
+ * on failure it returns NULL and leaves the original allocated. */
+ listen_pcb = tcp_listen(pcb);
+ LWIP_DEBUGF(ECHO_DEBUG, ("echo_init: listen-pcb: %x\n", listen_pcb));
+ if (listen_pcb == NULL) {
+ if (tcp_close(pcb) != ERR_OK)
+ tcp_abort(pcb);
+ return -1;
+ }
+ pcb = listen_pcb;
/* Set accept connection callback */
tcp_accept(pcb, echo_msgaccept);
diff --git a/examples/Linux-LWIP/ping.c b/examples/Linux-LWIP/ping.c
index ed497eba..ad03fea5 100644
--- a/examples/Linux-LWIP/ping.c
+++ b/examples/Linux-LWIP/ping.c
@@ -40,7 +40,8 @@ int ping_init(void)
struct raw_pcb *ping_pcb;
/* Create a new listener instance for ICMP messages */
ping_pcb = raw_new(IP_PROTO_ICMP);
- LWIP_ASSERT("ping_pcb != NULL", ping_pcb != NULL);
+ if (ping_pcb == NULL)
+ return -1;
/* Callback to ping_recv in ICMP message received */
raw_recv(ping_pcb, ping_recv, NULL);
diff --git a/examples/Linux-LWIP/sentry.c b/examples/Linux-LWIP/sentry.c
index acc24c7d..82489783 100644
--- a/examples/Linux-LWIP/sentry.c
+++ b/examples/Linux-LWIP/sentry.c
@@ -45,113 +45,143 @@ static wolfsentry_errcode_t test_action(
int sentry_init()
{
wolfsentry_errcode_t ret;
+ wolfsentry_errcode_t fini_ret;
wolfsentry_ent_id_t id;
-
+ char buf[512], err_buf[512];
+ struct wolfsentry_json_process_state *jps = NULL;
+ FILE *f = NULL;
+ size_t n;
struct wolfsentry_eventconfig config = { .route_private_data_size = 32, .route_private_data_alignment = 16 };
+
ret = wolfsentry_init(wolfsentry_build_settings, WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(NULL /* hpi */, NULL /* thread */), &config,
&wolfsentry);
if (ret < 0) {
fprintf(stderr, "wolfsentry_init() returned " WOLFSENTRY_ERROR_FMT "\n",
WOLFSENTRY_ERROR_FMT_ARGS(ret));
fprintf(stderr, "unable to initialize wolfSentry");
+ return ret;
}
- /* Insert the possible actions into wolfSentry */
- wolfsentry_action_insert( WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-insert",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert( WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-delete",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert( WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-match",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert( WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "notify-on-match",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert( WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-connect",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert( WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-connect2",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
-
- char buf[512], err_buf[512];
- struct wolfsentry_json_process_state *jps;
+ /* Insert the possible actions into wolfSentry. On any failure fall through
+ * to the cleanup path at the bottom so the context is not leaked. */
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ "handle-insert",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ "handle-delete",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ "handle-match",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ "notify-on-match",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ "handle-connect",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ "handle-connect2",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
/* Open the config file */
- FILE *f = fopen(wolfsentry_config_path, "r");
-
- if (f == NULL) {
- fprintf(stderr, "fopen(%s): %s\n",wolfsentry_config_path,strerror(errno));
- fprintf(stderr, "unable to open wolfSentry config file");
+ if (ret >= 0) {
+ f = fopen(wolfsentry_config_path, "r");
+ if (f == NULL) {
+ fprintf(stderr, "fopen(%s): %s\n",wolfsentry_config_path,strerror(errno));
+ fprintf(stderr, "unable to open wolfSentry config file");
+ ret = -1;
+ }
}
/* Initalize the wolfSentry JSON parser */
- if ((ret = wolfsentry_config_json_init(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- WOLFSENTRY_CONFIG_LOAD_FLAG_NONE,
- &jps)) < 0) {
- fprintf(stderr, "wolfsentry_config_json_init() returned "
- WOLFSENTRY_ERROR_FMT "\n",
- WOLFSENTRY_ERROR_FMT_ARGS(ret));
- fprintf(stderr, "error while initializing wolfSentry config parser");
+ if (ret >= 0) {
+ ret = wolfsentry_config_json_init(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE,
+ &jps);
+ if (ret < 0) {
+ fprintf(stderr, "wolfsentry_config_json_init() returned "
+ WOLFSENTRY_ERROR_FMT "\n",
+ WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ fprintf(stderr, "error while initializing wolfSentry config parser");
+ }
}
/* wolfSentry uses a streaming reader/parser for the config file */
- for (;;) {
- /* Read some data from the config file */
- size_t n = fread(buf, 1, sizeof buf, f);
- if ((n < sizeof buf) && ferror(f)) {
- fprintf(stderr, "fread(%s): %s\n",wolfsentry_config_path, strerror(errno));
- fprintf(stderr, "error while reading wolfSentry config file");
+ if (ret >= 0) {
+ for (;;) {
+ /* Read some data from the config file */
+ n = fread(buf, 1, sizeof buf, f);
+ if ((n < sizeof buf) && ferror(f)) {
+ fprintf(stderr, "fread(%s): %s\n",wolfsentry_config_path, strerror(errno));
+ fprintf(stderr, "error while reading wolfSentry config file");
+ ret = -1;
+ break;
+ }
+
+ /* Send the read data into the JSON parser */
+ ret = wolfsentry_config_json_feed(jps, buf, n, err_buf, sizeof err_buf);
+ if (ret < 0) {
+ fprintf(stderr, "%.*s\n", (int)sizeof err_buf, err_buf);
+ fprintf(stderr, "error while loading wolfSentry config file");
+ break;
+ }
+ if ((n < sizeof buf) && feof(f))
+ break;
}
+ }
- /* Send the read data into the JSON parser */
- ret = wolfsentry_config_json_feed(jps, buf, n, err_buf, sizeof err_buf);
- if (ret < 0) {
+ /* Clean up the JSON parser. This also commits the parsed config, so its
+ * result matters on the success path. */
+ if (jps != NULL) {
+ fini_ret = wolfsentry_config_json_fini(&jps, err_buf, sizeof err_buf);
+ if (fini_ret < 0) {
fprintf(stderr, "%.*s\n", (int)sizeof err_buf, err_buf);
fprintf(stderr, "error while loading wolfSentry config file");
+ if (ret >= 0)
+ ret = fini_ret;
}
- if ((n < sizeof buf) && feof(f))
- break;
}
- fclose(f);
- /* Clean up the JSON parser */
- if ((ret = wolfsentry_config_json_fini(&jps, err_buf, sizeof err_buf)) < 0) {
- fprintf(stderr, "%.*s\n", (int)sizeof err_buf, err_buf);
- fprintf(stderr, "error while loading wolfSentry config file");
+ if (f != NULL)
+ fclose(f);
+
+ /* Any failure after wolfsentry_init() must release the context. wolfSentry
+ * success codes are nonnegative but not necessarily zero, so normalize. */
+ if (ret < 0) {
+ if (wolfsentry != NULL)
+ (void)wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(&wolfsentry, NULL));
+ return ret;
}
return 0;
@@ -218,6 +248,11 @@ int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port
fprintf(stderr, "TCP Sentry action returned " WOLFSENTRY_ERROR_FMT "\n",
WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ /* Fail closed: any dispatch error denies the connection */
+ if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) < 0) {
+ return -1;
+ }
+
/* Check the result, if it contains "reject" then notify the caller */
if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) {
if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
@@ -261,6 +296,10 @@ int sentry_action_ping(const ip_addr_t *addr, u8_t type)
&action_results);
fprintf(stderr, "PING Sentry action returned " WOLFSENTRY_ERROR_FMT "\n",
WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ /* Fail closed: any dispatch error denies the packet */
+ if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) < 0) {
+ return -1;
+ }
if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) {
if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
return -1;
@@ -302,6 +341,10 @@ int sentry_action_mac(struct eth_addr *addr)
fprintf(stderr, "MAC Sentry action returned " WOLFSENTRY_ERROR_FMT "\n",
WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ /* Fail closed: any dispatch error denies the frame */
+ if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) < 0) {
+ return -1;
+ }
if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) {
if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
return -1;
diff --git a/examples/STM32-LWIP/README.md b/examples/STM32-LWIP/README.md
index 430cc6e7..f2c24b61 100644
--- a/examples/STM32-LWIP/README.md
+++ b/examples/STM32-LWIP/README.md
@@ -68,7 +68,10 @@ In `StartDefaultTask()`:
```c
/* USER CODE BEGIN 5 */
printf("Sentry init\n");
- sentry_init();
+ if (sentry_init() != 0) {
+ printf("Sentry init failed, all traffic will be denied\n");
+ Error_Handler();
+ }
printf("Echo init\n");
echo_init();
printf("Ping init\n");
diff --git a/examples/STM32-LWIP/Src/sentry.c b/examples/STM32-LWIP/Src/sentry.c
index 69b7f6ac..035f60a0 100644
--- a/examples/STM32-LWIP/Src/sentry.c
+++ b/examples/STM32-LWIP/Src/sentry.c
@@ -135,25 +135,28 @@ static wolfsentry_errcode_t test_action(
int sentry_init(void)
{
wolfsentry_errcode_t ret;
+ wolfsentry_errcode_t fini_ret;
wolfsentry_ent_id_t id;
+ struct wolfsentry_context *ctx = NULL;
struct wolfsentry_eventconfig config = { 0 };
char err_buf[512];
- struct wolfsentry_json_process_state *jps;
+ struct wolfsentry_json_process_state *jps = NULL;
config.route_private_data_size = 32;
config.route_private_data_alignment = 16;
ret = wolfsentry_init(wolfsentry_build_settings,
WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(NULL /* hpi */, NULL /* thread */), &config,
- &wolfsentry);
+ &ctx);
if (ret < 0)
{
printf("unable to initialize wolfSentry, " WOLFSENTRY_ERROR_FMT "\n",
WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ return ret;
}
/* Insert the possible actions into wolfSentry */
- wolfsentry_action_insert( WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ ret = wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
"handle-insert",
WOLFSENTRY_LENGTH_NULL_TERMINATED,
WOLFSENTRY_ACTION_FLAG_NONE,
@@ -161,83 +164,115 @@ int sentry_init(void)
NULL,
&id);
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-delete",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-match",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "notify-on-match",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-connect",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
-
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- "handle-connect2",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- test_action,
- NULL,
- &id);
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
+ "handle-delete",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
+ "handle-match",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
+ "notify-on-match",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
+ "handle-connect",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+
+ if (ret >= 0)
+ ret = wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
+ "handle-connect2",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action,
+ NULL,
+ &id);
+ if (ret < 0)
+ {
+ printf("unable to insert wolfSentry actions, " WOLFSENTRY_ERROR_FMT "\n",
+ WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ }
/* Initalize the wolfSentry JSON parser */
- if ((ret = wolfsentry_config_json_init(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
- WOLFSENTRY_CONFIG_LOAD_FLAG_NONE,
- &jps)) < 0)
+ if (ret >= 0)
{
- printf("error while initializing wolfSentry config parser, "
- WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ ret = wolfsentry_config_json_init(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE,
+ &jps);
+ if (ret < 0)
+ {
+ printf("error while initializing wolfSentry config parser, "
+ WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ }
}
/* wolfSentry uses a streaming reader/parser for the config file */
/* Send the read data into the JSON parser */
- ret = wolfsentry_config_json_feed(jps,
- (const unsigned char *)wolfsentry_config_data,
- strlen(wolfsentry_config_data), err_buf, sizeof err_buf);
-
- if (ret < 0)
- {
- printf("error while loading wolfSentry config data, "
- WOLFSENTRY_ERROR_FMT ", msg: %s\n",
- WOLFSENTRY_ERROR_FMT_ARGS(ret), err_buf);
- }
-
- /* Clean up the JSON parser */
- if ((ret = wolfsentry_config_json_fini(&jps, err_buf, sizeof err_buf)) < 0)
+ if (ret >= 0)
{
- printf("error while loading wolfSentry config data, "
- WOLFSENTRY_ERROR_FMT ", msg: %s\n",
- WOLFSENTRY_ERROR_FMT_ARGS(ret), err_buf);
+ ret = wolfsentry_config_json_feed(jps,
+ (const unsigned char *)wolfsentry_config_data,
+ strlen(wolfsentry_config_data), err_buf, sizeof err_buf);
+ if (ret < 0)
+ {
+ printf("error while loading wolfSentry config data, "
+ WOLFSENTRY_ERROR_FMT ", msg: %s\n",
+ WOLFSENTRY_ERROR_FMT_ARGS(ret), err_buf);
+ }
}
+ /* Clean up the JSON parser. This commits the parsed config. */
+ if (jps != NULL)
+ {
+ fini_ret = wolfsentry_config_json_fini(&jps, err_buf, sizeof err_buf);
+ if (fini_ret < 0)
+ {
+ printf("error while loading wolfSentry config data, "
+ WOLFSENTRY_ERROR_FMT ", msg: %s\n",
+ WOLFSENTRY_ERROR_FMT_ARGS(fini_ret), err_buf);
+ if (ret >= 0)
+ ret = fini_ret;
+ }
+ }
+
+ if (ret < 0)
+ {
+ (void)wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(&ctx, NULL));
+ return ret;
+ }
+
+ /* Publish the context only once the policy is committed, so a concurrent
+ * packet filter never dispatches against an empty route table. */
+ WOLFSENTRY_ATOMIC_STORE(wolfsentry, ctx);
+
return 0;
}
@@ -247,7 +282,7 @@ int sentry_init(void)
int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port, uint16_t remote_port, sentry_action_type action)
{
wolfsentry_errcode_t ret;
- wolfsentry_action_res_t action_results;
+ wolfsentry_action_res_t action_results = WOLFSENTRY_ACTION_RES_NONE;
/* Note that sa.addr is 0 bytes, addr_buf essentially enlarges this to the correct size */
struct {
struct wolfsentry_sockaddr sa;
@@ -255,6 +290,11 @@ int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port
} remote, local;
u32_t remoteip = remote_ip->addr;
u32_t localip = local_ip->addr;
+ struct wolfsentry_context *ctx = WOLFSENTRY_ATOMIC_LOAD(wolfsentry);
+
+ /* Fail closed until initialization has published a configured context */
+ if (ctx == NULL)
+ return -1;
/* Connect will increment the connection count in wolfSentry, disconnect
* will decrement it */
@@ -289,7 +329,7 @@ int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port
/* Send the details of this to wolfSentry and get the result */
ret = wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
&remote.sa,
&local.sa,
WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
@@ -304,11 +344,14 @@ int sentry_action(ip_addr_t *local_ip, ip_addr_t *remote_ip, uint16_t local_port
WOLFSENTRY_ERROR_FMT_ARGS(ret));
fflush(stdout);
+ /* Fail closed: a dispatch error denies the connection */
+ if (ret < 0) {
+ return -1;
+ }
+
/* Check the result, if it contains "reject" then notify the caller */
- if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) {
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
- return -1;
- }
+ if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
+ return -1;
}
return 0;
@@ -324,6 +367,11 @@ int sentry_action_ping(const ip_addr_t *addr, u8_t type)
struct wolfsentry_sockaddr sa;
byte addr_buf[4];
} remote;
+ struct wolfsentry_context *ctx = WOLFSENTRY_ATOMIC_LOAD(wolfsentry);
+
+ /* Fail closed until initialization has published a configured context */
+ if (ctx == NULL)
+ return -1;
/* ICMP protocol check. The ICMP packet type is the port number for this
* check */
@@ -335,7 +383,7 @@ int sentry_action_ping(const ip_addr_t *addr, u8_t type)
memcpy(remote.sa.addr, &addr->addr, 4);
ret = wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
&remote.sa,
&remote.sa, /* Reuse for now */
WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
@@ -348,10 +396,14 @@ int sentry_action_ping(const ip_addr_t *addr, u8_t type)
printf("PING Sentry action returned " WOLFSENTRY_ERROR_FMT "\n",
WOLFSENTRY_ERROR_FMT_ARGS(ret));
fflush(stdout);
- if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) {
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
- return -1;
- }
+
+ /* Fail closed: a dispatch error denies the packet */
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
+ return -1;
}
return 0;
@@ -367,6 +419,11 @@ int sentry_action_mac(struct eth_addr *addr)
struct wolfsentry_sockaddr sa;
byte addr_buf[6];
} remote;
+ struct wolfsentry_context *ctx = WOLFSENTRY_ATOMIC_LOAD(wolfsentry);
+
+ /* Fail closed until initialization has published a configured context */
+ if (ctx == NULL)
+ return -1;
/* We only really care about the data and length, the family is AF_LINK */
remote.sa.sa_family = WOLFSENTRY_AF_LINK;
@@ -376,7 +433,7 @@ int sentry_action_mac(struct eth_addr *addr)
memcpy(remote.sa.addr, &addr->addr, 6);
ret = wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL),
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(ctx, NULL),
&remote.sa,
&remote.sa, /* Reuse for now */
WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
@@ -390,10 +447,14 @@ int sentry_action_mac(struct eth_addr *addr)
printf("MAC Sentry action returned " WOLFSENTRY_ERROR_FMT "\n",
WOLFSENTRY_ERROR_FMT_ARGS(ret));
fflush(stdout);
- if (WOLFSENTRY_ERROR_DECODE_ERROR_CODE(ret) >= 0) {
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
- return -1;
- }
+
+ /* Fail closed: a dispatch error denies the frame */
+ if (ret < 0) {
+ return -1;
+ }
+
+ if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)) {
+ return -1;
}
return 0;
diff --git a/examples/notification-demo/log_server/log_server.c b/examples/notification-demo/log_server/log_server.c
index c90d2812..2c3a3dc2 100644
--- a/examples/notification-demo/log_server/log_server.c
+++ b/examples/notification-demo/log_server/log_server.c
@@ -315,6 +315,10 @@ wolfsentry_errcode_t circlog_enqueue_one(size_t msg_len, char **msg_buf) {
if (new_ent_size > circlog_size)
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ /* reset a fully drained queue so a wrap cannot strand a stale head. */
+ if (circlog_head == circlog_tail)
+ circlog_head = circlog_tail = 0;
+
if (circlog_tail + new_ent_size > circlog_size) {
/* if the new ent will wrap, old ents need to be cleared out to the end, and a skip ent inserted. */
if (circlog_tail + circlog_ent_size_from_msg_len(0) < circlog_size) {
@@ -342,7 +346,8 @@ wolfsentry_errcode_t circlog_enqueue_one(size_t msg_len, char **msg_buf) {
* the =, circlog_tail could be incremented below to equal circlog_head,
* which is defined to be an empty queue.
*/
- while ((circlog_head > new_msg_start) &&
+ while ((circlog_nents > 0) &&
+ (circlog_head >= new_msg_start) &&
(circlog_head <= new_msg_start + new_ent_size))
{
ret = circlog_dequeue_one(NULL);
@@ -350,6 +355,10 @@ wolfsentry_errcode_t circlog_enqueue_one(size_t msg_len, char **msg_buf) {
return ret;
}
+ /* if eviction drained the queue, the new entry alone defines the head. */
+ if (circlog_nents == 0)
+ circlog_head = new_msg_start;
+
new_ent = (struct circlog_message *)(void *)(circlog + new_msg_start);
ret = wolfsentry_time_now_plus_delta(global_wolfsentry, 0 /* td */, &new_ent->when);
new_ent->len = (uint16_t)msg_len;
@@ -417,15 +426,37 @@ struct log_server_data {
int error_code;
};
+#if defined(DEBUG_TLS) && defined(OPENSSL_EXTRA)
+/* certificate DN strings are peer-controlled; drop control bytes before logging. */
+static const char *sanitize_log_str(const char *in, char *out, size_t outsz) {
+ size_t i;
+ unsigned char c;
+
+ if (in == NULL)
+ return "(null)";
+ if (outsz == 0)
+ return out;
+ for (i = 0; (i + 1 < outsz) && (in[i] != '\0'); ++i) {
+ c = (unsigned char)in[i];
+ if ((c < 0x20) || (c == 0x7f))
+ out[i] = '?';
+ else
+ out[i] = (char)c;
+ }
+ out[i] = '\0';
+ return out;
+}
+#endif
static int myVerifyCheck(int preverify, WOLFSSL_X509_STORE_CTX* store) {
struct log_server_data *app_data = NULL;
- const char* issuerName = NULL;
+ char* issuerName = NULL;
unsigned long issuerHash = 0;
#if defined(DEBUG_TLS) && defined(OPENSSL_EXTRA)
const char *subjectCN;
- const char* subjectName;
+ char* subjectName = NULL;
unsigned long subjectHash = 0;
+ char subjectCN_buf[256], subjectName_buf[256], issuerName_buf[256];
#endif
struct ca_cert *ca_certs_i;
@@ -473,6 +504,10 @@ static int myVerifyCheck(int preverify, WOLFSSL_X509_STORE_CTX* store) {
issuerHash = wolfSSL_X509_issuer_name_hash(store->current_cert);
issuerName = wolfSSL_X509_NAME_oneline(
wolfSSL_X509_get_issuer_name(store->current_cert), 0, 0);
+ if (issuerName == NULL) {
+ fprintf(stderr, "ERROR: %s L%d null issuer name.\n", __FILE__,__LINE__);
+ return WOLFSSL_FAILURE;
+ }
#ifdef DEBUG_TLS
subjectCN = wolfSSL_X509_get_subjectCN(store->current_cert);
@@ -480,8 +515,11 @@ static int myVerifyCheck(int preverify, WOLFSSL_X509_STORE_CTX* store) {
subjectName = wolfSSL_X509_NAME_oneline(
wolfSSL_X509_get_subject_name(store->current_cert), 0, 0);
fprintf(stderr, "CN=%s subjectName->name=\"%s\" subject_hash=%lu\n",
- subjectCN, subjectName, subjectHash);
- fprintf(stderr, "issuerName->name=\"%s\" issuer_hash=%lu\n", issuerName, issuerHash);
+ sanitize_log_str(subjectCN, subjectCN_buf, sizeof subjectCN_buf),
+ sanitize_log_str(subjectName, subjectName_buf, sizeof subjectName_buf),
+ subjectHash);
+ fprintf(stderr, "issuerName->name=\"%s\" issuer_hash=%lu\n",
+ sanitize_log_str(issuerName, issuerName_buf, sizeof issuerName_buf), issuerHash);
#endif
#endif
@@ -497,11 +535,23 @@ static int myVerifyCheck(int preverify, WOLFSSL_X509_STORE_CTX* store) {
}
if (app_data->issuer_cert == NULL) {
fprintf(stderr, "ERROR: %s L%d app_data->issuer_cert NULL.\n",__FILE__,__LINE__);
+#ifdef OPENSSL_EXTRA
+ XFREE(issuerName, NULL, DYNAMIC_TYPE_OPENSSL);
+ #ifdef DEBUG_TLS
+ XFREE(subjectName, NULL, DYNAMIC_TYPE_OPENSSL);
+ #endif
+#endif
return WOLFSSL_FAILURE;
}
app_data->error_code = 0;
+#ifdef OPENSSL_EXTRA
+ XFREE(issuerName, NULL, DYNAMIC_TYPE_OPENSSL);
+ #ifdef DEBUG_TLS
+ XFREE(subjectName, NULL, DYNAMIC_TYPE_OPENSSL);
+ #endif
+#endif
return WOLFSSL_SUCCESS;
}
@@ -1120,6 +1170,7 @@ static int http_write_header(WOLFSSL *ssl, char *http_buf, size_t http_buf_size,
#ifndef NO_FILESYSTEM
/* reads file size, allocates buffer, reads into buffer, returns buffer */
#include
+#define LOAD_FILE_MAX_SZ (64 * 1024 * 1024)
static int load_file(const char* fname, unsigned char** buf, size_t* bufLen)
{
int ret;
@@ -1142,9 +1193,9 @@ static int load_file(const char* fname, unsigned char** buf, size_t* bufLen)
}
fseek(lFile, 0, SEEK_END);
- fileSz = (int)ftell(lFile);
+ fileSz = ftell(lFile);
rewind(lFile);
- if (fileSz > 0) {
+ if ((fileSz > 0) && (fileSz <= (long int)LOAD_FILE_MAX_SZ)) {
*bufLen = (size_t)fileSz;
*buf = (unsigned char*)malloc(*bufLen);
if (*buf == NULL) {
@@ -1417,6 +1468,12 @@ int main(int argc, char **argv) {
exit(1);
}
+ if (circlog_size_uint64 > (uint64_t)SIZE_MAX) {
+ fprintf(stderr, "ERROR: \"circlog-size\" value %llu exceeds SIZE_MAX on this ABI\n",
+ (unsigned long long)circlog_size_uint64);
+ exit(1);
+ }
+
ret = circlog_init((size_t)circlog_size_uint64);
if (ret < 0) {
fprintf(stderr, "ERROR: circlog_init() failed: " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
@@ -1477,6 +1534,47 @@ int main(int argc, char **argv) {
fprintf(stderr,"circlog_head=%zu circlog_tail=%zu\n",circlog_head,circlog_tail);
}
+
+ if (circlog != NULL) {
+ char *wrap_buf;
+ struct circlog_message *wrap_msg;
+ size_t wrap_filled, wrap_reachable;
+ static const char wrap_s[] = "wrap eviction regression probe entry.";
+ size_t wrap_ent = circlog_ent_size_from_msg_len(strlen(wrap_s));
+
+ if ((circlog_reset() >= 0) && ((wrap_ent * 2) <= circlog_size)) {
+ wrap_filled = 0;
+ while ((circlog_tail + wrap_ent) <= circlog_size) {
+ ret = circlog_enqueue_one(strlen(wrap_s), &wrap_buf);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: %s L%d circlog failed: " WOLFSENTRY_ERROR_FMT "\n", __FILE__, __LINE__, WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ exit(1);
+ }
+ memcpy(wrap_buf, wrap_s, strlen(wrap_s));
+ ++wrap_filled;
+ }
+
+ ret = circlog_enqueue_one(strlen(wrap_s), &wrap_buf);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: %s L%d circlog failed: " WOLFSENTRY_ERROR_FMT "\n", __FILE__, __LINE__, WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ exit(1);
+ }
+ memcpy(wrap_buf, wrap_s, strlen(wrap_s));
+
+ wrap_reachable = 0;
+ wrap_msg = NULL;
+ while (circlog_iterate(&wrap_msg) >= 0)
+ ++wrap_reachable;
+
+ if (circlog_nents != wrap_reachable) {
+ fprintf(stderr, "ERROR: %s L%d circlog nents=%zu disagrees with reachable=%zu after wrap (filled=%zu).\n",
+ __FILE__, __LINE__, circlog_nents, wrap_reachable, wrap_filled);
+ exit(1);
+ }
+
+ fprintf(stderr, "circlog wrap-eviction check OK: nents=%zu reachable=%zu\n", circlog_nents, wrap_reachable);
+ }
+ }
#endif /* CIRCLOG_UNIT_TEST */
log_server_init();
@@ -1871,7 +1969,8 @@ int main(int argc, char **argv) {
}
#endif
- if (! (wolfsentry_data->action_results & WOLFSENTRY_ACTION_RES_FALLTHROUGH)) {
+ if ((! (wolfsentry_data->action_results & WOLFSENTRY_ACTION_RES_FALLTHROUGH)) &&
+ (wolfsentry_data->rule_route != NULL)) {
wolfsentry_action_res_t action_results;
if (transaction_successful) {
ret = wolfsentry_route_event_dispatch_by_route(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(global_wolfsentry, global_thread),
diff --git a/examples/notification-demo/log_server/sentry.c b/examples/notification-demo/log_server/sentry.c
index e415539c..ff3a23b2 100644
--- a/examples/notification-demo/log_server/sentry.c
+++ b/examples/notification-demo/log_server/sentry.c
@@ -320,6 +320,39 @@ static int wolfSentry_NetworkFilterCallback(
}
+/* labels come from loaded config; escape them so they cannot break the JSON record. */
+static const char *json_escape_label(const char *in, char *out, size_t outsz) {
+ size_t i, o;
+ unsigned char c;
+
+ if (in == NULL)
+ return "";
+ if (outsz == 0)
+ return "";
+ o = 0;
+ for (i = 0; in[i] != '\0'; ++i) {
+ c = (unsigned char)in[i];
+ if ((c == '"') || (c == '\\')) {
+ if ((o + 2) >= outsz)
+ break;
+ out[o++] = '\\';
+ out[o++] = (char)c;
+ }
+ else if ((c < 0x20) || (c == 0x7f)) {
+ if ((o + 1) >= outsz)
+ break;
+ out[o++] = '?';
+ }
+ else {
+ if ((o + 1) >= outsz)
+ break;
+ out[o++] = (char)c;
+ }
+ }
+ out[o] = '\0';
+ return out;
+}
+
static wolfsentry_errcode_t wolfsentry_notify_via_UDP_JSON(
WOLFSENTRY_CONTEXT_ARGS_IN,
const struct wolfsentry_action *action,
@@ -353,6 +386,10 @@ static wolfsentry_errcode_t wolfsentry_notify_via_UDP_JSON(
char msgbuf[1024], *msgbuf_ptr = msgbuf;
int msgbuf_space_left = (int)sizeof msgbuf;
int msgbuf_len;
+ char action_label_buf[(WOLFSENTRY_MAX_LABEL_BYTES * 2) + 1];
+ char trigger_label_buf[(WOLFSENTRY_MAX_LABEL_BYTES * 2) + 1];
+ char parent_label_buf[(WOLFSENTRY_MAX_LABEL_BYTES * 2) + 1];
+ char family_name_buf[(WOLFSENTRY_MAX_LABEL_BYTES * 2) + 1];
(void)handler_arg;
(void)route_table;
@@ -451,12 +488,12 @@ static wolfsentry_errcode_t wolfsentry_notify_via_UDP_JSON(
(size_t)msgbuf_space_left,
"{ \"timestamp\" : \"%s\", \"action\" : \"%s\", \"trigger\" : %s%s%s, \"parent\" : %s%s%s, \"rule-id\" : " WOLFSENTRY_ENT_ID_FMT ", \"rule-hitcount\" : " WOLFSENTRY_HITCOUNT_FMT ", \"af\" : \"%s\", \"proto\" : %d, \"remote\" : { \"address\" : \"",
timebuf,
- wolfsentry_action_get_label(action),
- trigger_event ? "\"" : "", trigger_event ? wolfsentry_event_get_label(trigger_event) : "null", trigger_event ? "\"" : "",
- trigger_route_exports.parent_event_label ? "\"" : "", trigger_route_exports.parent_event_label ? trigger_route_exports.parent_event_label : "null", trigger_route_exports.parent_event_label ? "\"" : "",
+ json_escape_label(wolfsentry_action_get_label(action), action_label_buf, sizeof action_label_buf),
+ trigger_event ? "\"" : "", trigger_event ? json_escape_label(wolfsentry_event_get_label(trigger_event), trigger_label_buf, sizeof trigger_label_buf) : "null", trigger_event ? "\"" : "",
+ trigger_route_exports.parent_event_label ? "\"" : "", trigger_route_exports.parent_event_label ? json_escape_label(trigger_route_exports.parent_event_label, parent_label_buf, sizeof parent_label_buf) : "null", trigger_route_exports.parent_event_label ? "\"" : "",
wolfsentry_get_object_id(rule_route),
rule_route_exports.meta.hit_count,
- family_name,
+ json_escape_label(family_name, family_name_buf, sizeof family_name_buf),
trigger_route_exports.sa_proto);
msgbuf_space_left -= msgbuf_len;
@@ -551,10 +588,10 @@ static wolfsentry_errcode_t wolfsentry_notify_via_UDP_JSON(
if ((res_string = wolfsentry_action_res_assoc_by_flag(*action_results, res_bit)) != NULL) {
++n_res_bits;
if (n_res_bits > 1) {
+ if (msgbuf_space_left <= 0)
+ WOLFSENTRY_ERROR_RETURN(BUFFER_TOO_SMALL);
*msgbuf_ptr++ = ',';
--msgbuf_space_left;
- if (msgbuf_space_left < 0)
- WOLFSENTRY_ERROR_RETURN(BUFFER_TOO_SMALL);
}
msgbuf_len = snprintf(msgbuf_ptr, (size_t)msgbuf_space_left, "\"%s\"", res_string);
msgbuf_space_left -= msgbuf_len;
diff --git a/examples/notification-demo/udp_to_dbus/udp_to_dbus.c b/examples/notification-demo/udp_to_dbus/udp_to_dbus.c
index ec7f9c9c..f48c5d31 100644
--- a/examples/notification-demo/udp_to_dbus/udp_to_dbus.c
+++ b/examples/notification-demo/udp_to_dbus/udp_to_dbus.c
@@ -88,6 +88,7 @@ static int notify(WOLFSENTRY_CONTEXT_ARGS_IN, JSON_CONFIG *centijson_config, con
GError *error = NULL;
gboolean retval;
int ret;
+ gchar *body_esc = NULL;
struct wolfsentry_allocator *allocator = wolfsentry_get_allocator(wolfsentry);
@@ -144,6 +145,13 @@ static int notify(WOLFSENTRY_CONTEXT_ARGS_IN, JSON_CONFIG *centijson_config, con
local_port = json_value_int32(local_port_v);
n_decisions = json_value_array_size(decision_array_v);
+ if ((action == NULL) || (af == NULL) || (remote_addr == NULL) ||
+ (local_addr == NULL)) {
+ fprintf(stderr, "ERROR: ignoring malformed notification datagram "
+ "with missing string field(s)\n");
+ break;
+ }
+
SNPRINTF_MSGPTR(msgbuf_ptr, msgbuf_len_left, "%s/%d from %s:%d to %s:%d\n",
af,
proto,
@@ -175,12 +183,14 @@ static int notify(WOLFSENTRY_CONTEXT_ARGS_IN, JSON_CONFIG *centijson_config, con
SNPRINTF_MSGPTR(msgbuf_ptr, msgbuf_len_left, "]");
- /* xxx note that dbus notification daemons expect fragments of basic
- * HTML as input, so msgbuf needs rewriting to escape HTML entities.
- */
+ /* escape the completed body once, so no entity can be split by the
+ * fixed-size buffer; the summary is plain text, not markup. */
+ body_esc = g_markup_escape_text((const gchar *)msgbuf, -1);
+ if (body_esc == NULL)
+ break;
summary = action;
- body = msgbuf;
+ body = (const unsigned char *)body_esc;
type = "type";
app_name = "wolfsentry_udp_to_dbus";
icon_str = "icon_str";
@@ -206,6 +216,8 @@ static int notify(WOLFSENTRY_CONTEXT_ARGS_IN, JSON_CONFIG *centijson_config, con
WARN_ON_JSON_FAILURE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &p_root));
+ g_free(body_esc);
+
if (notify == NULL)
return -1;
diff --git a/src/action_builtins.c b/src/action_builtins.c
index 19cdf255..b4df29c4 100644
--- a/src/action_builtins.c
+++ b/src/action_builtins.c
@@ -24,6 +24,41 @@
#define WOLFSENTRY_SOURCE_ID WOLFSENTRY_SOURCE_ID_ACTION_BUILTINS_C
+/* the public insert wrappers zero action_results before the route table sees
+ * them, so the CONNECT that spawns a tracking route is counted here instead,
+ * on exactly the route that was just inserted. caller must hold the lock.
+ */
+static wolfsentry_errcode_t wolfsentry_builtin_track_peer_count_connect(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ wolfsentry_ent_id_t route_id,
+ const struct wolfsentry_eventconfig_internal *config)
+{
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_route *route = NULL;
+ uint16_t new_count;
+
+ ret = wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, route_id, (struct wolfsentry_table_ent_header **)&route);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ if (route->header.parent_table == NULL)
+ WOLFSENTRY_ERROR_RETURN(INTERNAL_CHECK_FATAL);
+ if (route->header.parent_table->ent_type != WOLFSENTRY_OBJECT_TYPE_ROUTE)
+ WOLFSENTRY_ERROR_RETURN(WRONG_OBJECT);
+
+ if (WOLFSENTRY_CHECK_BITS(WOLFSENTRY_ATOMIC_LOAD(route->flags), WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS))
+ WOLFSENTRY_RETURN_OK;
+
+ /* dispatch counts every connection and enforces the limit only when it is nonzero, so match it. */
+ if ((config->config.max_connection_count > 0) &&
+ (WOLFSENTRY_ATOMIC_LOAD(route->meta.connection_count) >= config->config.max_connection_count))
+ WOLFSENTRY_ERROR_RETURN(INCOMPATIBLE_STATE);
+
+ WOLFSENTRY_ATOMIC_INCREMENT_UNSIGNED_SAFELY_BY_ONE(route->meta.connection_count, new_count);
+ if (new_count == 0)
+ WOLFSENTRY_ERROR_RETURN(OVERFLOW_AVERTED);
+
+ WOLFSENTRY_RETURN_OK;
+}
+
/* this simple action creates a new tracking rule, either as a fallthrough or as
* a side effect. it can be used to open a pinhole route for bidirectional
* traffic, or to track derogatory events on a locally bound port (e.g. crypto
@@ -44,10 +79,13 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer(
{
wolfsentry_route_flags_t rule_flags;
const struct wolfsentry_event *rule_parent_event = NULL, *rule_aux_event = NULL;
+ const struct wolfsentry_event *ephemeral_parent_event = NULL;
+ const struct wolfsentry_eventconfig_internal *ephemeral_config;
wolfsentry_addr_bits_t rule_local_addr_len, rule_remote_addr_len;
struct wolfsentry_route_exports target_exports;
wolfsentry_errcode_t ret;
- wolfsentry_action_res_t insert_action_results;
+ wolfsentry_action_res_t insert_action_results = WOLFSENTRY_ACTION_RES_NONE;
+ wolfsentry_ent_id_t new_route_id = WOLFSENTRY_ENT_ID_NONE;
(void)action;
(void)handler_arg;
@@ -99,17 +137,31 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer(
rule_parent_event = wolfsentry_route_parent_event(rule_route);
if (rule_parent_event)
rule_aux_event = wolfsentry_event_get_aux_event(rule_parent_event);
- if (rule_aux_event) {
- target_exports.parent_event_label = wolfsentry_event_get_label(rule_aux_event);
- target_exports.parent_event_label_len = WOLFSENTRY_LENGTH_NULL_TERMINATED;
- } else if (rule_parent_event) {
- target_exports.parent_event_label = wolfsentry_event_get_label(rule_parent_event);
+ if (rule_aux_event)
+ ephemeral_parent_event = rule_aux_event;
+ else
+ ephemeral_parent_event = rule_parent_event;
+
+ if (ephemeral_parent_event != NULL) {
+ target_exports.parent_event_label = wolfsentry_event_get_label(ephemeral_parent_event);
target_exports.parent_event_label_len = WOLFSENTRY_LENGTH_NULL_TERMINATED;
} else {
target_exports.parent_event_label = NULL;
target_exports.parent_event_label_len = 0;
}
+ if ((ephemeral_parent_event != NULL) && (ephemeral_parent_event->config != NULL))
+ ephemeral_config = ephemeral_parent_event->config;
+ else
+ ephemeral_config = &wolfsentry->config;
+
+ /* an ephemeral route with no purge time escapes both the idle purge and
+ * max_purgeable_routes, letting a remote peer grow the table without bound.
+ * skip the insert but return OK so the rest of the match chain still runs.
+ */
+ if (ephemeral_config->config.route_idle_time_for_purge <= 0)
+ WOLFSENTRY_RETURN_OK;
+
target_exports.private_data = NULL;
target_exports.private_data_size = 0;
@@ -117,14 +169,23 @@ static wolfsentry_errcode_t wolfsentry_builtin_action_track_peer(
WOLFSENTRY_RERETURN_IF_ERROR(ret);
/* can't pass action_results directly to _route_insert -- it would get zeroed. */
- WOLFSENTRY_WARN_ON_FAILURE(
- wolfsentry_route_insert_by_exports_into_table(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- route_table,
- NULL /* void *caller_arg*/,
- &target_exports,
- NULL /* id */,
- &insert_action_results));
+ ret = wolfsentry_route_insert_by_exports_into_table(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route_table,
+ NULL /* void *caller_arg*/,
+ &target_exports,
+ &new_route_id,
+ &insert_action_results);
+ WOLFSENTRY_WARN_ON_FAILURE(ret);
+ /* no route survives a failed insert, so drop INSERTED (it would suppress generator
+ * accounting); other bits are the chain's decisions and are NONE on early failure. */
+ if (ret < 0) {
+ WOLFSENTRY_CLEAR_BITS(insert_action_results, WOLFSENTRY_ACTION_RES_INSERTED);
+ } else if (WOLFSENTRY_CHECK_BITS(insert_action_results, WOLFSENTRY_ACTION_RES_INSERTED) &&
+ WOLFSENTRY_CHECK_BITS(*action_results, WOLFSENTRY_ACTION_RES_CONNECT))
+ {
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_builtin_track_peer_count_connect(WOLFSENTRY_CONTEXT_ARGS_OUT, new_route_id, ephemeral_config));
+ }
*action_results |= insert_action_results; /* copying over the bits is crucial to inhibit double-accounting for
* connection_count, derogatory_count, and commendable_count.
*/
diff --git a/src/actions.c b/src/actions.c
index 755c80e0..50969ec1 100644
--- a/src/actions.c
+++ b/src/actions.c
@@ -143,12 +143,12 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_insert_1(
goto out;
if ((ret = wolfsentry_id_allocate(WOLFSENTRY_CONTEXT_ARGS_OUT, &new->header)) < 0)
goto out;
- if (id)
- *id = new->header.id;
if ((ret = wolfsentry_table_ent_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, &new->header, &wolfsentry->actions->header, 1 /* unique_p */)) < 0) {
ret = WOLFSENTRY_ERROR_RECODE(ret);
goto out;
}
+ if (id != NULL)
+ *id = new->header.id;
ret = WOLFSENTRY_ERROR_ENCODE(OK);
out:
@@ -256,6 +256,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_action_drop_reference(WOLFSENTRY_
if ((action->header.parent_table != NULL) &&
(action->header.parent_table->ent_type != WOLFSENTRY_OBJECT_TYPE_ACTION))
WOLFSENTRY_ERROR_RETURN(WRONG_OBJECT);
+ if (action_results)
+ WOLFSENTRY_CLEAR_ALL_BITS(*action_results);
WOLFSENTRY_REFCOUNT_DECREMENT(action->header.refcount, refs_left, ret);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
if (refs_left > 0)
@@ -504,6 +506,9 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_list_delete(
WOLFSENTRY_MUTEX_OR_RETURN();
+ if (wolfsentry->action_dispatch_depth != 0)
+ WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(BUSY);
+
for (wolfsentry_list_ent_get_first(&action_list->header, &i);
i;
wolfsentry_list_ent_get_next(&action_list->header, &i))
@@ -534,6 +539,11 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_list_delete_all(
WOLFSENTRY_MUTEX_OR_RETURN();
+ /* no dispatch-in-progress guard here: this runs only from event teardown
+ * (at refcount zero) and clone error cleanup, never on a list being
+ * iterated, so it must not fail while an unrelated dispatch is active.
+ */
+
for (wolfsentry_list_ent_get_first(&action_list->header, &i);
i;
i = next)
@@ -581,16 +591,6 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_list_dispatch(
if (WOLFSENTRY_CHECK_BITS(rule_route->flags, WOLFSENTRY_ROUTE_FLAG_INSERT_ACTIONS_CALLED))
WOLFSENTRY_ERROR_RETURN(ALREADY);
- else {
- wolfsentry_route_flags_t flags_before;
- wolfsentry_route_flags_t flags_after;
- WOLFSENTRY_ATOMIC_UPDATE_FLAGS(
- rule_route->flags,
- (wolfsentry_route_flags_t)WOLFSENTRY_ROUTE_FLAG_INSERT_ACTIONS_CALLED,
- (wolfsentry_route_flags_t)WOLFSENTRY_ROUTE_FLAG_NONE,
- &flags_before,
- &flags_after);
- }
break;
case WOLFSENTRY_ACTION_TYPE_MATCH:
@@ -604,16 +604,6 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_list_dispatch(
if (WOLFSENTRY_CHECK_BITS(rule_route->flags, WOLFSENTRY_ROUTE_FLAG_DELETE_ACTIONS_CALLED))
WOLFSENTRY_ERROR_RETURN(ALREADY);
- else {
- wolfsentry_route_flags_t flags_before;
- wolfsentry_route_flags_t flags_after;
- WOLFSENTRY_ATOMIC_UPDATE_FLAGS(
- rule_route->flags,
- (wolfsentry_route_flags_t)WOLFSENTRY_ROUTE_FLAG_DELETE_ACTIONS_CALLED,
- (wolfsentry_route_flags_t)WOLFSENTRY_ROUTE_FLAG_NONE,
- &flags_before,
- &flags_after);
- }
break;
case WOLFSENTRY_ACTION_TYPE_DECISION:
@@ -630,6 +620,38 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_list_dispatch(
(action_event->config && WOLFSENTRY_CHECK_BITS(action_event->config->config.flags, WOLFSENTRY_EVENTCONFIG_FLAG_INHIBIT_ACTIONS)))
WOLFSENTRY_RETURN_OK;
+ /* hold a reference on the event before marking lifecycle state, so a failed
+ * acquisition cannot leave the route flagged with its handlers never run.
+ */
+ WOLFSENTRY_REFCOUNT_INCREMENT(action_event->header.refcount, ret);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+
+ if (action_type == WOLFSENTRY_ACTION_TYPE_INSERT) {
+ wolfsentry_route_flags_t flags_before;
+ wolfsentry_route_flags_t flags_after;
+ WOLFSENTRY_ATOMIC_UPDATE_FLAGS(
+ rule_route->flags,
+ (wolfsentry_route_flags_t)WOLFSENTRY_ROUTE_FLAG_INSERT_ACTIONS_CALLED,
+ (wolfsentry_route_flags_t)WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before,
+ &flags_after);
+ }
+ else if (action_type == WOLFSENTRY_ACTION_TYPE_DELETE) {
+ wolfsentry_route_flags_t flags_before;
+ wolfsentry_route_flags_t flags_after;
+ WOLFSENTRY_ATOMIC_UPDATE_FLAGS(
+ rule_route->flags,
+ (wolfsentry_route_flags_t)WOLFSENTRY_ROUTE_FLAG_DELETE_ACTIONS_CALLED,
+ (wolfsentry_route_flags_t)WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before,
+ &flags_after);
+ }
+
+ /* mark a dispatch in progress, so a reentrant handler cannot free a live
+ * list entry (the single-entry mutator returns BUSY).
+ */
+ WOLFSENTRY_ATOMIC_INCREMENT(wolfsentry->action_dispatch_depth, 1);
+
for (i = (struct wolfsentry_action_list_ent *)w_a_l->header.head;
i;
i = (struct wolfsentry_action_list_ent *)i->header.next)
@@ -641,11 +663,21 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_action_list_dispatch(
#ifdef WOLFSENTRY_DEBUG_ACTIONS
fprintf(stderr,"calling action %s for event %s and action type %u\n", wolfsentry_action_get_label(i->action), wolfsentry_event_get_label(trigger_event), (unsigned)action_type);
#endif
- if ((ret = i->action->handler(WOLFSENTRY_CONTEXT_ARGS_OUT, i->action, i->action->handler_arg, caller_arg, trigger_event, action_type, target_route, route_table, rule_route, action_results)) < 0)
+ if ((ret = i->action->handler(WOLFSENTRY_CONTEXT_ARGS_OUT, i->action, i->action->handler_arg, caller_arg, trigger_event, action_type, target_route, route_table, rule_route, action_results)) < 0) {
+ WOLFSENTRY_ATOMIC_DECREMENT(wolfsentry->action_dispatch_depth, 1);
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, action_event, NULL /* action_results */));
WOLFSENTRY_ERROR_RERETURN(ret);
- if (WOLFSENTRY_CHECK_BITS(*action_results, WOLFSENTRY_ACTION_RES_STOP))
+ }
+ if (WOLFSENTRY_CHECK_BITS(*action_results, WOLFSENTRY_ACTION_RES_STOP)) {
+ WOLFSENTRY_ATOMIC_DECREMENT(wolfsentry->action_dispatch_depth, 1);
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, action_event, NULL /* action_results */));
WOLFSENTRY_RETURN_OK;
+ }
}
+
+ WOLFSENTRY_ATOMIC_DECREMENT(wolfsentry->action_dispatch_depth, 1);
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, action_event, NULL /* action_results */));
+
WOLFSENTRY_RETURN_OK;
}
diff --git a/src/addr_families.c b/src/addr_families.c
index ae58f5cb..3c48e702 100644
--- a/src/addr_families.c
+++ b/src/addr_families.c
@@ -105,26 +105,23 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_addr_family_insert(
family_byname_len = (int)strlen(family_byname);
if (family_byname_len == 0)
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
- if (family_byname_len > WOLFSENTRY_MAX_LABEL_BYTES) {
- ret = WOLFSENTRY_ERROR_ENCODE(STRING_ARG_TOO_LONG);
- goto out;
- }
+ if (family_byname_len > WOLFSENTRY_MAX_LABEL_BYTES)
+ WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
#endif
- if ((bynumber = (struct wolfsentry_addr_family_bynumber *)WOLFSENTRY_MALLOC(sizeof *bynumber)) == NULL) {
- ret = WOLFSENTRY_ERROR_ENCODE(SYS_RESOURCE_FAILED);
- goto out;
- }
+ if ((bynumber = (struct wolfsentry_addr_family_bynumber *)WOLFSENTRY_MALLOC(sizeof *bynumber)) == NULL)
+ WOLFSENTRY_ERROR_RETURN(SYS_RESOURCE_FAILED);
+
+ memset(bynumber, 0, sizeof *bynumber);
#ifdef WOLFSENTRY_PROTOCOL_NAMES
byname_size = sizeof *byname + (size_t)family_byname_len + 1;
if ((byname = (struct wolfsentry_addr_family_byname *)WOLFSENTRY_MALLOC(byname_size)) == NULL) {
- ret = WOLFSENTRY_ERROR_ENCODE(SYS_RESOURCE_FAILED);
- goto out;
+ WOLFSENTRY_FREE(bynumber);
+ WOLFSENTRY_ERROR_RETURN(SYS_RESOURCE_FAILED);
}
#endif
- memset(bynumber, 0, sizeof *bynumber);
bynumber->number = family_bynumber;
bynumber->parser = parser;
bynumber->formatter = formatter;
@@ -142,8 +139,13 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_addr_family_insert(
#endif
ret = WOLFSENTRY_MUTEX_EX(wolfsentry);
- if (ret < 0)
- goto out;
+ if (ret < 0) {
+ WOLFSENTRY_FREE(bynumber);
+#ifdef WOLFSENTRY_PROTOCOL_NAMES
+ WOLFSENTRY_FREE(byname);
+#endif
+ WOLFSENTRY_ERROR_RERETURN(ret);
+ }
if ((ret = wolfsentry_id_allocate(WOLFSENTRY_CONTEXT_ARGS_OUT, &bynumber->header)) < 0)
goto out;
@@ -165,6 +167,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_addr_family_insert(
out:
+ /* reached only after the context lock is held, so unlocking is correct. */
if (ret < 0) {
if (bynumber != NULL) {
if (bynumber->header.id != WOLFSENTRY_ENT_ID_NONE)
@@ -497,6 +500,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_addr_family_pton(
*/
if (WOLFSENTRY_ATOMIC_LOAD(wolfsentry->addr_families_byname->header.n_ents) > 0) {
struct wolfsentry_addr_family_bynumber *addr_family;
+ wolfsentry_addr_family_t found_number = WOLFSENTRY_AF_UNSPEC;
WOLFSENTRY_SHARED_OR_RETURN();
ret = wolfsentry_addr_family_get_byname_1(
WOLFSENTRY_CONTEXT_ARGS_OUT,
@@ -504,14 +508,18 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_addr_family_pton(
family_name,
family_name_len,
&addr_family);
+ if (ret >= 0)
+ found_number = addr_family->number;
WOLFSENTRY_UNLOCK_FOR_RETURN();
if (ret >= 0) {
- *family_number = addr_family->number;
+ *family_number = found_number;
WOLFSENTRY_RETURN_OK;
} else if (! WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND))
WOLFSENTRY_ERROR_RERETURN(ret);
}
+ if (strcaseeq(family_name, "UNSPEC", (size_t)family_name_len))
+ { *family_number = WOLFSENTRY_AF_UNSPEC; WOLFSENTRY_RETURN_OK; }
if (strcaseeq(family_name, "UNIX", (size_t)family_name_len))
{ *family_number = WOLFSENTRY_AF_UNIX; WOLFSENTRY_RETURN_OK; }
if (strcaseeq(family_name, "LOCAL", (size_t)family_name_len))
diff --git a/src/events.c b/src/events.c
index 6d92a88d..1c4c2767 100644
--- a/src/events.c
+++ b/src/events.c
@@ -77,6 +77,37 @@ static wolfsentry_errcode_t wolfsentry_event_init_1(const char *label, int label
WOLFSENTRY_RETURN_OK;
}
+/* callers hold the context lock, which is what the plain flag write relies on */
+static wolfsentry_errcode_t wolfsentry_event_aux_owner_add(struct wolfsentry_event *aux_event) {
+ wolfsentry_refcount_t owners;
+
+ WOLFSENTRY_ATOMIC_INCREMENT_UNSIGNED_SAFELY_BY_ONE(aux_event->aux_owner_count, owners);
+ if (owners == 0)
+ WOLFSENTRY_ERROR_RETURN(OVERFLOW_AVERTED);
+ if (! WOLFSENTRY_CHECK_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT))
+ WOLFSENTRY_SET_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT);
+ WOLFSENTRY_RETURN_OK;
+}
+
+/* may run without the context lock when a deleted owner is finally freed, so only the atomic count changes here; the mark is resynced under the lock */
+static wolfsentry_errcode_t wolfsentry_event_aux_owner_remove(struct wolfsentry_event *aux_event) {
+ wolfsentry_refcount_t owners;
+
+ WOLFSENTRY_ATOMIC_DECREMENT_UNSIGNED_SAFELY_BY_ONE(aux_event->aux_owner_count, owners);
+ if (owners == MAX_UINT_OF(aux_event->aux_owner_count))
+ WOLFSENTRY_ERROR_RETURN(INTERNAL_CHECK_FATAL);
+ WOLFSENTRY_RETURN_OK;
+}
+
+static void wolfsentry_event_aux_owner_resync_mark(struct wolfsentry_event *event) {
+ if ((WOLFSENTRY_ATOMIC_LOAD(event->aux_owner_count) == 0) &&
+ WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT))
+ {
+ WOLFSENTRY_CLEAR_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT);
+ }
+ WOLFSENTRY_RETURN_VOID;
+}
+
static void wolfsentry_event_free(WOLFSENTRY_CONTEXT_ARGS_IN, struct wolfsentry_event *event) {
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_list_delete_all(WOLFSENTRY_CONTEXT_ARGS_OUT, &event->post_action_list));
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_list_delete_all(WOLFSENTRY_CONTEXT_ARGS_OUT, &event->insert_action_list));
@@ -149,6 +180,10 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_event_clone_bare(
memcpy(*new_event, src_event, new_size);
WOLFSENTRY_TABLE_ENT_HEADER_RESET(**new_ent);
+ /* no route in a route-free clone can depend on the copied layout */
+ if (WOLFSENTRY_CHECK_BITS(flags, WOLFSENTRY_CLONE_FLAG_NO_ROUTES))
+ WOLFSENTRY_CLEAR_BITS((*new_event)->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT);
+
WOLFSENTRY_LIST_HEADER_RESET((*new_event)->post_action_list.header);
WOLFSENTRY_LIST_HEADER_RESET((*new_event)->insert_action_list.header);
WOLFSENTRY_LIST_HEADER_RESET((*new_event)->match_action_list.header);
@@ -157,6 +192,8 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_event_clone_bare(
WOLFSENTRY_LIST_HEADER_RESET((*new_event)->decision_action_list.header);
(*new_event)->aux_event = NULL;
+ (*new_event)->aux_owner_count = 0;
+ WOLFSENTRY_CLEAR_BITS((*new_event)->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT);
if (src_event->config) {
if (((*new_event)->config = WOLFSENTRY_MALLOC_1(dest_context->hpi.allocator, sizeof *(*new_event)->config)) == NULL) {
@@ -257,6 +294,8 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_event_clone_resolve(
}
WOLFSENTRY_REFCOUNT_INCREMENT(new_event->aux_event->header.refcount, ret);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ ret = wolfsentry_event_aux_owner_add(new_event->aux_event);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
}
WOLFSENTRY_RETURN_OK;
@@ -289,12 +328,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_insert(
wolfsentry_event_free(WOLFSENTRY_CONTEXT_ARGS_OUT, new);
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
- if (id)
- *id = new->header.id;
if ((ret = wolfsentry_table_ent_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, &new->header, &wolfsentry->events->header, 1 /* unique_p */)) < 0) {
wolfsentry_event_free(WOLFSENTRY_CONTEXT_ARGS_OUT, new);
WOLFSENTRY_ERROR_UNLOCK_AND_RETURN_RECODED(ret);
}
+ if (id)
+ *id = new->header.id;
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
@@ -306,7 +345,17 @@ WOLFSENTRY_API const char *wolfsentry_event_get_label(const struct wolfsentry_ev
WOLFSENTRY_API wolfsentry_event_flags_t wolfsentry_event_get_flags(const struct wolfsentry_event *event)
{
- return event ? event->flags : WOLFSENTRY_EVENT_FLAG_NONE;
+ wolfsentry_event_flags_t flags;
+
+ if (event == NULL)
+ return WOLFSENTRY_EVENT_FLAG_NONE;
+ /* an owner freed without the context lock only drops the count, so derive the mark from it */
+ flags = event->flags;
+ if (WOLFSENTRY_ATOMIC_LOAD(event->aux_owner_count) > 0)
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT);
+ else
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT);
+ return flags;
}
WOLFSENTRY_API const struct wolfsentry_event *wolfsentry_event_get_aux_event(const struct wolfsentry_event *event)
@@ -319,7 +368,7 @@ static wolfsentry_errcode_t wolfsentry_event_get_1(WOLFSENTRY_CONTEXT_ARGS_IN, c
WOLFSENTRY_STACKBUF(
struct wolfsentry_event,
label,
- WOLFSENTRY_MAX_LABEL_BYTES,
+ WOLFSENTRY_MAX_LABEL_BYTES + 1,
target);
struct wolfsentry_event *event_1 = &target.target;
@@ -356,27 +405,56 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_get_config(WOLFSENTRY_CONTE
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
+static int wolfsentry_event_has_routes(const struct wolfsentry_context *wolfsentry, const struct wolfsentry_event *event) {
+ const struct wolfsentry_table_ent_header *ent;
+
+ if (WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT))
+ return 1;
+ /* an aux event parents the ephemeral routes its owners generate */
+ if (WOLFSENTRY_ATOMIC_LOAD(event->aux_owner_count) != 0)
+ return 1;
+ /* beyond the table's own reference, each live route, deleted or not, holds one */
+ if (WOLFSENTRY_ATOMIC_LOAD(event->header.refcount) > 1)
+ return 1;
+ for (ent = wolfsentry_table_first(&wolfsentry->routes->header); ent != NULL; ent = ent->next) {
+ if (((const struct wolfsentry_route *)ent)->parent_event == event)
+ return 1;
+ }
+ return 0;
+}
+
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_update_config(WOLFSENTRY_CONTEXT_ARGS_IN, const char *label, int label_len, const struct wolfsentry_eventconfig *config) {
struct wolfsentry_event *event;
+ const struct wolfsentry_eventconfig_internal *current_config;
+ struct wolfsentry_eventconfig_internal new_config;
wolfsentry_errcode_t ret;
+ if (config == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+
WOLFSENTRY_MUTEX_OR_RETURN();
ret = wolfsentry_event_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, label, label_len, &event);
WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
+ ret = wolfsentry_eventconfig_load(config, &new_config);
+ WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
+
+ current_config = (event->config != NULL) ? event->config : &wolfsentry->config;
+
+ /* existing routes were allocated, and are accessed and freed, using the event's current layout */
+ if (wolfsentry_event_has_routes(wolfsentry, event) &&
+ ((new_config.config.route_private_data_size != current_config->config.route_private_data_size) ||
+ (new_config.config.route_private_data_alignment != current_config->config.route_private_data_alignment)))
+ WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(INCOMPATIBLE_STATE);
+
if (event->config == NULL) {
if ((event->config = (struct wolfsentry_eventconfig_internal *)WOLFSENTRY_MALLOC(sizeof *event->config)) == NULL)
WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(SYS_RESOURCE_FAILED);
- if ((ret = wolfsentry_eventconfig_load(config, event->config)) < 0) {
- WOLFSENTRY_FREE(event->config);
- event->config = NULL;
- }
- WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
- } else {
- ret = wolfsentry_eventconfig_load(config, event->config);
- WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
+ memcpy(event->config, &new_config, sizeof *event->config);
+
+ WOLFSENTRY_UNLOCK_AND_RETURN_OK;
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_IN, const char *label, int label_len, struct wolfsentry_event **event) {
@@ -418,6 +496,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_drop_reference(WOLFSENTRY_C
if (refs_left > 0)
WOLFSENTRY_RETURN_OK;
if (event->aux_event) {
+ ret = wolfsentry_event_aux_owner_remove(event->aux_event);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
ret = wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event->aux_event, NULL);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
event->aux_event = NULL;
@@ -613,45 +693,52 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_set_aux_event(
int aux_event_label_len)
{
wolfsentry_errcode_t ret;
- struct wolfsentry_event *event, *aux_event = NULL;
+ struct wolfsentry_event *event, *aux_event = NULL, *old_aux_event;
WOLFSENTRY_MUTEX_OR_RETURN();
ret = wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event_label, event_label_len, &event);
WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
- if (WOLFSENTRY_CHECK_BITS(event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT)) {
+ /* the owner count is authoritative; a mark left by an owner freed without the lock is cleared here */
+ if (WOLFSENTRY_ATOMIC_LOAD(event->aux_owner_count) != 0) {
ret = WOLFSENTRY_ERROR_ENCODE(INCOMPATIBLE_STATE);
goto out;
}
+ wolfsentry_event_aux_owner_resync_mark(event);
if (aux_event_label) {
ret = wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, aux_event_label, aux_event_label_len, &aux_event);
if (ret < 0) {
aux_event = NULL;
goto out;
}
- if (WOLFSENTRY_CHECK_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT)) {
+ if ((aux_event == event) ||
+ WOLFSENTRY_CHECK_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT)) {
ret = WOLFSENTRY_ERROR_ENCODE(INCOMPATIBLE_STATE);
goto out;
}
} else
aux_event = NULL;
- if (event->aux_event)
- WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event->aux_event, NULL /* action_results */));
+ if (aux_event != NULL) {
+ ret = wolfsentry_event_aux_owner_add(aux_event);
+ if (ret < 0)
+ goto out;
+ }
+
+ old_aux_event = event->aux_event;
event->aux_event = aux_event;
+ if (old_aux_event != NULL) {
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_aux_owner_remove(old_aux_event));
+ wolfsentry_event_aux_owner_resync_mark(old_aux_event);
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, old_aux_event, NULL /* action_results */));
+ }
ret = WOLFSENTRY_ERROR_ENCODE(OK);
out:
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, event, NULL /* action_results */));
- if (aux_event) {
- if (ret < 0)
- WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, aux_event, NULL /* action_results */));
- else {
- if (! WOLFSENTRY_CHECK_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT))
- WOLFSENTRY_SET_BITS(aux_event->flags, WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT);
- }
- }
+ if ((aux_event != NULL) && (ret < 0))
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, aux_event, NULL /* action_results */));
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
diff --git a/src/json/centijson_dom.c b/src/json/centijson_dom.c
index cf880d95..9bc4a69e 100644
--- a/src/json/centijson_dom.c
+++ b/src/json/centijson_dom.c
@@ -61,7 +61,7 @@
static void *json_malloc(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator), size_t size) {
if (allocator)
- return allocator->malloc(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), size);
+ return allocator->malloc(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator->context), size);
else
return NULL;
}
@@ -141,6 +141,34 @@ init_number(
}
}
+static int
+json_dom_path_push(JSON_DOM_PARSER* dom_parser, JSON_VALUE* value)
+{
+ if(dom_parser->path_size >= dom_parser->path_alloc) {
+ JSON_VALUE** new_path;
+ size_t new_path_alloc;
+
+ if(dom_parser->path_alloc == 0) {
+ new_path_alloc = 32;
+ }
+ else if(dom_parser->path_alloc > SIZE_MAX / 2 / sizeof(JSON_VALUE*)) {
+ return JSON_ERR_OUTOFMEMORY;
+ }
+ else {
+ new_path_alloc = dom_parser->path_alloc * 2;
+ }
+ new_path = (JSON_VALUE**) realloc((void *)dom_parser->path, new_path_alloc * sizeof(JSON_VALUE*));
+ if(new_path == NULL)
+ return JSON_ERR_OUTOFMEMORY;
+
+ dom_parser->path = new_path;
+ dom_parser->path_alloc = new_path_alloc;
+ }
+
+ dom_parser->path[dom_parser->path_size++] = value;
+ return 0;
+}
+
int
json_dom_process(JSON_TYPE type, const unsigned char* data, size_t data_size, void* user_data)
{
@@ -154,6 +182,16 @@ json_dom_process(JSON_TYPE type, const unsigned char* data, size_t data_size, vo
return 0;
}
+ if(dom_parser->path_size > 0 &&
+ dom_parser->path[dom_parser->path_size - 1] == NULL)
+ {
+ /* A NULL path entry marks a duplicate container dropped by USEFIRST;
+ * swallow its contents so the enclosing nesting state stays correct. */
+ if(type == JSON_ARRAY_BEG || type == JSON_OBJECT_BEG)
+ return json_dom_path_push(dom_parser, NULL);
+ return 0;
+ }
+
if(type == JSON_KEY) {
/* Object key: We just store it until we get the json_value to use it with. */
if(json_value_init_string_(
@@ -201,7 +239,10 @@ json_dom_process(JSON_TYPE type, const unsigned char* data, size_t data_size, vo
if(!json_value_is_new(new_json_value)) {
/* We have already set json_value for this key. */
switch(dom_parser->flags & JSON_DOM_DUPKEY_MASK) {
- case JSON_DOM_DUPKEY_USEFIRST: return 0;
+ case JSON_DOM_DUPKEY_USEFIRST:
+ if(type == JSON_ARRAY_BEG || type == JSON_OBJECT_BEG)
+ return json_dom_path_push(dom_parser, NULL);
+ return 0;
case JSON_DOM_DUPKEY_USELAST:
ret = json_value_fini(
#ifdef WOLFSENTRY
@@ -264,28 +305,9 @@ json_dom_process(JSON_TYPE type, const unsigned char* data, size_t data_size, vo
if(type == JSON_ARRAY_BEG || type == JSON_OBJECT_BEG) {
/* Push the array or object to the path, so we know where to
* append their json_values. */
- if(dom_parser->path_size >= dom_parser->path_alloc) {
- JSON_VALUE** new_path;
- size_t new_path_alloc;
-
- if(dom_parser->path_alloc == 0) {
- new_path_alloc = 32;
- }
- else if(dom_parser->path_alloc > SIZE_MAX / 2 / sizeof(JSON_VALUE*)) {
- return JSON_ERR_OUTOFMEMORY;
- }
- else {
- new_path_alloc = dom_parser->path_alloc * 2;
- }
- new_path = (JSON_VALUE**) realloc((void *)dom_parser->path, new_path_alloc * sizeof(JSON_VALUE*));
- if(new_path == NULL)
- return JSON_ERR_OUTOFMEMORY;
-
- dom_parser->path = new_path;
- dom_parser->path_alloc = new_path_alloc;
- }
-
- dom_parser->path[dom_parser->path_size++] = new_json_value;
+ ret = json_dom_path_push(dom_parser, new_json_value);
+ if(ret < 0)
+ return ret;
}
return 0;
diff --git a/src/json/centijson_sax.c b/src/json/centijson_sax.c
index 1c71d536..0bb42ba6 100644
--- a/src/json/centijson_sax.c
+++ b/src/json/centijson_sax.c
@@ -54,6 +54,7 @@
#ifdef CENTIJSON_USE_LOCALE
#include /* localeconv() */
#endif
+#include /* DBL_MAX */
#include /* snprintf() */
#include
#include
@@ -404,8 +405,12 @@ json_literal_automaton(JSON_PARSER* parser, const unsigned char* input, size_t s
if(off < size && !IS_TOKEN_BOUNDARY(input[off])) {
memcpy(&parser->pos, &parser->value_pos, sizeof(JSON_INPUT_POS));
json_raise(parser, JSON_ERR_SYNTAX);
- } else if(input == NULL /* EOF */ ||
- (off < size && IS_TOKEN_BOUNDARY(input[off]))) {
+ } else if(input == NULL /* EOF */) {
+ if(parser->substate < literal_size)
+ json_raise_for_value(parser, JSON_ERR_SYNTAX);
+ else
+ json_process(parser, type, NULL, 0);
+ } else if(off < size && IS_TOKEN_BOUNDARY(input[off])) {
json_process(parser, type, NULL, 0);
}
return off;
@@ -980,7 +985,10 @@ json_fini(JSON_PARSER* parser, JSON_INPUT_POS* p_pos)
if(parser->automaton != AUTOMATON_MAIN) {
json_raise(parser, JSON_ERR_SYNTAX);
}
- } else if(parser->nesting_level != 0 || !(parser->state & CAN_SEE_EOF)) {
+ }
+
+ if(parser->errcode >= 0 &&
+ (parser->nesting_level != 0 || !(parser->state & CAN_SEE_EOF))) {
json_raise_unexpected(parser);
}
}
@@ -1221,7 +1229,7 @@ json_number_to_double(const unsigned char* num, size_t num_size, double* p_resul
* requirements.
*/
- if(num_size + 1 < sizeof(local_buffer)) {
+ if(num_size < sizeof(local_buffer)) {
/* The number is short enough so we can avoid heap allocation. */
buffer = local_buffer;
} else {
@@ -1315,11 +1323,7 @@ json_dump_uint64(uint64_t u64, JSON_DUMP_CALLBACK write_func, void* user_data)
WOLFSENTRY_API int
json_dump_double(double dbl, JSON_DUMP_CALLBACK write_func, void* user_data)
{
-#if defined(FREERTOS) || (__STDC_VERSION__ < 199901L)
- static const char fmt[] = "%.16f";
-#else
- static const char fmt[] = "%.16lg";
-#endif
+ static const char fmt[] = "%.17g";
static const size_t extra_bytes = 2; /* Space reserved for ".0" */
#ifdef CENTIJSON_USE_LOCALE
struct lconv* locale_info;
@@ -1334,6 +1338,10 @@ json_dump_double(double dbl, JSON_DUMP_CALLBACK write_func, void* user_data)
char* fp;
int ret;
+ /* JSON has no representation for infinity or NaN. */
+ if(dbl != dbl || dbl > DBL_MAX || dbl < -DBL_MAX)
+ return write_func((const unsigned char *)"null", 4, user_data);
+
while(1) {
n = snprintf(buffer, capacity, fmt, dbl);
if(n >= 0 && (size_t)n < capacity)
diff --git a/src/json/centijson_value.c b/src/json/centijson_value.c
index 51cf4059..b9d52fa2 100644
--- a/src/json/centijson_value.c
+++ b/src/json/centijson_value.c
@@ -56,6 +56,7 @@
#endif
#include
+#include
#ifndef WOLFSENTRY_NO_ALLOCA
#include
#endif
@@ -255,6 +256,27 @@ json_value_type(const JSON_VALUE* v)
return (JSON_VALUE_TYPE)(v->data.data_bytes[0] & TYPE_MASK);
}
+static uint64_t json_int_magnitude(int64_t x)
+{
+ return (x < 0) ? (~(uint64_t)x + 1U) : (uint64_t)x;
+}
+
+/* An integer is exactly representable in a binary floating type with
+ * mantissa_bits of precision when its significant bits (ignoring trailing
+ * zeros) fit in that precision. */
+static int json_int_fits_mantissa(uint64_t u, int mantissa_bits)
+{
+ int bits = 0;
+
+ while((u != 0) && ((u & 1U) == 0))
+ u >>= 1;
+ while(u != 0) {
+ ++bits;
+ u >>= 1;
+ }
+ return bits <= mantissa_bits;
+}
+
WOLFSENTRY_API int
json_value_is_compatible(const JSON_VALUE* v, JSON_VALUE_TYPE type)
{
@@ -265,39 +287,43 @@ json_value_is_compatible(const JSON_VALUE* v, JSON_VALUE_TYPE type)
* long as the conversion does not loose much information. */
switch(json_value_type(v)) {
case JSON_VALUE_INT32:
- return (type == JSON_VALUE_INT64 || type == JSON_VALUE_FLOAT || type == JSON_VALUE_DOUBLE) ||
+ return (type == JSON_VALUE_INT64 || type == JSON_VALUE_DOUBLE) ||
+ (type == JSON_VALUE_FLOAT && json_int_fits_mantissa(json_int_magnitude(json_value_int32(v)), FLT_MANT_DIG)) ||
(type == JSON_VALUE_UINT32 && json_value_int32(v) >= 0) ||
(type == JSON_VALUE_UINT64 && json_value_int32(v) >= 0);
case JSON_VALUE_UINT32:
- return (type == JSON_VALUE_INT64 || type == JSON_VALUE_UINT64 || type == JSON_VALUE_FLOAT || type == JSON_VALUE_DOUBLE) ||
+ return (type == JSON_VALUE_INT64 || type == JSON_VALUE_UINT64 || type == JSON_VALUE_DOUBLE) ||
+ (type == JSON_VALUE_FLOAT && json_int_fits_mantissa((uint64_t)json_value_uint32(v), FLT_MANT_DIG)) ||
(type == JSON_VALUE_INT32 && json_value_uint32(v) <= INT32_MAX);
case JSON_VALUE_INT64:
- return (type == JSON_VALUE_FLOAT || type == JSON_VALUE_DOUBLE) ||
+ return (type == JSON_VALUE_DOUBLE && json_int_fits_mantissa(json_int_magnitude(json_value_int64(v)), DBL_MANT_DIG)) ||
+ (type == JSON_VALUE_FLOAT && json_int_fits_mantissa(json_int_magnitude(json_value_int64(v)), FLT_MANT_DIG)) ||
(type == JSON_VALUE_INT32 && json_value_int64(v) >= INT32_MIN && json_value_int64(v) <= INT32_MAX) ||
(type == JSON_VALUE_UINT32 && json_value_int64(v) >= 0 && json_value_int64(v) <= UINT32_MAX) ||
(type == JSON_VALUE_UINT64 && json_value_int64(v) >= 0);
case JSON_VALUE_UINT64:
- return (type == JSON_VALUE_FLOAT || type == JSON_VALUE_DOUBLE) ||
+ return (type == JSON_VALUE_DOUBLE && json_int_fits_mantissa(json_value_uint64(v), DBL_MANT_DIG)) ||
+ (type == JSON_VALUE_FLOAT && json_int_fits_mantissa(json_value_uint64(v), FLT_MANT_DIG)) ||
(type == JSON_VALUE_INT32 && json_value_uint64(v) <= INT32_MAX) ||
(type == JSON_VALUE_UINT32 && json_value_uint64(v) <= UINT32_MAX) ||
(type == JSON_VALUE_INT64 && json_value_uint64(v) <= INT64_MAX);
case JSON_VALUE_FLOAT:
return (type == JSON_VALUE_DOUBLE) ||
- (type == JSON_VALUE_INT32 && json_value_float(v) == (float)json_value_int32(v)) ||
- (type == JSON_VALUE_UINT32 && json_value_float(v) == (float)json_value_uint32(v)) ||
- (type == JSON_VALUE_INT64 && json_value_float(v) == (float)json_value_int64(v)) ||
- (type == JSON_VALUE_UINT64 && json_value_float(v) == (float)json_value_uint64(v));
+ (type == JSON_VALUE_INT32 && (double)json_value_float(v) >= -2147483648.0 && (double)json_value_float(v) <= 2147483647.0 && json_value_float(v) == (float)json_value_int32(v)) ||
+ (type == JSON_VALUE_UINT32 && (double)json_value_float(v) >= 0.0 && (double)json_value_float(v) <= 4294967295.0 && json_value_float(v) == (float)json_value_uint32(v)) ||
+ (type == JSON_VALUE_INT64 && (double)json_value_float(v) >= -9223372036854775808.0 && (double)json_value_float(v) < 9223372036854775808.0 && json_value_float(v) == (float)json_value_int64(v)) ||
+ (type == JSON_VALUE_UINT64 && (double)json_value_float(v) >= 0.0 && (double)json_value_float(v) < 18446744073709551616.0 && json_value_float(v) == (float)json_value_uint64(v));
case JSON_VALUE_DOUBLE:
- return (type == JSON_VALUE_FLOAT) ||
- (type == JSON_VALUE_INT32 && json_value_double(v) == (double)json_value_int32(v)) ||
- (type == JSON_VALUE_UINT32 && json_value_double(v) == (double)json_value_uint32(v)) ||
- (type == JSON_VALUE_INT64 && json_value_double(v) == (double)json_value_int64(v)) ||
- (type == JSON_VALUE_UINT64 && json_value_double(v) == (double)json_value_uint64(v));
+ return (type == JSON_VALUE_FLOAT && json_value_double(v) >= -(double)FLT_MAX && json_value_double(v) <= (double)FLT_MAX && (double)(float)json_value_double(v) == json_value_double(v)) || /* cppcheck-suppress suspiciousFloatingPointCast */
+ (type == JSON_VALUE_INT32 && json_value_double(v) >= -2147483648.0 && json_value_double(v) <= 2147483647.0 && json_value_double(v) == (double)json_value_int32(v)) ||
+ (type == JSON_VALUE_UINT32 && json_value_double(v) >= 0.0 && json_value_double(v) <= 4294967295.0 && json_value_double(v) == (double)json_value_uint32(v)) ||
+ (type == JSON_VALUE_INT64 && json_value_double(v) >= -9223372036854775808.0 && json_value_double(v) < 9223372036854775808.0 && json_value_double(v) == (double)json_value_int64(v)) ||
+ (type == JSON_VALUE_UINT64 && json_value_double(v) >= 0.0 && json_value_double(v) < 18446744073709551616.0 && json_value_double(v) == (double)json_value_uint64(v));
default:
break;
@@ -320,30 +346,67 @@ json_value_path(JSON_VALUE* root, const char* path)
{
const unsigned char* token_beg = (const unsigned char *)path;
const unsigned char* token_end;
+ const unsigned char* p;
JSON_VALUE* v = root;
+ size_t path_index;
+ size_t arr_size;
+ int negative;
+ int have_digit;
while(1) {
token_end = token_beg;
while(*token_end != '\0' && *token_end != '/')
token_end++;
- if(token_end - token_beg > 2 && token_beg[0] == '[' && token_end[-1] == ']') {
- size_t path_index = 0;
+ p = token_beg;
+
+ /* Optional leading dictionary key, up to the first '[' or token end. */
+ if(p < token_end && *p != '[') {
+ const unsigned char* key_end = p;
+ while(key_end < token_end && *key_end != '[')
+ key_end++;
+ v = json_value_dict_get_(v, p, (size_t)(key_end - p));
+ if(v == NULL)
+ return NULL;
+ p = key_end;
+ }
- token_beg++;
- while('0' <= *token_beg && *token_beg <= '9') {
- path_index = (path_index * 10U) + (*token_beg - (unsigned)'0');
- token_beg++;
+ /* Zero or more attached array indexes "[n]", where n may be negative
+ * (relative to the array length). */
+ while(p < token_end && *p == '[') {
+ p++;
+ negative = 0;
+ if(*p == '-') {
+ negative = 1;
+ p++;
}
- if(*token_beg != ']')
+ path_index = 0;
+ have_digit = 0;
+ while('0' <= *p && *p <= '9') {
+ size_t digit = (size_t)(*p - (unsigned char)'0');
+ if(path_index > (SIZE_MAX - digit) / 10U)
+ return NULL;
+ path_index = (path_index * 10U) + digit;
+ have_digit = 1;
+ p++;
+ }
+ if((! have_digit) || (*p != ']'))
return NULL;
+ p++;
+ if(negative) {
+ arr_size = json_value_array_size(v);
+ if((path_index == 0) || (path_index > arr_size))
+ return NULL;
+ path_index = arr_size - path_index;
+ }
v = json_value_array_get(v, path_index);
- } else if(token_end - token_beg > 0) {
- v = json_value_dict_get_(v, token_beg, (size_t)(token_end - token_beg));
+ if(v == NULL)
+ return NULL;
}
- if(v == NULL)
+ /* The token must be fully consumed; reject trailing bytes. */
+ if(p != token_end)
return NULL;
if(*token_end == '\0')
@@ -881,6 +944,15 @@ json_value_array_realloc(
{
JSON_VALUE* json_value_buf;
+ /* Shrinking to zero: free the buffer directly rather than via realloc(p, 0),
+ * which is allowed to free and return NULL and would leave a dangling buffer. */
+ if(alloc == 0) {
+ free(a->json_value_buf);
+ a->json_value_buf = NULL;
+ a->alloc = 0;
+ return 0;
+ }
+
json_value_buf = (JSON_VALUE*) realloc(a->json_value_buf, alloc * sizeof(JSON_VALUE));
if(json_value_buf == NULL)
return -1;
@@ -945,16 +1017,21 @@ json_value_array_insert(
JSON_VALUE* v, size_t index)
{
ARRAY* a = json_value_array_payload(v);
+ size_t new_alloc;
if(a == NULL || index > a->size)
return NULL;
if(a->size >= a->alloc) {
+ new_alloc = (a->alloc > 0) ? a->alloc * 2 : 1;
+ /* a wrapped doubling must not reach the shrink-to-zero path */
+ if(new_alloc <= a->size)
+ return NULL;
if(json_value_array_realloc(
#ifdef WOLFSENTRY
WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator),
#endif
- a, (a->alloc > 0) ? a->alloc * 2 : 1) != 0)
+ a, new_alloc) != 0)
return NULL;
}
@@ -990,12 +1067,16 @@ json_value_array_remove_range(
{
ARRAY* a = json_value_array_payload(v);
size_t i;
+ size_t end;
int ret;
- if(a == NULL || index + count > a->size)
+ /* Validate without adding, so index + count cannot wrap. */
+ if(a == NULL || index > a->size || count > a->size - index)
return -1;
- for(i = index; i < index + count; i++) {
+ end = index + count;
+
+ for(i = index; i < end; i++) {
ret = json_value_fini(
#ifdef WOLFSENTRY
WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator),
@@ -1005,9 +1086,9 @@ json_value_array_remove_range(
return ret;
}
- if(index + count < a->size) {
- memmove(a->json_value_buf + index, a->json_value_buf + index + count,
- (a->size - (index + count)) * sizeof(JSON_VALUE));
+ if(end < a->size) {
+ memmove(a->json_value_buf + index, a->json_value_buf + end,
+ (a->size - end) * sizeof(JSON_VALUE));
}
a->size -= count;
@@ -1556,6 +1637,7 @@ json_value_dict_remove_(
int path_len = 0;
int cmp;
int ret;
+ int ret2;
if (node == NULL)
return -1;
@@ -1673,24 +1755,24 @@ json_value_dict_remove_(
else
d->order_tail = node->order_prev;
}
+ /* The node is already unlinked, so finalize both members best-effort, then
+ * always free it and decrement the size to keep the dictionary consistent. */
ret = json_value_fini(
#ifdef WOLFSENTRY
WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator),
#endif
&node->key);
- if (ret < 0)
- return ret;
- ret = json_value_fini(
+ ret2 = json_value_fini(
#ifdef WOLFSENTRY
WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator),
#endif
&node->json_value);
- if (ret < 0)
- return ret;
+ if (ret == 0)
+ ret = ret2;
free(node);
d->size--;
- return 0;
+ return ret;
}
WOLFSENTRY_API int
@@ -1770,48 +1852,53 @@ json_value_dict_clean(
JSON_VALUE* v)
{
DICT* d = json_value_dict_payload(v);
- RBTREE **stack;
- int stack_size;
RBTREE* node;
- RBTREE* right;
+ RBTREE* left;
int ret;
if(d == NULL)
return 0;
- stack = (RBTREE **)malloc(rbtree_stack_size_needed(d));
- if (stack == NULL)
- return JSON_ERR_OUTOFMEMORY;
-
- stack_size = json_value_dict_leftmost_path(stack, d->root);
-
- while(stack_size > 0) {
- node = stack[--stack_size];
- right = node->right;
+ /* Rotate left children up instead of keeping a traversal stack, so cleanup
+ * cannot fail on allocation; d->root always holds every remaining node. */
+ node = d->root;
+ while(node != NULL) {
+ if(node->left != NULL) {
+ left = node->left;
+ node->left = left->right;
+ left->right = node;
+ node = left;
+ d->root = node;
+ continue;
+ }
ret = json_value_fini(
#ifdef WOLFSENTRY
WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator),
#endif
&node->key);
- if (ret < 0) {
- free(node);
- free((void *)stack);
+ if (ret < 0)
return ret;
- }
ret = json_value_fini(
#ifdef WOLFSENTRY
WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator),
#endif
&node->json_value);
- if (ret < 0) {
- free(node);
- free((void *)stack);
+ if (ret < 0)
return ret;
+ d->root = node->right;
+ if(v->data.data_bytes[0] & HAS_ORDERLIST) {
+ if(node->order_prev != NULL)
+ node->order_prev->order_next = node->order_next;
+ else
+ d->order_head = node->order_next;
+ if(node->order_next != NULL)
+ node->order_next->order_prev = node->order_prev;
+ else
+ d->order_tail = node->order_prev;
}
free(node);
-
- stack_size += json_value_dict_leftmost_path(stack + stack_size, right);
+ node = d->root;
}
if(v->data.data_bytes[0] & HAS_ORDERLIST)
@@ -1819,8 +1906,6 @@ json_value_dict_clean(
else
memset(d, 0, OFFSETOF(DICT, order_head));
- free((void *)stack);
-
return 0;
}
/* NOLINTEND(misc-no-recursion) */
@@ -1876,38 +1961,47 @@ json_value_clone(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allo
const ARRAY* src_a = json_value_array_payload((JSON_VALUE *)node);
size_t n = json_value_array_size(node);
const JSON_VALUE* src_values = json_value_array_get_all(node);
- ARRAY* dest_a;
- JSON_VALUE* dest_values;
+ ARRAY* dest_a = NULL;
+ JSON_VALUE* dest_values = NULL;
size_t i;
ret = json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), clone);
- if (ret < 0)
- break;
- dest_a = json_value_array_payload(clone);
- if (! dest_a) {
- ret = JSON_ERR_INTERNAL;
- break;
+ if (ret == 0) {
+ dest_a = json_value_array_payload(clone);
+ if (dest_a == NULL)
+ ret = JSON_ERR_INTERNAL;
}
- ret = json_value_array_realloc(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator),
- dest_a, src_a->alloc);
- if (ret < 0)
- break;
+ /* Skip the element buffer entirely for an empty array; a zero-size
+ * allocation may legitimately return NULL. */
+ if ((ret == 0) && (src_a->alloc > 0))
+ ret = json_value_array_realloc(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator),
+ dest_a, src_a->alloc);
- dest_values = json_value_array_get_all(clone);
- if (! dest_values) {
- ret = JSON_ERR_INTERNAL;
- break;
+ if (ret == 0) {
+ dest_values = json_value_array_get_all(clone);
+ if ((n > 0) && (dest_values == NULL))
+ ret = JSON_ERR_INTERNAL;
}
- for(i = 0; i < n; i++) {
- ret = json_value_clone(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src_values[i], &dest_values[i]);
- if (ret < 0)
- break;
+ if (ret == 0) {
+ for(i = 0; i < n; i++) {
+ json_value_init_null(&dest_values[i]);
+ dest_a->size = i + 1;
+ ret = json_value_clone(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src_values[i], &dest_values[i]);
+ if (ret < 0)
+ break;
+ }
}
- dest_a->size = src_a->size;
+ /* On any failure finalize the clone so the initialized prefix and
+ * the array allocation are released, not leaked. */
+ if (ret < 0) {
+ int ret2 = json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), clone);
+ if (ret2 < 0)
+ WOLFSENTRY_WARN("json_value_fini: %s\n", json_error_str(ret2));
+ }
break;
}
@@ -1940,11 +2034,16 @@ json_value_clone(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allo
}
} else {
int stack_size;
- RBTREE **stack = (RBTREE **)malloc(rbtree_stack_size_needed(src_dict)); /* put this on the heap to avoid runaway growth of stack on deep JSON trees. */
- if (! stack) {
+ RBTREE **stack = NULL;
+ /* put the stack on the heap to avoid runaway growth of the C
+ * stack on deep JSON trees; an empty tree needs none, and a
+ * zero-size allocation may legitimately return NULL. */
+ if (src_dict->root != NULL)
+ stack = (RBTREE **)malloc(rbtree_stack_size_needed(src_dict));
+ if ((src_dict->root != NULL) && (stack == NULL)) {
ret = JSON_ERR_OUTOFMEMORY;
} else {
- stack_size = json_value_dict_leftmost_path(stack, src_dict->root);
+ stack_size = (stack != NULL) ? json_value_dict_leftmost_path(stack, src_dict->root) : 0;
while(stack_size > 0) {
src_dict_node = stack[--stack_size];
diff --git a/src/json/load_config.c b/src/json/load_config.c
index 732f17a9..f691c5e8 100644
--- a/src/json/load_config.c
+++ b/src/json/load_config.c
@@ -111,6 +111,9 @@ struct wolfsentry_json_process_state {
WOLFSENTRY_SOCKADDR(WOLFSENTRY_MAX_ADDR_BITS) remote;
WOLFSENTRY_SOCKADDR(WOLFSENTRY_MAX_ADDR_BITS) local;
wolfsentry_route_flags_t flags;
+ int tcplike_port_numbers_explicit;
+ int remote_prefix_bits_given;
+ int local_prefix_bits_given;
} route;
struct {
char label[WOLFSENTRY_MAX_LABEL_BYTES];
@@ -154,7 +157,58 @@ static wolfsentry_errcode_t reset_o_u_c(struct wolfsentry_json_process_state *jp
WOLFSENTRY_RETURN_OK;
}
+/* Range check that does not depend on errno: re-scan the already
+ * syntactically-validated numeric string in buf (strtol-style base-0 rules) and
+ * reject it when its magnitude exceeds limit. A leading sign is skipped so the
+ * caller decides sign handling.
+ */
+static wolfsentry_errcode_t convert_check_magnitude(const char *buf, uint64_t limit) {
+ uint64_t acc = 0;
+ uint64_t base = 10;
+ const char *p = buf;
+
+ while ((*p == ' ') || (*p == '\t'))
+ ++p;
+ if ((*p == '-') || (*p == '+'))
+ ++p;
+ if ((p[0] == '0') && ((p[1] == 'x') || (p[1] == 'X'))) {
+ base = 16;
+ p += 2;
+ } else if (p[0] == '0') {
+ base = 8;
+ ++p;
+ }
+
+ while (*p != 0) {
+ uint64_t digit;
+ if ((*p >= '0') && (*p <= '9'))
+ digit = (uint64_t)(*p - '0');
+ else if ((*p >= 'a') && (*p <= 'f'))
+ digit = (uint64_t)(*p - 'a') + 10U;
+ else if ((*p >= 'A') && (*p <= 'F'))
+ digit = (uint64_t)(*p - 'A') + 10U;
+ else
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ if (digit >= base)
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ if (acc > (limit - digit) / base)
+ WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG);
+ acc = (acc * base) + digit;
+ ++p;
+ }
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+/* local equivalent of strspn(p, " \t"), which freestanding targets may lack. */
+static const char *convert_skip_blanks(const char *p) {
+ while ((*p == ' ') || (*p == '\t'))
+ ++p;
+ return p;
+}
+
static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char *data, size_t data_size, uint64_t *out) {
+ wolfsentry_errcode_t ret;
char buf[24];
char *endptr;
#ifdef WOLFSENTRY_HAVE_LONG_LONG
@@ -172,6 +226,12 @@ static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char *
memcpy(buf, data, data_size);
buf[data_size] = 0;
+ /* strtoull()/strtoul() silently negate a leading minus; reject it so a
+ * negative literal does not wrap to a large unsigned value.
+ */
+ if (*convert_skip_blanks(buf) == '-')
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+
#ifndef WOLFSENTRY_NO_ERRNO_H
errno = 0;
#endif
@@ -187,14 +247,19 @@ static wolfsentry_errcode_t convert_uint64(JSON_TYPE type, const unsigned char *
if ((size_t)(endptr - buf) != data_size)
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ ret = convert_check_magnitude(buf, (uint64_t)MAX_UINT_OF(conv));
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
*out = (uint64_t)conv;
WOLFSENTRY_RETURN_OK;
}
static wolfsentry_errcode_t convert_sint64(JSON_TYPE type, const unsigned char *data, size_t data_size, int64_t *out) {
+ wolfsentry_errcode_t ret;
char buf[24];
char *endptr;
+ const char *sign_p;
+ uint64_t magnitude_limit;
#ifdef WOLFSENTRY_HAVE_LONG_LONG
long long conv;
#else
@@ -225,6 +290,13 @@ static wolfsentry_errcode_t convert_sint64(JSON_TYPE type, const unsigned char *
if ((size_t)(endptr - buf) != data_size)
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ sign_p = convert_skip_blanks(buf);
+ if (*sign_p == '-')
+ magnitude_limit = (uint64_t)MAX_SINT_OF(conv) + 1U;
+ else
+ magnitude_limit = (uint64_t)MAX_SINT_OF(conv);
+ ret = convert_check_magnitude(buf, magnitude_limit);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
*out = (int64_t)conv;
WOLFSENTRY_RETURN_OK;
@@ -233,7 +305,14 @@ static wolfsentry_errcode_t convert_sint64(JSON_TYPE type, const unsigned char *
static wolfsentry_errcode_t convert_uint32(JSON_TYPE type, const unsigned char *data, size_t data_size, uint32_t *out) {
char buf[16];
char *endptr;
+ /* use the widest available unsigned type so the range check below is live
+ * even on ILP32 ABIs, where unsigned long is the same width as *out.
+ */
+#ifdef WOLFSENTRY_HAVE_LONG_LONG
+ unsigned long long conv;
+#else
unsigned long conv;
+#endif
/* allow JSON_STRING to accommodate hex and octal values. */
if ((type != JSON_NUMBER) && (type != JSON_STRING))
@@ -247,7 +326,11 @@ static wolfsentry_errcode_t convert_uint32(JSON_TYPE type, const unsigned char *
#ifndef WOLFSENTRY_NO_ERRNO_H
errno = 0;
#endif
+#ifdef WOLFSENTRY_HAVE_LONG_LONG
+ conv = strtoull(buf, &endptr, 0);
+#else
conv = strtoul(buf, &endptr, 0);
+#endif
#ifndef WOLFSENTRY_NO_ERRNO_H
if (errno != 0)
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
@@ -295,7 +378,7 @@ static wolfsentry_errcode_t convert_uint16(JSON_TYPE type, const unsigned char *
}
static wolfsentry_errcode_t convert_uint8(JSON_TYPE type, const unsigned char *data, size_t data_size, uint8_t *out) {
- char buf[4];
+ char buf[8];
char *endptr;
unsigned long conv;
@@ -327,7 +410,8 @@ static wolfsentry_errcode_t convert_uint8(JSON_TYPE type, const unsigned char *d
}
static wolfsentry_errcode_t convert_double(JSON_TYPE type, const unsigned char *data, size_t data_size, double *out) {
- char buf[24];
+ /* fits the longest %.17g rendering, 24 characters */
+ char buf[25];
char *endptr;
double conv;
@@ -413,6 +497,12 @@ static wolfsentry_errcode_t convert_wolfsentry_duration(struct wolfsentry_contex
}
if ((size_t)(endptr - buf) != data_size)
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ /* durations are non-negative; a negative value (including a LONG_MIN
+ * saturation when errno is unavailable) would otherwise reach the interval
+ * conversion and overflow.
+ */
+ if (conv < 0)
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
if ((ret = wolfsentry_interval_from_seconds(wolfsentry, conv, 0 /* howlong_nsecs */, out)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
else
@@ -623,6 +713,13 @@ static inline int convert_hex_byte(const unsigned char **in, size_t *in_len, byt
return 0;
}
+/* a handler registered for AF_LOCAL takes precedence over the built-in literal path */
+static int af_local_is_builtin(struct wolfsentry_json_process_state *jps)
+{
+ wolfsentry_addr_family_parser_t parser;
+ return (wolfsentry_addr_family_get_parser(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_AF_LOCAL, &parser) < 0);
+}
+
static wolfsentry_errcode_t convert_sockaddr_address(
struct wolfsentry_json_process_state *jps,
JSON_TYPE type,
@@ -632,6 +729,7 @@ static wolfsentry_errcode_t convert_sockaddr_address(
byte *sa_addr)
{
if ((sa->sa_family == WOLFSENTRY_AF_LINK48) || (sa->sa_family == WOLFSENTRY_AF_LINK64)) {
+ wolfsentry_addr_family_t requested_family = sa->sa_family;
int n;
if (type != JSON_STRING)
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
@@ -642,6 +740,12 @@ static wolfsentry_errcode_t convert_sockaddr_address(
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
++n;
if (data_size == 0) {
+ /* an explicit EUI-64 family must not be silently reclassified
+ * as EUI-48 by the parsed byte count (LINK48 and the generic
+ * LINK alias share a value, so only LINK64 can be enforced).
+ */
+ if ((requested_family == WOLFSENTRY_AF_LINK64) && (n != 8))
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
if (n == 6)
sa->sa_family = WOLFSENTRY_AF_LINK48;
else if (n == 8)
@@ -704,7 +808,14 @@ static wolfsentry_errcode_t convert_sockaddr_address(
else if (sa->sa_family == WOLFSENTRY_AF_CAN) {
/* accept CAN addrs as either strings consisting of hex or octal numbers, or as a JSON-native decimal integer. */
char d_buf[13], *endptr;
+ /* use the widest available unsigned type so the range check below is
+ * live even on ILP32 ABIs, where unsigned long equals uint32_t.
+ */
+#ifdef WOLFSENTRY_HAVE_LONG_LONG
+ unsigned long long addr;
+#else
unsigned long addr;
+#endif
if ((type != JSON_STRING) && (type != JSON_NUMBER))
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
if (data_size >= sizeof d_buf)
@@ -725,7 +836,18 @@ static wolfsentry_errcode_t convert_sockaddr_address(
}
memcpy(d_buf, data, data_size);
d_buf[data_size] = 0;
+#ifndef WOLFSENTRY_NO_ERRNO_H
+ errno = 0;
+#endif
+#ifdef WOLFSENTRY_HAVE_LONG_LONG
+ addr = strtoull(d_buf, &endptr, 0);
+#else
addr = strtoul(d_buf, &endptr, 0);
+#endif
+#ifndef WOLFSENTRY_NO_ERRNO_H
+ if (errno != 0)
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+#endif
if ((size_t)(endptr - d_buf) != data_size)
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
if (addr > MAX_UINT_OF(uint32_t))
@@ -738,6 +860,35 @@ static wolfsentry_errcode_t convert_sockaddr_address(
sa->addr_len = 32;
WOLFSENTRY_RETURN_OK;
}
+ else if ((sa->sa_family == WOLFSENTRY_AF_LOCAL) && af_local_is_builtin(jps)) {
+ /* mirror wolfsentry_route_format_address(): the address is a raw path
+ * stored verbatim, with addr_len recording its length.
+ */
+ size_t i;
+ if (type != JSON_STRING)
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ /* an empty path would be a zero-length, match-all prefix. */
+ if (data_size == 0)
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ if (data_size > (size_t)(WOLFSENTRY_MAX_ADDR_BITS / 8))
+ WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
+ /* the route formatter emits the path verbatim between quotes without
+ * escaping, so reject bytes that would corrupt or inject into that
+ * dump (quote, backslash, control bytes including NUL).
+ */
+ for (i = 0; i < data_size; ++i) {
+ if ((data[i] < 0x20) || (data[i] == 0x7f) || (data[i] == '"') || (data[i] == '\\'))
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ }
+ /* addr_len must equal the pathname length, so a prefix-bits supplied
+ * before the address cannot leave trailing implicit bytes in the dump.
+ */
+ if ((sa->addr_len != 0) && (sa->addr_len != (wolfsentry_addr_bits_t)(data_size * 8)))
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ memcpy(sa_addr, data, data_size);
+ sa->addr_len = (wolfsentry_addr_bits_t)(data_size * 8);
+ WOLFSENTRY_RETURN_OK;
+ }
else if (sa->sa_family == WOLFSENTRY_AF_UNSPEC) {
WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE);
}
@@ -813,6 +964,10 @@ static wolfsentry_errcode_t convert_sockaddr_port_name(struct wolfsentry_json_pr
WOLFSENTRY_ERROR_RETURN(ITEM_NOT_FOUND);
}
+ /* glibc reports an unknown service with a zero return and a NULL result. */
+ if (s == NULL)
+ WOLFSENTRY_ERROR_RETURN(ITEM_NOT_FOUND);
+
sa->sa_port = (wolfsentry_port_t)ntohs((uint16_t)s->s_port);
WOLFSENTRY_RETURN_OK;
}
@@ -836,13 +991,34 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_
else
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
} else if (! strcmp(jps->cur_keyname, "address")) {
+ wolfsentry_addr_bits_t prior_addr_len = sa->addr_len;
+ wolfsentry_addr_bits_t max_addr_bits;
+ int prefix_given = (prior_addr_len != 0);
+ wolfsentry_errcode_t ret;
if (sa->sa_family == WOLFSENTRY_AF_UNSPEC)
WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE);
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+ /* a bitmask also sets addr_len, to the combined address and mask width */
+ if (jps->o_u_c.route.flags & (sa == (struct wolfsentry_sockaddr *)&jps->o_u_c.route.remote ?
+ WOLFSENTRY_ROUTE_FLAG_REMOTE_ADDR_BITMASK :
+ WOLFSENTRY_ROUTE_FLAG_LOCAL_ADDR_BITMASK))
+ prefix_given = 0;
+#endif
WOLFSENTRY_CLEAR_BITS(jps->o_u_c.route.flags,
sa == (struct wolfsentry_sockaddr *)&jps->o_u_c.route.remote ?
WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD :
WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD);
- WOLFSENTRY_ERROR_RERETURN(convert_sockaddr_address(jps, type, data, data_size, sa, sa->addr));
+ ret = convert_sockaddr_address(jps, type, data, data_size, sa, sa->addr);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ /* an earlier prefix-bits on generic LINK was allowed the EUI-64 width;
+ * now that the address has settled the family, the prefix must fit it. */
+ if (prefix_given && (sa->sa_family == WOLFSENTRY_AF_LINK48)) {
+ ret = wolfsentry_addr_family_max_addr_bits(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, sa->sa_family, &max_addr_bits);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ if (prior_addr_len > max_addr_bits)
+ WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG);
+ }
+ WOLFSENTRY_RETURN_OK;
}
#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
else if (! strcmp(jps->cur_keyname, "bitmask")) {
@@ -852,6 +1028,14 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_
if (sa->sa_family == WOLFSENTRY_AF_UNSPEC)
WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE);
+ /* the right-justify below would keep only the prefix-sized leading bytes of a full address */
+ if ((jps->section_under_construction == S_U_C_REMOTE_ENDPOINT) ?
+ jps->o_u_c.route.remote_prefix_bits_given :
+ jps->o_u_c.route.local_prefix_bits_given)
+ {
+ WOLFSENTRY_ERROR_RETURN(CONFIG_MISPLACED_KEY);
+ }
+
ret = wolfsentry_addr_family_max_addr_bits(
JPS_WOLFSENTRY_CONTEXT_ARGS_OUT,
sa->sa_family,
@@ -868,8 +1052,14 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_
* move it to right-justified position.
*/
if ((sa->addr_len != 0) && (sa->addr_len != addr_size)) {
- size_t pad_bytes = WOLFSENTRY_BITS_TO_BYTES((size_t)addr_size - (size_t)sa->addr_len);
- memmove(sa->addr + pad_bytes, sa->addr, sa->addr_len);
+ size_t addr_bytes;
+ size_t pad_bytes;
+ if (sa->addr_len > addr_size)
+ WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG);
+ /* addr_len is a bit count; the move operates on whole bytes. */
+ addr_bytes = WOLFSENTRY_BITS_TO_BYTES((size_t)sa->addr_len);
+ pad_bytes = WOLFSENTRY_BITS_TO_BYTES((size_t)addr_size) - addr_bytes;
+ memmove(sa->addr + pad_bytes, sa->addr, addr_bytes);
memset(sa->addr, 0, pad_bytes);
}
@@ -886,6 +1076,10 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_
}
#endif
else if (! strcmp(jps->cur_keyname, "prefix-bits")) {
+ wolfsentry_addr_bits_t max_addr_bits;
+ wolfsentry_addr_bits_t prior_addr_len = sa->addr_len;
+ wolfsentry_addr_family_t width_family = sa->sa_family;
+ wolfsentry_errcode_t ret;
if (sa->sa_family == WOLFSENTRY_AF_UNSPEC)
WOLFSENTRY_ERROR_RETURN(CONFIG_OUT_OF_SEQUENCE);
#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
@@ -898,7 +1092,40 @@ static wolfsentry_errcode_t handle_route_endpoint_clause(struct wolfsentry_json_
WOLFSENTRY_ERROR_RETURN(CONFIG_MISPLACED_KEY);
}
#endif
- WOLFSENTRY_ERROR_RERETURN(convert_uint16(type, data, data_size, &sa->addr_len));
+ ret = convert_uint16(type, data, data_size, &sa->addr_len);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ /* bound prefix-bits to the address family width so a later copy cannot
+ * read past the fixed-size staging buffer. AF_LOCAL path addresses have
+ * no fixed family width (and no entry in the family table), so bound
+ * them by the staging buffer, matching the formatter's dump output.
+ */
+ if ((sa->sa_family == WOLFSENTRY_AF_LOCAL) && af_local_is_builtin(jps)) {
+ max_addr_bits = (wolfsentry_addr_bits_t)((WOLFSENTRY_MAX_ADDR_BITS / 8) * 8);
+ /* an AF_LOCAL prefix-bits only denotes the pathname length, so it
+ * must be nonzero whole bytes and match any pathname already parsed;
+ * keeping it nonzero leaves addr_len == 0 meaning "not supplied".
+ */
+ if ((sa->addr_len == 0) || (sa->addr_len & 0x7))
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ if ((prior_addr_len != 0) && (sa->addr_len != prior_addr_len))
+ WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
+ }
+ else {
+ /* generic LINK becomes EUI-48 or EUI-64 from the address length, so
+ * before an address arrives allow the wider width; the address
+ * handler then checks the prefix against the settled family. */
+ if ((width_family == WOLFSENTRY_AF_LINK48) && (prior_addr_len == 0))
+ width_family = WOLFSENTRY_AF_LINK64;
+ ret = wolfsentry_addr_family_max_addr_bits(JPS_WOLFSENTRY_CONTEXT_ARGS_OUT, width_family, &max_addr_bits);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ }
+ if (sa->addr_len > max_addr_bits)
+ WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG);
+ if (jps->section_under_construction == S_U_C_REMOTE_ENDPOINT)
+ jps->o_u_c.route.remote_prefix_bits_given = 1;
+ else
+ jps->o_u_c.route.local_prefix_bits_given = 1;
+ WOLFSENTRY_RETURN_OK;
}
else if (! strcmp(jps->cur_keyname, "interface")) {
WOLFSENTRY_CLEAR_BITS(jps->o_u_c.route.flags,
@@ -981,6 +1208,10 @@ static wolfsentry_errcode_t handle_route_protocol_clause(struct wolfsentry_json_
WOLFSENTRY_ERROR_RETURN(ITEM_NOT_FOUND);
}
+ /* glibc reports an unknown protocol with a zero return and a NULL result. */
+ if (p == NULL)
+ WOLFSENTRY_ERROR_RETURN(ITEM_NOT_FOUND);
+
jps->o_u_c.route.remote.sa_proto = (wolfsentry_proto_t)p->p_proto;
jps->o_u_c.route.local.sa_proto = (wolfsentry_proto_t)p->p_proto;
}
@@ -988,7 +1219,9 @@ static wolfsentry_errcode_t handle_route_protocol_clause(struct wolfsentry_json_
else
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_VALUE);
- if (((jps->o_u_c.route.remote.sa_family == WOLFSENTRY_AF_INET) || (jps->o_u_c.route.remote.sa_family == WOLFSENTRY_AF_INET6)) &&
+ /* implicit default only; an explicit tcplike-port-numbers setting wins. */
+ if ((! jps->o_u_c.route.tcplike_port_numbers_explicit) &&
+ ((jps->o_u_c.route.remote.sa_family == WOLFSENTRY_AF_INET) || (jps->o_u_c.route.remote.sa_family == WOLFSENTRY_AF_INET6)) &&
((jps->o_u_c.route.remote.sa_proto == IPPROTO_TCP) || (jps->o_u_c.route.remote.sa_proto == IPPROTO_UDP)))
WOLFSENTRY_SET_BITS(jps->o_u_c.route.flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS);
@@ -1071,8 +1304,12 @@ static wolfsentry_errcode_t handle_route_clause(struct wolfsentry_json_process_s
else {
wolfsentry_route_flags_t flag;
ret = wolfsentry_route_flag_assoc_by_name(jps->cur_keyname, WOLFSENTRY_LENGTH_NULL_TERMINATED, &flag);
- if (ret >= 0)
- WOLFSENTRY_ERROR_RERETURN(handle_route_boolean_clause(type, &jps->o_u_c.route.flags, flag));
+ if (ret >= 0) {
+ ret = handle_route_boolean_clause(type, &jps->o_u_c.route.flags, flag);
+ if ((ret >= 0) && (flag == WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS))
+ jps->o_u_c.route.tcplike_port_numbers_explicit = 1;
+ WOLFSENTRY_ERROR_RERETURN(ret);
+ }
else {
if (WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND))
WOLFSENTRY_ERROR_RETURN(CONFIG_INVALID_KEY);
@@ -1100,7 +1337,7 @@ static wolfsentry_errcode_t handle_event_clause(struct wolfsentry_json_process_s
}
if ((jps->cur_depth == 3) && (type == JSON_STRING) && (! strcmp(jps->cur_keyname, "label"))) {
- if (data_size >= sizeof jps->o_u_c.event.label)
+ if (data_size > sizeof jps->o_u_c.event.label)
WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
memcpy(jps->o_u_c.event.label, data, data_size);
jps->o_u_c.event.label_len = (int)data_size;
@@ -1632,7 +1869,7 @@ static wolfsentry_errcode_t json_process(
WOLFSENTRY_RETURN_OK;
}
if (! strcmp(jps->cur_keyname, "actions-update")) {
- WOLFSENTRY_ERROR_RETURN(CONFIG_MISSING_HANDLER);
+ WOLFSENTRY_ERROR_OUT(CONFIG_MISSING_HANDLER);
/*
jps->table_under_construction = T_U_C_ACTIONS;
WOLFSENTRY_RETURN_OK;
@@ -1735,7 +1972,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex(
#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
.max_total_len = WOLFSENTRY_MAX_JSON_TOTAL_LEN,
.max_total_values = 0,
- .max_number_len = 20,
+ .max_number_len = 24,
.max_string_len = WOLFSENTRY_KV_MAX_VALUE_BYTES,
.max_key_len = WOLFSENTRY_MAX_LABEL_BYTES,
.max_nesting_level = WOLFSENTRY_MAX_JSON_NESTING,
@@ -1743,7 +1980,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_init_ex(
#else
WOLFSENTRY_MAX_JSON_TOTAL_LEN,
0,
- 20,
+ 24,
WOLFSENTRY_KV_MAX_VALUE_BYTES,
WOLFSENTRY_MAX_LABEL_BYTES,
WOLFSENTRY_MAX_JSON_NESTING,
@@ -2008,13 +2245,21 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_fini(
if (full_cycle_of_insert_actions_p) {
wolfsentry_action_res_t action_results = WOLFSENTRY_ACTION_RES_NONE;
- if ((ret = wolfsentry_route_bulk_clear_insert_action_status(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, &action_results)) < 0)
- goto out;
- if ((ret = wolfsentry_context_flush(JPSP_WOLFSENTRY_CONTEXT_ARGS_OUT)) < 0)
+ ret = wolfsentry_route_bulk_clear_insert_action_status(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, &action_results);
+ if (ret >= 0)
+ ret = wolfsentry_context_flush(JPSP_WOLFSENTRY_CONTEXT_ARGS_OUT);
+ /* the candidate is already live after the exchange; re-enable its
+ * actions even if the preceding steps failed, so a post-exchange
+ * failure cannot leave the running configuration action-inhibited.
+ * keep the first error.
+ */
+ if (ret >= 0)
+ ret = wolfsentry_context_enable_actions(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT);
+ else
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_enable_actions(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT));
+ if (ret < 0)
goto out;
- }
-
- if ((ret = wolfsentry_context_enable_actions(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT)) < 0)
+ } else if ((ret = wolfsentry_context_enable_actions(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT)) < 0)
goto out;
{
@@ -2036,6 +2281,26 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_config_json_fini(
if ((*jps)->wolfsentry && ((*jps)->wolfsentry != (*jps)->wolfsentry_actual))
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_free(JPSP_P_WOLFSENTRY_CONTEXT_ARGS_OUT));
+ /* In the default (non-transactional) load mode the live context was flushed
+ * before parsing; a failure leaves it partially populated while its default
+ * policy is unchanged. Fail closed so a half-loaded policy cannot admit
+ * traffic: clear the routes and set a REJECT default policy.
+ */
+ if ((ret < 0) &&
+ (! WOLFSENTRY_MASKIN_BITS((*jps)->load_flags,
+ WOLFSENTRY_CONFIG_LOAD_FLAG_DRY_RUN |
+ WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT |
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH)))
+ {
+ struct wolfsentry_route_table *main_table;
+ wolfsentry_action_res_t action_results;
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ if (wolfsentry_route_get_main_table(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, &main_table) >= 0) {
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_route_flush_table(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, main_table, &action_results));
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_route_table_default_policy_set(JPSP_WOLFSENTRY_ACTUAL_CONTEXT_ARGS_OUT, main_table, WOLFSENTRY_ACTION_RES_REJECT));
+ }
+ }
+
#ifdef WOLFSENTRY_THREADSAFE
{
wolfsentry_errcode_t _lock_ret;
diff --git a/src/kv.c b/src/kv.c
index d921ad5d..c453f2fe 100644
--- a/src/kv.c
+++ b/src/kv.c
@@ -26,6 +26,9 @@
#include
#endif
+#include
+#include
+
#define WOLFSENTRY_SOURCE_ID WOLFSENTRY_SOURCE_ID_KV_C
static inline int wolfsentry_kv_key_cmp_1(const char *left_label, const unsigned int left_label_len, const char *right_label, const unsigned int right_label_len) {
@@ -60,7 +63,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_new(
struct wolfsentry_kv_pair_internal **kv)
{
size_t kv_size;
- if (key_len == 0)
+ if (key == NULL)
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
if (data_len < 0)
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
@@ -68,6 +71,8 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_new(
WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG);
if (key_len < 0)
key_len = (int)strlen(key);
+ if (key_len <= 0)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
if (key_len > WOLFSENTRY_MAX_LABEL_BYTES)
WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
kv_size = sizeof **kv + (size_t)key_len + 1 + (size_t)data_len;
@@ -170,9 +175,14 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_insert(
struct wolfsentry_kv_pair_internal *kv)
{
wolfsentry_errcode_t ret;
+ wolfsentry_kv_validator_t validator = WOLFSENTRY_ATOMIC_LOAD(kv_table->validator);
- if (kv_table->validator) {
- ret = kv_table->validator(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->kv);
+ /* read the validator pointer atomically (paired with the atomic store in
+ * the setter) but call it without the context lock held, to avoid a
+ * lock-order inversion with locks the application callback may take.
+ */
+ if (validator) {
+ ret = validator(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->kv);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
}
@@ -224,9 +234,14 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_set(
{
struct wolfsentry_kv_pair_internal *old;
wolfsentry_errcode_t ret;
+ wolfsentry_kv_validator_t validator = WOLFSENTRY_ATOMIC_LOAD(kv_table->validator);
- if (kv_table->validator) {
- ret = kv_table->validator(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->kv);
+ /* read the validator pointer atomically (paired with the atomic store in
+ * the setter) but call it without the context lock held, to avoid a
+ * lock-order inversion with locks the application callback may take.
+ */
+ if (validator) {
+ ret = validator(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->kv);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
}
@@ -242,11 +257,23 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_set(
WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
WOLFSENTRY_UNLOCK_AND_RETURN_OK;
}
- if (((ret = wolfsentry_table_ent_delete_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &old->header)) < 0) ||
- ((ret = wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, old, NULL)) < 0))
- {
+ /* reserve the replacement ID first, so an ID allocation failure leaves the old entry in place. */
+ if ((ret = wolfsentry_id_allocate(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->header)) < 0)
+ WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
+ if ((ret = wolfsentry_table_ent_delete_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &old->header)) < 0) {
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_table_ent_delete_by_id_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->header));
+ WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
+ }
+ if ((ret = wolfsentry_table_ent_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->header, &kv_table->header, 1 /* unique_p */)) < 0) {
+ /* the old ID was released by the delete, so restoring needs a fresh one; drop the old entry if that fails. */
+ if (kv->header.id != WOLFSENTRY_ENT_ID_NONE)
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_table_ent_delete_by_id_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv->header));
+ if (wolfsentry_kv_insert_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, old) < 0)
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, old, NULL));
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, old, NULL));
+ WOLFSENTRY_UNLOCK_AND_RETURN_OK;
}
ret = wolfsentry_kv_insert_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv);
@@ -257,6 +284,7 @@ static wolfsentry_errcode_t wolfsentry_kv_get_1(
WOLFSENTRY_CONTEXT_ARGS_IN,
struct wolfsentry_kv_table *kv_table,
const struct wolfsentry_kv_pair_internal *kv_template,
+ int take_reference,
struct wolfsentry_kv_pair_internal **kv)
{
wolfsentry_errcode_t ret;
@@ -272,6 +300,13 @@ static wolfsentry_errcode_t wolfsentry_kv_get_1(
else if ((WOLFSENTRY_KV_TYPE(&kv_template->kv) != WOLFSENTRY_KV_NONE) &&
(WOLFSENTRY_KV_TYPE(&kv_template->kv) != WOLFSENTRY_KV_TYPE(&ret_kv->kv)))
WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(WRONG_TYPE);
+ /* take the reference while the shared lock is still held, so a concurrent
+ * delete cannot free the entry between lookup and increment.
+ */
+ if (take_reference) {
+ WOLFSENTRY_REFCOUNT_INCREMENT(ret_kv->header.refcount, ret);
+ WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
+ }
*kv = ret_kv;
WOLFSENTRY_UNLOCK_AND_RETURN_OK;
}
@@ -289,7 +324,7 @@ static wolfsentry_errcode_t wolfsentry_kv_get_2(
if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, 0 /* data_len */, &kv_template)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
kv_template->kv.v_type = type;
- ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, kv);
+ ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, 1 /* take_reference */, kv);
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_template, NULL));
WOLFSENTRY_ERROR_RERETURN(ret);
}
@@ -300,11 +335,7 @@ static wolfsentry_errcode_t wolfsentry_kv_get_reference_1(
const struct wolfsentry_kv_pair_internal *kv_template,
struct wolfsentry_kv_pair_internal **kv)
{
- wolfsentry_errcode_t ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, kv);
- WOLFSENTRY_RERETURN_IF_ERROR(ret);
- WOLFSENTRY_REFCOUNT_INCREMENT((*kv)->header.refcount, ret);
- WOLFSENTRY_RERETURN_IF_ERROR(ret);
- WOLFSENTRY_RETURN_OK;
+ WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, 1 /* take_reference */, kv));
}
WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_get_reference(
@@ -318,7 +349,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_get_reference(
struct wolfsentry_kv_pair_internal *kv_template;
wolfsentry_errcode_t ret;
if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, 0 /* data_len */, &kv_template)) < 0)
- WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
+ WOLFSENTRY_ERROR_RERETURN(ret);
kv_template->kv.v_type = type;
ret = wolfsentry_kv_get_reference_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, kv);
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_template, NULL));
@@ -338,11 +369,11 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_get_type(
struct wolfsentry_kv_pair_internal *kv = NULL;
if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, 0 /* data_len */, &kv_template)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
- ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, &kv);
+ ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, 1 /* take_reference */, &kv);
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_template, NULL));
WOLFSENTRY_RERETURN_IF_ERROR(ret);
*type = WOLFSENTRY_KV_TYPE(&kv->kv);
- WOLFSENTRY_RETURN_OK;
+ WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL));
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_kv_type_to_string(
@@ -402,6 +433,111 @@ static int _json_value_dump_writer(const unsigned char* str, size_t size, void *
}
#endif
+#ifndef WOLFSENTRY_HAVE_JSON_DOM
+
+#define WOLFSENTRY_KV_EMIT_CHAR(c) do { if (needed < out_space) out[needed] = (char)(c); ++needed; } while (0)
+
+/* escape a KV string into a JSON string literal, mirroring json_dump_string in
+ * the DOM path, and return the snprintf-style length (excluding the null).
+ */
+static int wolfsentry_kv_render_string_escaped(
+ const char *in,
+ size_t in_len,
+ char *out,
+ size_t out_space)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ size_t in_i;
+ size_t needed = 0;
+ unsigned char c;
+
+ WOLFSENTRY_KV_EMIT_CHAR('"');
+ for (in_i = 0; in_i < in_len; ++in_i) {
+ c = (unsigned char)in[in_i];
+ switch (c) {
+ case '"':
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ WOLFSENTRY_KV_EMIT_CHAR('"');
+ break;
+ case '\\':
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ break;
+ case '\n':
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ WOLFSENTRY_KV_EMIT_CHAR('n');
+ break;
+ case '\r':
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ WOLFSENTRY_KV_EMIT_CHAR('r');
+ break;
+ case '\t':
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ WOLFSENTRY_KV_EMIT_CHAR('t');
+ break;
+ case '\b':
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ WOLFSENTRY_KV_EMIT_CHAR('b');
+ break;
+ case '\f':
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ WOLFSENTRY_KV_EMIT_CHAR('f');
+ break;
+ default:
+ if (c < 0x20) {
+ WOLFSENTRY_KV_EMIT_CHAR('\\');
+ WOLFSENTRY_KV_EMIT_CHAR('u');
+ WOLFSENTRY_KV_EMIT_CHAR('0');
+ WOLFSENTRY_KV_EMIT_CHAR('0');
+ WOLFSENTRY_KV_EMIT_CHAR(hexdigits[(c >> 4) & 0xf]);
+ WOLFSENTRY_KV_EMIT_CHAR(hexdigits[c & 0xf]);
+ } else {
+ WOLFSENTRY_KV_EMIT_CHAR(c);
+ }
+ break;
+ }
+ }
+ WOLFSENTRY_KV_EMIT_CHAR('"');
+
+ if (out_space > 0) {
+ if (needed < out_space)
+ out[needed] = 0;
+ else
+ out[out_space - 1] = 0;
+ }
+
+ return (int)needed;
+}
+
+#undef WOLFSENTRY_KV_EMIT_CHAR
+
+#endif /* !WOLFSENTRY_HAVE_JSON_DOM */
+
+/* snprintf() semantics; C89 has no portable 64-bit conversion specifier. */
+static int wolfsentry_kv_render_uint64(uint64_t magnitude, int negative, char *out, size_t out_space)
+{
+ char digits[20];
+ size_t n_digits = 0;
+ size_t needed;
+ size_t pos = 0;
+
+ do {
+ digits[n_digits++] = (char)('0' + (int)(magnitude % 10U));
+ magnitude /= 10U;
+ } while (magnitude != 0);
+ needed = n_digits + (negative ? 1U : 0U);
+
+ if (out_space > 0) {
+ if (negative && (pos < out_space - 1))
+ out[pos++] = '-';
+ while ((n_digits > 0) && (pos < out_space - 1))
+ out[pos++] = digits[--n_digits];
+ out[pos] = 0;
+ }
+
+ return (int)needed;
+}
+
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_kv_render_value(
WOLFSENTRY_CONTEXT_ARGS_IN,
const struct wolfsentry_kv_pair *kv,
@@ -425,17 +561,36 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_kv_render_value(
*out_len = snprintf(out, out_space, "false");
break;
case WOLFSENTRY_KV_UINT:
- *out_len = snprintf(out, out_space, "%lu", (long unsigned int)WOLFSENTRY_KV_V_UINT(kv));
+ *out_len = wolfsentry_kv_render_uint64(WOLFSENTRY_KV_V_UINT(kv), 0, out, out_space);
break;
- case WOLFSENTRY_KV_SINT:
- *out_len = snprintf(out, out_space, "%ld", (long int)WOLFSENTRY_KV_V_SINT(kv));
+ case WOLFSENTRY_KV_SINT: {
+ int64_t v_sint = WOLFSENTRY_KV_V_SINT(kv);
+ if (v_sint < 0)
+ *out_len = wolfsentry_kv_render_uint64((uint64_t)(-(v_sint + 1)) + 1U, 1, out, out_space);
+ else
+ *out_len = wolfsentry_kv_render_uint64((uint64_t)v_sint, 0, out, out_space);
break;
- case WOLFSENTRY_KV_FLOAT:
- *out_len = snprintf(out, out_space, "%.10f", WOLFSENTRY_KV_V_FLOAT(kv));
+ }
+ case WOLFSENTRY_KV_FLOAT: {
+ double v_float = WOLFSENTRY_KV_V_FLOAT(kv);
+ /* non-finite values have no bare JSON token, so emit a quoted string
+ * that the loader's strtod path re-reads to the same value. finiteness
+ * is tested with DBL_MAX comparisons to stay legal under C89 (no C99
+ * isnan()/isinf()).
+ */
+ if (!(v_float <= DBL_MAX) && !(v_float >= -DBL_MAX)) /* NaN */
+ *out_len = snprintf(out, out_space, "\"nan\"");
+ else if (!(v_float <= DBL_MAX)) /* +Inf */
+ *out_len = snprintf(out, out_space, "\"inf\"");
+ else if (!(v_float >= -DBL_MAX)) /* -Inf */
+ *out_len = snprintf(out, out_space, "\"-inf\"");
+ else
+ *out_len = snprintf(out, out_space, "%.17g", v_float);
break;
+ }
case WOLFSENTRY_KV_STRING: {
#ifndef WOLFSENTRY_HAVE_JSON_DOM
- *out_len = snprintf(out, out_space, "\"%.*s\"", (int)WOLFSENTRY_KV_V_STRING_LEN(kv), WOLFSENTRY_KV_V_STRING(kv));
+ *out_len = wolfsentry_kv_render_string_escaped(WOLFSENTRY_KV_V_STRING(kv), WOLFSENTRY_KV_V_STRING_LEN(kv), out, out_space);
break;
#else
struct dump_buf_state dbs;
@@ -545,7 +700,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_delete(
if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, 0 /* data_len */, &kv_template)) < 0)
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
- ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, &old);
+ ret = wolfsentry_kv_get_1(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_table, kv_template, 0 /* take_reference */, &old);
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_template, NULL));
WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
if (old->kv.v_type & WOLFSENTRY_KV_FLAG_READONLY)
@@ -591,7 +746,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_kv_set_validator(
ret = wolfsentry_table_map(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_table->header, (wolfsentry_map_function_t)apply_validator, (void *)&context, action_results);
WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
}
- wolfsentry->user_values->validator = validator;
+ WOLFSENTRY_ATOMIC_STORE(kv_table->validator, validator);
WOLFSENTRY_UNLOCK_AND_RETURN_OK;
}
@@ -713,7 +868,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_set_mutability(
struct wolfsentry_kv_pair_internal *kv = NULL;
if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_NONE, &kv)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
- WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_set_mutability(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, kv, mutable));
+ ret = wolfsentry_kv_set_mutability(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, kv, mutable);
+ if (ret < 0)
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL));
+ else
+ ret = wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL);
+ WOLFSENTRY_ERROR_RERETURN(ret);
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_mutability(
@@ -727,7 +887,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_mutability(
if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_NONE, &kv)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
*mutable = wolfsentry_kv_get_mutability(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, kv);
- WOLFSENTRY_RETURN_OK;
+ WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL));
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_type(
@@ -805,10 +965,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_bool(
if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_NONE, &kv)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
if ((WOLFSENTRY_KV_TYPE(&kv->kv) != WOLFSENTRY_KV_TRUE) &&
- (WOLFSENTRY_KV_TYPE(&kv->kv) != WOLFSENTRY_KV_FALSE))
+ (WOLFSENTRY_KV_TYPE(&kv->kv) != WOLFSENTRY_KV_FALSE)) {
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL));
WOLFSENTRY_ERROR_RETURN(WRONG_TYPE);
+ }
*value = WOLFSENTRY_KV_TYPE(&kv->kv);
- WOLFSENTRY_RETURN_OK;
+ WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL));
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_uint(
@@ -845,7 +1007,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_uint(
if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_UINT, &kv)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
*value = WOLFSENTRY_KV_V_UINT(&kv->kv);
- WOLFSENTRY_RETURN_OK;
+ WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL));
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_sint(
@@ -883,7 +1045,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_sint(
if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_SINT, &kv)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
*value = WOLFSENTRY_KV_V_SINT(&kv->kv);
- WOLFSENTRY_RETURN_OK;
+ WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL));
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_double(
@@ -921,7 +1083,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_float(
if ((ret = wolfsentry_kv_get_2(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->user_values, key, key_len, WOLFSENTRY_KV_FLOAT, &kv)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
*value = WOLFSENTRY_KV_V_FLOAT(&kv->kv);
- WOLFSENTRY_RETURN_OK;
+ WOLFSENTRY_ERROR_RERETURN(wolfsentry_kv_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, kv, NULL));
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_string(
@@ -934,8 +1096,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_string(
{
wolfsentry_errcode_t ret;
struct wolfsentry_kv_pair_internal *kv;
- if (value_len < 0)
- value_len = (int)strlen(value);
+ if (value_len < 0) {
+ size_t value_strlen = strlen(value);
+ if (value_strlen > (size_t)WOLFSENTRY_KV_MAX_VALUE_BYTES)
+ WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
+ value_len = (int)value_strlen;
+ }
if (value_len > WOLFSENTRY_KV_MAX_VALUE_BYTES)
WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
if ((ret = wolfsentry_kv_new(WOLFSENTRY_CONTEXT_ARGS_OUT, key, key_len, value_len + 1, &kv)) < 0)
@@ -1008,8 +1174,14 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_bytes_base64(
wolfsentry_errcode_t ret;
int decoded_len;
struct wolfsentry_kv_pair_internal *kv;
- if (value_len < 0)
- value_len = (int)strlen(value);
+ /* the decoded size computation adds 3 to the length */
+ if (value_len < 0) {
+ size_t value_strlen = strlen(value);
+ if (value_strlen > (size_t)(INT_MAX - 3))
+ WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
+ value_len = (int)value_strlen;
+ } else if (value_len > INT_MAX - 3)
+ WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
decoded_len = WOLFSENTRY_BASE64_DECODED_BUFSPC(value, value_len);
if (decoded_len > WOLFSENTRY_KV_MAX_VALUE_BYTES)
WOLFSENTRY_ERROR_RETURN(STRING_ARG_TOO_LONG);
diff --git a/src/lwip/packet_filter_glue.c b/src/lwip/packet_filter_glue.c
index f8d3b603..a7cca6d1 100644
--- a/src/lwip/packet_filter_glue.c
+++ b/src/lwip/packet_filter_glue.c
@@ -105,7 +105,7 @@ static err_t ethernet_filter_with_wolfsentry(
#endif
if (wolfsentry == NULL)
- WOLFSENTRY_RETURN_VALUE(ERR_OK);
+ WOLFSENTRY_RETURN_VALUE(ERR_ABRT);
switch(event->reason) {
case FILT_RECEIVING:
@@ -186,7 +186,10 @@ static err_t ethernet_filter_with_wolfsentry(
WOLFSENTRY_WARN_ON_FAILURE(ws_ret);
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
+ /* fail closed: a failed dispatch leaves no trustworthy verdict. */
+ if (WOLFSENTRY_IS_FAILURE(ws_ret))
+ ret = ERR_ABRT;
+ else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
ret = ERR_ABRT;
else
ret = ERR_OK;
@@ -242,7 +245,7 @@ static err_t ip4_filter_with_wolfsentry(
#endif
if (wolfsentry == NULL)
- WOLFSENTRY_RETURN_VALUE(ERR_OK);
+ WOLFSENTRY_RETURN_VALUE(ERR_ABRT);
switch(event->reason) {
case FILT_RECEIVING:
@@ -326,7 +329,10 @@ static err_t ip4_filter_with_wolfsentry(
WOLFSENTRY_WARN_ON_FAILURE(ws_ret);
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
+ /* fail closed: a failed dispatch leaves no trustworthy verdict. */
+ if (WOLFSENTRY_IS_FAILURE(ws_ret))
+ ret = ERR_ABRT;
+ else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
ret = ERR_ABRT;
else
ret = ERR_OK;
@@ -377,7 +383,7 @@ static err_t ip6_filter_with_wolfsentry(
#endif
if (wolfsentry == NULL)
- WOLFSENTRY_RETURN_VALUE(ERR_OK);
+ WOLFSENTRY_RETURN_VALUE(ERR_ABRT);
switch(event->reason) {
case FILT_RECEIVING:
@@ -461,7 +467,10 @@ static err_t ip6_filter_with_wolfsentry(
WOLFSENTRY_WARN_ON_FAILURE(ws_ret);
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
+ /* fail closed: a failed dispatch leaves no trustworthy verdict. */
+ if (WOLFSENTRY_IS_FAILURE(ws_ret))
+ ret = ERR_ABRT;
+ else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
ret = ERR_ABRT;
else
ret = ERR_OK;
@@ -515,7 +524,7 @@ static err_t tcp_filter_with_wolfsentry(
#endif
if (wolfsentry == NULL)
- WOLFSENTRY_RETURN_VALUE(ERR_OK);
+ WOLFSENTRY_RETURN_VALUE(ERR_ABRT);
switch(event->reason) {
case FILT_ACCEPTING:
@@ -540,6 +549,7 @@ static err_t tcp_filter_with_wolfsentry(
route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
else
route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT;
+ action_results = WOLFSENTRY_ACTION_RES_CLOSE_WAIT;
break;
case FILT_PORT_UNREACHABLE:
route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
@@ -662,7 +672,10 @@ static err_t tcp_filter_with_wolfsentry(
WOLFSENTRY_WARN_ON_FAILURE(ws_ret);
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET))
+ /* fail closed: a failed dispatch leaves no trustworthy verdict. */
+ if (WOLFSENTRY_IS_FAILURE(ws_ret))
+ ret = ERR_ABRT;
+ else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET))
ret = ERR_RST;
else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
ret = ERR_ABRT;
@@ -727,7 +740,7 @@ static err_t udp_filter_with_wolfsentry(
#endif
if (wolfsentry == NULL)
- WOLFSENTRY_RETURN_VALUE(ERR_OK);
+ WOLFSENTRY_RETURN_VALUE(ERR_ABRT);
switch(event->reason) {
case FILT_BINDING:
@@ -851,7 +864,10 @@ static err_t udp_filter_with_wolfsentry(
WOLFSENTRY_WARN_ON_FAILURE(ws_ret);
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET))
+ /* fail closed: a failed dispatch leaves no trustworthy verdict. */
+ if (WOLFSENTRY_IS_FAILURE(ws_ret))
+ ret = ERR_ABRT;
+ else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET))
ret = ERR_RST;
else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
ret = ERR_ABRT;
@@ -914,7 +930,7 @@ static err_t icmp4_filter_with_wolfsentry(
#endif
if (wolfsentry == NULL)
- WOLFSENTRY_RETURN_VALUE(ERR_OK);
+ WOLFSENTRY_RETURN_VALUE(ERR_ABRT);
switch(event->reason) {
case FILT_RECEIVING:
@@ -999,7 +1015,10 @@ static err_t icmp4_filter_with_wolfsentry(
WOLFSENTRY_WARN_ON_FAILURE(ws_ret);
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
+ /* fail closed: a failed dispatch leaves no trustworthy verdict. */
+ if (WOLFSENTRY_IS_FAILURE(ws_ret))
+ ret = ERR_ABRT;
+ else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
ret = ERR_ABRT;
else
ret = ERR_OK;
@@ -1050,7 +1069,7 @@ static err_t icmp6_filter_with_wolfsentry(
#endif
if (wolfsentry == NULL)
- WOLFSENTRY_RETURN_VALUE(ERR_OK);
+ WOLFSENTRY_RETURN_VALUE(ERR_ABRT);
switch(event->reason) {
case FILT_RECEIVING:
@@ -1135,7 +1154,10 @@ static err_t icmp6_filter_with_wolfsentry(
WOLFSENTRY_WARN_ON_FAILURE(ws_ret);
- if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
+ /* fail closed: a failed dispatch leaves no trustworthy verdict. */
+ if (WOLFSENTRY_IS_FAILURE(ws_ret))
+ ret = ERR_ABRT;
+ else if (WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT))
ret = ERR_ABRT;
else
ret = ERR_OK;
@@ -1148,8 +1170,118 @@ static err_t icmp6_filter_with_wolfsentry(
#endif /* LWIP_ICMP6 */
+#define LWIP_FILTER_SLOT_ETHERNET (1U << 0)
+#define LWIP_FILTER_SLOT_IP (1U << 1)
+#define LWIP_FILTER_SLOT_ICMP (1U << 2)
+#define LWIP_FILTER_SLOT_TCP (1U << 3)
+#define LWIP_FILTER_SLOT_UDP (1U << 4)
+
+/* lwIP has no getters for its filter slots, so the owning context is tracked
+ * here. Contexts hold only their own mutex, so ownership changes atomically
+ * and only the owner touches the slots and the lwIP setters. */
+static struct wolfsentry_context *lwip_filter_owner = NULL;
+static unsigned int lwip_filter_slots = 0;
+
+static wolfsentry_errcode_t lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_IN, unsigned int slot)
+{
+ wolfsentry_errcode_t ret;
+#ifdef WOLFSENTRY_THREADSAFE
+ struct wolfsentry_context *expected = NULL;
+
+ if ((! WOLFSENTRY_ATOMIC_CMPXCHG(&lwip_filter_owner, &expected, wolfsentry, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)) &&
+ (expected != wolfsentry))
+ {
+ WOLFSENTRY_ERROR_RETURN(INCOMPATIBLE_STATE);
+ }
+#else
+ if ((lwip_filter_owner != NULL) && (lwip_filter_owner != wolfsentry))
+ WOLFSENTRY_ERROR_RETURN(INCOMPATIBLE_STATE);
+ lwip_filter_owner = wolfsentry;
+#endif
+ ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL);
+ if (ret < 0) {
+ if (lwip_filter_slots == 0)
+ WOLFSENTRY_ATOMIC_STORE(lwip_filter_owner, NULL);
+ WOLFSENTRY_ERROR_RERETURN(ret);
+ }
+ lwip_filter_slots |= slot;
+ WOLFSENTRY_RETURN_OK;
+}
+
+static void lwip_filter_clear_slot(unsigned int slot)
+{
+ switch (slot) {
+ case LWIP_FILTER_SLOT_ETHERNET:
+#if LWIP_ARP || LWIP_ETHERNET
+ ethernet_filter(NULL);
+ ethernet_filter_mask(0);
+ ethernet_filter_arg(NULL);
+#endif
+ break;
+ case LWIP_FILTER_SLOT_IP:
+#if LWIP_IPV4
+ ip4_filter(NULL);
+ ip4_filter_mask(0);
+ ip4_filter_arg(NULL);
+#endif
+#if LWIP_IPV6
+ ip6_filter(NULL);
+ ip6_filter_mask(0);
+ ip6_filter_arg(NULL);
+#endif
+ break;
+ case LWIP_FILTER_SLOT_ICMP:
+#if LWIP_ICMP
+ icmp_filter(NULL);
+ icmp_filter_mask(0);
+ icmp_filter_arg(NULL);
+#endif
+#if LWIP_ICMP6
+ icmp6_filter(NULL);
+ icmp6_filter_mask(0);
+ icmp6_filter_arg(NULL);
+#endif
+ break;
+ case LWIP_FILTER_SLOT_TCP:
+#if LWIP_TCP
+ tcp_filter(NULL);
+ tcp_filter_mask(0);
+ tcp_filter_arg(NULL);
+#endif
+ break;
+ case LWIP_FILTER_SLOT_UDP:
+#if LWIP_UDP
+ udp_filter(NULL);
+ udp_filter_mask(0);
+ udp_filter_arg(NULL);
+#endif
+ break;
+ }
+}
+
+static wolfsentry_errcode_t lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_IN, unsigned int slot)
+{
+ struct wolfsentry_context *owner = WOLFSENTRY_ATOMIC_LOAD(lwip_filter_owner);
+
+ WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
+ /* with no owner, no wolfSentry callbacks are installed, so there is nothing to clear. */
+ if (owner == NULL)
+ WOLFSENTRY_RETURN_OK;
+ if (owner != wolfsentry)
+ WOLFSENTRY_ERROR_RETURN(INCOMPATIBLE_STATE);
+ lwip_filter_clear_slot(slot);
+ lwip_filter_slots &= ~slot;
+ /* ownership is released only after the last slot is fully cleared. */
+ if (lwip_filter_slots == 0)
+ WOLFSENTRY_ATOMIC_STORE(lwip_filter_owner, NULL);
+ WOLFSENTRY_RETURN_OK;
+}
+
WOLFSENTRY_API_VOID wolfsentry_cleanup_lwip_filter_callbacks(WOLFSENTRY_CONTEXT_ARGS_IN, void *cleanup_arg) {
(void)cleanup_arg;
+ /* a destroyed non-owner must not disable the surviving owner's filters. */
+ if (WOLFSENTRY_ATOMIC_LOAD(lwip_filter_owner) != wolfsentry)
+ return;
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_install_lwip_filter_callbacks(WOLFSENTRY_CONTEXT_ARGS_OUT, 0, 0, 0, 0, 0));
}
@@ -1158,17 +1290,18 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_ethernet_call
packet_filter_event_mask_t ethernet_mask)
{
#if LWIP_ARP || LWIP_ETHERNET
+ wolfsentry_errcode_t ret;
+
WOLFSENTRY_MUTEX_OR_RETURN();
if (ethernet_mask) {
- wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL);
+ ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_ETHERNET);
WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
- ethernet_filter(ethernet_filter_with_wolfsentry);
- ethernet_filter_mask(ethernet_mask);
ethernet_filter_arg((void *)wolfsentry);
+ ethernet_filter_mask(ethernet_mask);
+ ethernet_filter(ethernet_filter_with_wolfsentry);
} else {
- ethernet_filter(NULL);
- ethernet_filter_mask(0);
- ethernet_filter_arg(NULL);
+ ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_ETHERNET);
+ WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
}
WOLFSENTRY_UNLOCK_AND_RETURN_OK;
#else
@@ -1184,33 +1317,28 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_ip_callbacks(
packet_filter_event_mask_t ip_mask)
{
#if LWIP_IPV4 || LWIP_IPV6
+ wolfsentry_errcode_t ret;
+
WOLFSENTRY_MUTEX_OR_RETURN();
- if (ip_mask) {
- wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL);
- WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
- }
+ if (ip_mask)
+ ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_IP);
+ else
+ ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_IP);
+ WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
#endif
#if LWIP_IPV4
if (ip_mask) {
- ip4_filter(ip4_filter_with_wolfsentry);
- ip4_filter_mask(ip_mask);
ip4_filter_arg((void *)wolfsentry);
- } else {
- ip4_filter(NULL);
- ip4_filter_mask(0);
- ip4_filter_arg(NULL);
+ ip4_filter_mask(ip_mask);
+ ip4_filter(ip4_filter_with_wolfsentry);
}
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
if (ip_mask) {
- ip6_filter(ip6_filter_with_wolfsentry);
- ip6_filter_mask(ip_mask);
ip6_filter_arg((void *)wolfsentry);
- } else {
- ip6_filter(NULL);
- ip6_filter_mask(0);
- ip6_filter_arg(NULL);
+ ip6_filter_mask(ip_mask);
+ ip6_filter(ip6_filter_with_wolfsentry);
}
#endif /* LWIP_IPV6 */
@@ -1229,30 +1357,28 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_icmp_callback
packet_filter_event_mask_t icmp_mask)
{
#if LWIP_ICMP || LWIP_ICMP6
+ wolfsentry_errcode_t ret;
+
WOLFSENTRY_MUTEX_OR_RETURN();
- if (icmp_mask) {
- wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL);
- WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
- }
+ if (icmp_mask)
+ ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_ICMP);
+ else
+ ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_ICMP);
+ WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
#endif
#if LWIP_ICMP
if (icmp_mask) {
- icmp_filter(icmp4_filter_with_wolfsentry);
- icmp_filter_mask(icmp_mask);
icmp_filter_arg((void *)wolfsentry);
- } else
- icmp_filter(NULL);
+ icmp_filter_mask(icmp_mask);
+ icmp_filter(icmp4_filter_with_wolfsentry);
+ }
#endif /* LWIP_ICMP */
#if LWIP_ICMP6
if (icmp_mask) {
- icmp6_filter(icmp6_filter_with_wolfsentry);
- icmp6_filter_mask(icmp_mask);
icmp6_filter_arg((void *)wolfsentry);
- } else {
- icmp6_filter(NULL);
- icmp6_filter_mask(0);
- icmp6_filter_arg(NULL);
+ icmp6_filter_mask(icmp_mask);
+ icmp6_filter(icmp6_filter_with_wolfsentry);
}
#endif /* LWIP_ICMP6 */
@@ -1271,22 +1397,23 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_tcp_callback(
packet_filter_event_mask_t tcp_mask)
{
#if LWIP_TCP
+ wolfsentry_errcode_t ret;
+
WOLFSENTRY_MUTEX_OR_RETURN();
if (tcp_mask) {
- wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL);
+ ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_TCP);
WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
- tcp_filter(tcp_filter_with_wolfsentry);
/* make sure wolfSentry sees the close/reset events that balance earlier
* accepts, for concurrent-connection accounting purposes.
*/
if (tcp_mask & (FILT_MASK(ACCEPTING) | FILT_MASK(CLOSED) | FILT_MASK(REMOTE_RESET)))
tcp_mask |= FILT_MASK(ACCEPTING) | FILT_MASK(CLOSED) | FILT_MASK(REMOTE_RESET);
- tcp_filter_mask(tcp_mask);
tcp_filter_arg((void *)wolfsentry);
+ tcp_filter_mask(tcp_mask);
+ tcp_filter(tcp_filter_with_wolfsentry);
} else {
- tcp_filter(NULL);
- tcp_filter_mask(0);
- tcp_filter_arg(NULL);
+ ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_TCP);
+ WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
}
WOLFSENTRY_UNLOCK_AND_RETURN_OK;
#else
@@ -1302,17 +1429,18 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_lwip_filter_udp_callback(
packet_filter_event_mask_t udp_mask)
{
#if LWIP_UDP
+ wolfsentry_errcode_t ret;
+
WOLFSENTRY_MUTEX_OR_RETURN();
if (udp_mask) {
- wolfsentry_errcode_t ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_lwip_filter_callbacks, NULL);
+ ret = lwip_filter_claim_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_UDP);
WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
- udp_filter(udp_filter_with_wolfsentry);
- udp_filter_mask(udp_mask);
udp_filter_arg((void *)wolfsentry);
+ udp_filter_mask(udp_mask);
+ udp_filter(udp_filter_with_wolfsentry);
} else {
- udp_filter(NULL);
- udp_filter_mask(0);
- udp_filter_arg(NULL);
+ ret = lwip_filter_release_slot(WOLFSENTRY_CONTEXT_ARGS_OUT, LWIP_FILTER_SLOT_UDP);
+ WOLFSENTRY_UNLOCK_AND_RERETURN_IF_ERROR(ret);
}
WOLFSENTRY_UNLOCK_AND_RETURN_OK;
#else
diff --git a/src/routes.c b/src/routes.c
index c9a13acb..9ccaffed 100644
--- a/src/routes.c
+++ b/src/routes.c
@@ -248,17 +248,16 @@ static inline int cmp_addrs_bitmaskful(
if (left_addr_len < right_addr_len) {
maskful_addr = right_addr;
- mask_len = right_addr_len >> 1;
+ mask_len = WOLFSENTRY_BITS_TO_BYTES(right_addr_len >> 1);
target_addr = left_addr;
- padding_offset = mask_len - left_addr_len;
+ padding_offset = mask_len - WOLFSENTRY_BITS_TO_BYTES(left_addr_len);
} else {
maskful_addr = left_addr;
- mask_len = left_addr_len >> 1;
+ mask_len = WOLFSENTRY_BITS_TO_BYTES(left_addr_len >> 1);
target_addr = right_addr;
- padding_offset = mask_len - right_addr_len;
+ padding_offset = mask_len - WOLFSENTRY_BITS_TO_BYTES(right_addr_len);
}
- mask_len = WOLFSENTRY_BITS_TO_BYTES(mask_len);
mask = maskful_addr + mask_len;
for (i = 0; i < mask_len; ++i) {
@@ -296,28 +295,21 @@ static inline int addr_bitmask_match_size(
const byte *b,
unsigned int b_len)
{
- const byte *longer_addr, *shorter_addr;
- unsigned int mask_len, i, padding_offset;
+ const byte *mask;
+ unsigned int mask_len, i;
int ret = 0;
+ /* specificity is the number of bits the longer, maskful address's mask constrains */
if (a_len < b_len) {
- longer_addr = b;
mask_len = WOLFSENTRY_BITS_TO_BYTES(b_len >> 1);
- shorter_addr = a;
- padding_offset = mask_len - a_len;
+ mask = b + mask_len;
} else {
- longer_addr = a;
mask_len = WOLFSENTRY_BITS_TO_BYTES(a_len >> 1);
- shorter_addr = b;
- padding_offset = mask_len - b_len;
+ mask = a + mask_len;
}
- for (ret = 0, i = 0; i < mask_len; ++i) {
- if (i < padding_offset)
- ret += popcount32(~(uint32_t)(longer_addr[i])) - 24;
- else
- ret += popcount32(~(uint32_t)(longer_addr[i] ^ shorter_addr[i - padding_offset])) - 24;
- }
+ for (i = 0; i < mask_len; ++i)
+ ret += popcount32((uint32_t)mask[i]);
return ret;
}
@@ -1004,6 +996,10 @@ static wolfsentry_errcode_t wolfsentry_route_new(
} else {
if (parent_event != NULL) {
WOLFSENTRY_REFCOUNT_INCREMENT(parent_event->header.refcount, ret);
+ if (ret < 0) {
+ wolfsentry_route_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT, config->config.route_private_data_alignment, *new);
+ *new = NULL;
+ }
}
}
@@ -1063,6 +1059,10 @@ static wolfsentry_errcode_t wolfsentry_route_new_by_exports(
} else {
if (parent_event != NULL) {
WOLFSENTRY_REFCOUNT_INCREMENT(parent_event->header.refcount, ret);
+ if (ret < 0) {
+ wolfsentry_route_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT, config->config.route_private_data_alignment, *new);
+ *new = NULL;
+ }
}
}
@@ -1165,8 +1165,9 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_route_clone(
/* now zero the private data section. */
memset((*new_route)->data, 0, config->config.route_private_data_size);
- /* finally, copy the address section, carefully. */
- memcpy((byte *)(*new_route)->data + (*new_route)->data_addr_offset, src_route->data, src_route->data_addr_size);
+ /* finally, copy the address section, carefully, reading it from the
+ * source's own address offset rather than the start of its data. */
+ memcpy((byte *)(*new_route)->data + (*new_route)->data_addr_offset, (const byte *)src_route->data + src_route->data_addr_offset, src_route->data_addr_size);
}
WOLFSENTRY_TABLE_ENT_HEADER_RESET(**new_ent);
@@ -1179,6 +1180,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_route_clone(
thread,
#endif
config->config.route_private_data_alignment, *new_route);
+ *new_route = NULL;
WOLFSENTRY_ERROR_RERETURN(ret);
}
WOLFSENTRY_REFCOUNT_INCREMENT((*new_route)->parent_event->header.refcount, ret);
@@ -1320,13 +1322,28 @@ static wolfsentry_errcode_t wolfsentry_route_insert_1(
wolfsentry_route_flags_t flags_before, flags_after;
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_table_ent_delete_1(WOLFSENTRY_CONTEXT_ARGS_OUT, &route_to_insert->header));
wolfsentry_route_update_flags_1(route_to_insert, WOLFSENTRY_ROUTE_FLAG_NONE, WOLFSENTRY_ROUTE_FLAG_IN_TABLE, &flags_before, &flags_after);
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+ if (route_to_insert->flags & (WOLFSENTRY_ROUTE_FLAG_REMOTE_ADDR_BITMASK | WOLFSENTRY_ROUTE_FLAG_LOCAL_ADDR_BITMASK))
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_bitmask_matching_downref(route_to_insert->sa_family, route_table));
+#endif
+ WOLFSENTRY_CLEAR_BITS(*action_results, WOLFSENTRY_ACTION_RES_INSERTED);
+ /* the caller drops the last reference on failure, so the route
+ * must not be linked into the purge list or wildcard bookkeeping.
+ */
+ WOLFSENTRY_ERROR_RERETURN(ret);
}
- } else {
- if (route_to_insert->parent_event) {
- if (! WOLFSENTRY_CHECK_BITS(route_to_insert->parent_event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT))
- WOLFSENTRY_SET_BITS(route_to_insert->parent_event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT);
- }
+ } else
ret = WOLFSENTRY_ERROR_ENCODE(OK);
+
+ /* an aux event parents the ephemeral routes it generates, so it keeps its
+ * subevent role rather than taking on the mutually exclusive parent role.
+ * the owner count is authoritative, since a freed owner leaves the stored mark stale.
+ */
+ if ((route_to_insert->parent_event != NULL) &&
+ (WOLFSENTRY_ATOMIC_LOAD(route_to_insert->parent_event->aux_owner_count) == 0) &&
+ (! WOLFSENTRY_CHECK_BITS(route_to_insert->parent_event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT)))
+ {
+ WOLFSENTRY_SET_BITS(route_to_insert->parent_event->flags, WOLFSENTRY_EVENT_FLAG_IS_PARENT_EVENT);
}
if (route_to_insert->meta.purge_after)
@@ -1472,6 +1489,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_insert_into_table(
wolfsentry_errcode_t ret;
struct wolfsentry_event *event = NULL;
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+
WOLFSENTRY_MUTEX_OR_RETURN();
if (event_label) {
@@ -1496,6 +1516,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_insert_by_exports_into_tabl
wolfsentry_errcode_t ret;
struct wolfsentry_event *event = NULL;
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+
WOLFSENTRY_MUTEX_OR_RETURN();
if (route_exports->parent_event_label) {
@@ -1566,6 +1589,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_insert_into_table_and_check
wolfsentry_errcode_t ret;
struct wolfsentry_event *event = NULL;
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+
WOLFSENTRY_MUTEX_OR_RETURN();
if (event_label) {
@@ -1647,6 +1673,7 @@ static wolfsentry_errcode_t wolfsentry_route_lookup_0(
int contiguous_search;
wolfsentry_route_flags_t inexact_matches_buf;
int seen_target_af;
+ int af_wildcard_span_entered;
#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
int af_is_bitmask_matched;
#endif
@@ -1740,9 +1767,16 @@ static wolfsentry_errcode_t wolfsentry_route_lookup_0(
*/
if (cursor_position == 0) {
struct wolfsentry_event *parent_event = ((struct wolfsentry_route *)cursor.point)->parent_event ? ((struct wolfsentry_route *)cursor.point)->parent_event : NULL;
- if ((action_results == NULL) || (parent_event == NULL) || (parent_event->config == NULL) ||
- (((*action_results & parent_event->config->config.action_res_filter_bits_set) == parent_event->config->config.action_res_filter_bits_set) &&
- ((~(*action_results) & parent_event->config->config.action_res_filter_bits_unset) == parent_event->config->config.action_res_filter_bits_unset)))
+ /* PENALTYBOXED is mutable and not part of the key, so an exact hit
+ * must still honor the bind/listen exclusion applied by the full scan.
+ */
+ int excluded_reject_route = (action_results != NULL) &&
+ WOLFSENTRY_CHECK_BITS(*action_results, WOLFSENTRY_ACTION_RES_EXCLUDE_REJECT_ROUTES) &&
+ WOLFSENTRY_MASKIN_BITS(((struct wolfsentry_route *)cursor.point)->flags, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED);
+ if ((! excluded_reject_route) &&
+ ((action_results == NULL) || (parent_event == NULL) || (parent_event->config == NULL) ||
+ (((*action_results & parent_event->config->config.action_res_filter_bits_set) == parent_event->config->config.action_res_filter_bits_set) &&
+ ((~(*action_results) & parent_event->config->config.action_res_filter_bits_unset) == parent_event->config->config.action_res_filter_bits_unset))))
{
int effective_priority = parent_event ? parent_event->priority : 0;
if (effective_priority <= table->highest_priority_route_in_table) {
@@ -1803,6 +1837,7 @@ static wolfsentry_errcode_t wolfsentry_route_lookup_0(
#endif
seen_target_af = 0;
+ af_wildcard_span_entered = 0;
for (i = (struct wolfsentry_route *)wolfsentry_table_cursor_current(&cursor);
i;
@@ -1812,9 +1847,15 @@ static wolfsentry_errcode_t wolfsentry_route_lookup_0(
if (! seen_target_af)
seen_target_af = 1;
} else {
+ /* jump to the wildcard-AF span at most once; the span's own
+ * members never match the target AF and would otherwise re-trigger
+ * the jump forever.
+ */
if (seen_target_af &&
+ (! af_wildcard_span_entered) &&
(! (target_route->flags & WOLFSENTRY_ROUTE_FLAG_SA_FAMILY_WILDCARD)))
{
+ af_wildcard_span_entered = 1;
if (table->last_af_wildcard_route) {
i = table->last_af_wildcard_route;
wolfsentry_table_cursor_set(&cursor, &i->header);
@@ -1996,6 +2037,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_table_default_policy_set(
if ((default_policy != WOLFSENTRY_ACTION_RES_NONE) &&
(! WOLFSENTRY_MASKIN_BITS(default_policy, WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_REJECT)))
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ if (WOLFSENTRY_CHECK_BITS(default_policy, WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_REJECT))
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ /* a port reset only qualifies a reject verdict. */
+ if (WOLFSENTRY_MASKIN_BITS(default_policy, WOLFSENTRY_ACTION_RES_PORT_RESET) &&
+ (! WOLFSENTRY_MASKIN_BITS(default_policy, WOLFSENTRY_ACTION_RES_REJECT)))
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
WOLFSENTRY_MUTEX_OR_RETURN();
table->default_policy = default_policy;
if (table == wolfsentry->routes)
@@ -2280,6 +2327,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_delete_from_table(
wolfsentry_errcode_t ret;
struct wolfsentry_event *event = NULL;
+ if ((action_results == NULL) || (n_deleted == NULL))
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+
WOLFSENTRY_MUTEX_OR_RETURN();
if (event_label) {
@@ -2332,6 +2382,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_delete_by_id(
struct wolfsentry_event *trigger_event = NULL;
struct wolfsentry_route *route;
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+
WOLFSENTRY_MUTEX_OR_RETURN();
if (trigger_label) {
@@ -2380,6 +2433,7 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
struct wolfsentry_eventconfig_internal *config;
wolfsentry_route_flags_t current_rule_route_flags;
wolfsentry_errcode_t ret;
+ wolfsentry_errcode_t action_ret;
wolfsentry_time_t now;
int penalty_triggered = 0;
wolfsentry_hitcount_t derog_snap;
@@ -2431,10 +2485,11 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
#endif
{
rule_route->meta.purge_after = new_purge_after; /* coherent assignment */
- if (route_table->purge_list.tail != &rule_route->purge_links) {
- wolfsentry_list_ent_delete(&route_table->purge_list, &rule_route->purge_links);
- wolfsentry_route_purge_list_insert(route_table, rule_route);
- }
+ /* the refreshed route may now be the newest rather than the
+ * stalest, so always reposition it instead of leaving a
+ * just-refreshed tail stranded at the stale end. */
+ wolfsentry_list_ent_delete(&route_table->purge_list, &rule_route->purge_links);
+ wolfsentry_route_purge_list_insert(route_table, rule_route);
}
}
}
@@ -2447,7 +2502,7 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
* trigger_event->action_list must call wolfsentry_route_set_wildcard(),
* in addition to setting _ACTION_RES_INSERT.
*/
- WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_list_dispatch(
+ action_ret = wolfsentry_action_list_dispatch(
WOLFSENTRY_CONTEXT_ARGS_OUT,
caller_arg,
trigger_event,
@@ -2456,7 +2511,10 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
target_route,
route_table,
rule_route,
- action_results));
+ action_results);
+ WOLFSENTRY_WARN_ON_FAILURE(action_ret);
+ if (action_ret < 0)
+ *action_results |= WOLFSENTRY_ACTION_RES_ERROR;
WOLFSENTRY_CLEAR_BITS(*action_results, WOLFSENTRY_ACTION_RES_STOP);
current_rule_route_flags = WOLFSENTRY_ATOMIC_LOAD(rule_route->flags);
}
@@ -2474,10 +2532,15 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
wolfsentry_route_increment_hitcount(WOLFSENTRY_CONTEXT_ARGS_OUT, rule_route, action_results);
}
parent_event = route_table->default_event;
+ /* the rule route and its event changed, so refresh the cached config
+ * and flags to match the fallthrough route rather than reusing the
+ * superseded route's state. */
+ config = (parent_event && parent_event->config) ? parent_event->config : &wolfsentry->config;
+ current_rule_route_flags = WOLFSENTRY_ATOMIC_LOAD(rule_route->flags);
}
if (parent_event && (wolfsentry_list_ent_get_len(&parent_event->match_action_list.header) > 0)) {
- WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_list_dispatch(
+ action_ret = wolfsentry_action_list_dispatch(
WOLFSENTRY_CONTEXT_ARGS_OUT,
caller_arg,
parent_event,
@@ -2486,7 +2549,10 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
target_route,
route_table,
rule_route,
- action_results));
+ action_results);
+ WOLFSENTRY_WARN_ON_FAILURE(action_ret);
+ if (action_ret < 0)
+ *action_results |= WOLFSENTRY_ACTION_RES_ERROR;
WOLFSENTRY_CLEAR_BITS(*action_results, WOLFSENTRY_ACTION_RES_STOP);
current_rule_route_flags = WOLFSENTRY_ATOMIC_LOAD(rule_route->flags);
}
@@ -2505,29 +2571,40 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
(void)ret;
}
- if ((! (current_rule_route_flags & WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS)) &&
- (config->config.max_connection_count > 0))
- {
+ if (! (current_rule_route_flags & WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS)) {
if (*action_results & WOLFSENTRY_ACTION_RES_CONNECT) {
- if (rule_route->meta.connection_count >= config->config.max_connection_count) {
+ uint16_t new_connection_count;
+ /* count every connection so disconnects stay balanced when the
+ * limit changes; a zero limit only means nothing is rejected. */
+ if ((config->config.max_connection_count > 0) &&
+ (rule_route->meta.connection_count >= config->config.max_connection_count))
+ {
*action_results |= WOLFSENTRY_ACTION_RES_REJECT;
ret = WOLFSENTRY_ERROR_ENCODE(OK);
goto done;
}
- if (WOLFSENTRY_ATOMIC_INCREMENT_BY_ONE(rule_route->meta.connection_count) > config->config.max_connection_count) {
- WOLFSENTRY_ATOMIC_DECREMENT_BY_ONE(rule_route->meta.connection_count);
+ WOLFSENTRY_ATOMIC_INCREMENT_UNSIGNED_SAFELY_BY_ONE(rule_route->meta.connection_count, new_connection_count);
+ /* with no limit a saturated counter simply stops counting; under a
+ * limit it cannot track another connection, so refuse it. */
+ if ((config->config.max_connection_count > 0) &&
+ ((new_connection_count == 0) || (new_connection_count > config->config.max_connection_count)))
+ {
+ if (new_connection_count != 0)
+ WOLFSENTRY_ATOMIC_DECREMENT_BY_ONE(rule_route->meta.connection_count);
*action_results |= WOLFSENTRY_ACTION_RES_REJECT;
ret = WOLFSENTRY_ERROR_ENCODE(OK);
goto done;
}
} else if (*action_results & WOLFSENTRY_ACTION_RES_DISCONNECT) {
uint16_t new_connection_count;
- /* important to decrement safely, because untimely route
- * deletions can lead to a decrement being assigned to a
- * different route than the one originally incremented.
- */
+ /* decrement safely: an untimely route deletion can assign a
+ * decrement to a different route than the one incremented. */
WOLFSENTRY_ATOMIC_DECREMENT_UNSIGNED_SAFELY(rule_route->meta.connection_count, 1, new_connection_count);
- if (new_connection_count == MAX_UINT_OF(rule_route->meta.connection_count))
+ /* only an already-zero count under an enforced limit is
+ * anomalous; with no limit, unconnected routes legitimately see
+ * disconnects, so do not warn on them. */
+ if ((new_connection_count == MAX_UINT_OF(rule_route->meta.connection_count)) &&
+ (config->config.max_connection_count > 0))
WOLFSENTRY_WARN("_RES_DISCONNECT for route #" WOLFSENTRY_ENT_ID_FMT ", whose connection_count is already zero.", rule_route->header.id);
}
}
@@ -2638,7 +2715,7 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
parent_event && (wolfsentry_list_ent_get_len(&parent_event->update_action_list.header) > 0))
{
WOLFSENTRY_CLEAR_BITS(*action_results, WOLFSENTRY_ACTION_RES_STOP);
- WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_list_dispatch(
+ action_ret = wolfsentry_action_list_dispatch(
WOLFSENTRY_CONTEXT_ARGS_OUT,
caller_arg,
parent_event,
@@ -2647,13 +2724,16 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
target_route,
route_table,
rule_route,
- action_results));
+ action_results);
+ WOLFSENTRY_WARN_ON_FAILURE(action_ret);
+ if (action_ret < 0)
+ *action_results |= WOLFSENTRY_ACTION_RES_ERROR;
/* no need to refresh current_rule_route_flags */
}
if (parent_event && (wolfsentry_list_ent_get_len(&parent_event->decision_action_list.header) > 0)) {
WOLFSENTRY_CLEAR_BITS(*action_results, WOLFSENTRY_ACTION_RES_STOP);
- WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_action_list_dispatch(
+ action_ret = wolfsentry_action_list_dispatch(
WOLFSENTRY_CONTEXT_ARGS_OUT,
caller_arg,
parent_event,
@@ -2662,7 +2742,10 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_0(
target_route,
route_table,
rule_route,
- action_results));
+ action_results);
+ WOLFSENTRY_WARN_ON_FAILURE(action_ret);
+ if (action_ret < 0)
+ *action_results |= WOLFSENTRY_ACTION_RES_ERROR;
/* no need to refresh current_rule_route_flags */
}
@@ -2695,6 +2778,8 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_1(
struct wolfsentry_route *rule_route = NULL;
struct wolfsentry_event *trigger_event = NULL;
wolfsentry_errcode_t ret;
+ int apply_default_policy = 0;
+ static int warned_unset_default_policy = 0;
if ((remote == NULL) ||
(local == NULL) ||
@@ -2767,6 +2852,7 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_1(
else {
/* nothing to do. */
/* carry through ret from final wolfsentry_route_lookup_0(), and impose the default policy. */
+ apply_default_policy = 1;
goto just_free_resources;
}
@@ -2806,8 +2892,29 @@ static wolfsentry_errcode_t wolfsentry_route_event_dispatch_1(
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, trigger_event, NULL /* action_results */));
if (rule_route == NULL) {
+ if (! apply_default_policy) {
+ /* a resource failure (not a clean no-match) prevented rule
+ * evaluation; fail closed with an explicit reject rather than
+ * silently applying the default policy as a successful fallthrough,
+ * so an integrator that gates on _RES_REJECT drops the packet.
+ */
+ *action_results |= WOLFSENTRY_ACTION_RES_ERROR | WOLFSENTRY_ACTION_RES_REJECT;
+ WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
+ }
if (inexact_matches)
*inexact_matches = WOLFSENTRY_ROUTE_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD;
+ if (route_table->default_policy == WOLFSENTRY_ACTION_RES_NONE) {
+ /* claim the one-shot atomically so only a single caller warns. */
+ int warn_expected = 0;
+#ifdef WOLFSENTRY_THREADSAFE
+ if (WOLFSENTRY_ATOMIC_CMPXCHG(&warned_unset_default_policy, &warn_expected, 1, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
+#else
+ warn_expected = warned_unset_default_policy;
+ warned_unset_default_policy = 1;
+ if (! warn_expected)
+#endif
+ WOLFSENTRY_WARN("%s", "default route policy is unset (NONE); unmatched traffic falls through with no accept or reject verdict.\n");
+ }
*action_results |= route_table->default_policy | WOLFSENTRY_ACTION_RES_FALLTHROUGH;
WOLFSENTRY_SUCCESS_UNLOCK_AND_RETURN(USED_FALLBACK);
}
@@ -2832,6 +2939,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_with_table(
wolfsentry_action_res_t *action_results
)
{
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
WOLFSENTRY_CLEAR_ALL_BITS(*action_results);
WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_event_dispatch_1(WOLFSENTRY_CONTEXT_ARGS_OUT, route_table, remote, local, flags, event_label, event_label_len, caller_arg, id, inexact_matches, action_results));
}
@@ -2849,6 +2958,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch(
wolfsentry_action_res_t *action_results
)
{
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
WOLFSENTRY_CLEAR_ALL_BITS(*action_results);
WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_event_dispatch_1(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, remote, local, flags, event_label, event_label_len, caller_arg, id, inexact_matches, action_results));
}
@@ -2857,6 +2968,8 @@ static wolfsentry_errcode_t check_user_inited_result(wolfsentry_action_res_t act
if (WOLFSENTRY_MASKIN_BITS(action_results,
WOLFSENTRY_ACTION_RES_ACCEPT |
WOLFSENTRY_ACTION_RES_REJECT |
+ WOLFSENTRY_ACTION_RES_FALLTHROUGH |
+ WOLFSENTRY_ACTION_RES_INSERTED |
WOLFSENTRY_ACTION_RES_STOP |
WOLFSENTRY_ACTION_RES_DEALLOCATED |
WOLFSENTRY_ACTION_RES_ERROR))
@@ -2879,7 +2992,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_with_table_w
wolfsentry_action_res_t *action_results
)
{
- wolfsentry_errcode_t ret = check_user_inited_result(*action_results);
+ wolfsentry_errcode_t ret;
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ ret = check_user_inited_result(*action_results);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_event_dispatch_1(WOLFSENTRY_CONTEXT_ARGS_OUT, route_table, remote, local, flags, event_label, event_label_len, caller_arg, id, inexact_matches, action_results));
}
@@ -2897,7 +3013,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_with_inited_
wolfsentry_action_res_t *action_results
)
{
- wolfsentry_errcode_t ret = check_user_inited_result(*action_results);
+ wolfsentry_errcode_t ret;
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ ret = check_user_inited_result(*action_results);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_event_dispatch_1(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, remote, local, flags, event_label, event_label_len, caller_arg, id, inexact_matches, action_results));
}
@@ -2953,6 +3072,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_by_id(
wolfsentry_action_res_t *action_results
)
{
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
WOLFSENTRY_CLEAR_ALL_BITS(*action_results);
WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_event_dispatch_by_id_1(WOLFSENTRY_CONTEXT_ARGS_OUT, id, event_label, event_label_len, caller_arg, action_results));
}
@@ -2966,7 +3087,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_by_id_with_i
wolfsentry_action_res_t *action_results
)
{
- wolfsentry_errcode_t ret = check_user_inited_result(*action_results);
+ wolfsentry_errcode_t ret;
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ ret = check_user_inited_result(*action_results);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_event_dispatch_by_id_1(WOLFSENTRY_CONTEXT_ARGS_OUT, id, event_label, event_label_len, caller_arg, action_results));
}
@@ -3019,6 +3143,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_by_route(
wolfsentry_action_res_t *action_results
)
{
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
WOLFSENTRY_CLEAR_ALL_BITS(*action_results);
WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_event_dispatch_by_route_1(WOLFSENTRY_CONTEXT_ARGS_OUT, route, event_label, event_label_len, caller_arg, action_results));
}
@@ -3032,7 +3158,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_by_route_wit
wolfsentry_action_res_t *action_results
)
{
- wolfsentry_errcode_t ret = check_user_inited_result(*action_results);
+ wolfsentry_errcode_t ret;
+ if (action_results == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ ret = check_user_inited_result(*action_results);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_event_dispatch_by_route_1(WOLFSENTRY_CONTEXT_ARGS_OUT, route, event_label, event_label_len, caller_arg, action_results));
}
@@ -3042,6 +3171,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_by_route_wit
WOLFSENTRY_LOCAL_VOID wolfsentry_route_purge_list_insert(struct wolfsentry_route_table *route_table, struct wolfsentry_route *route_to_insert) {
struct wolfsentry_list_ent_header *point_ent;
+ /* deletion leaves an entry's own links stale, and a head insert keeps prev */
+ route_to_insert->purge_links.prev = NULL;
+ route_to_insert->purge_links.next = NULL;
for (wolfsentry_list_ent_get_first(&route_table->purge_list, &point_ent); point_ent; point_ent = point_ent->next) {
const struct wolfsentry_route *point_route = WOLFSENTRY_ROUTE_PURGE_HEADER_TO_TABLE_ENT_HEADER(point_ent);
if (point_route->meta.purge_after < route_to_insert->meta.purge_after)
@@ -3057,6 +3189,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_purge_time_set(
wolfsentry_time_t purge_after)
{
WOLFSENTRY_HAVE_MUTEX_OR_RETURN();
+ if (route->header.parent_table == NULL)
+ WOLFSENTRY_ERROR_RETURN(NOT_PERMITTED);
if (route->meta.purge_after)
wolfsentry_list_ent_delete(&((struct wolfsentry_route_table *)route->header.parent_table)->purge_list, &route->purge_links);
route->meta.purge_after = purge_after;
@@ -3075,6 +3209,9 @@ static wolfsentry_errcode_t wolfsentry_route_stale_purge_1(
wolfsentry_errcode_t ret;
int n = 0;
wolfsentry_action_res_t fallback_action_results = 0;
+ struct wolfsentry_list_ent_header *cursor;
+ int examined = 0;
+ wolfsentry_time_t requeue_delay = 0;
#ifdef WOLFSENTRY_THREADSAFE
int have_mutex = 0;
int got_lock = 0;
@@ -3092,6 +3229,11 @@ static wolfsentry_errcode_t wolfsentry_route_stale_purge_1(
WOLFSENTRY_ERROR_RETURN(ALREADY);
}
+ if (mode == 2) {
+ if ((ret = WOLFSENTRY_INTERVAL_FROM_SECONDS(WOLFSENTRY_ROUTE_PURGE_MARGIN_SECONDS, 0 /* howlong_nsecs */, &requeue_delay)) < 0)
+ WOLFSENTRY_ERROR_RERETURN(ret);
+ }
+
if (action_results == NULL)
action_results = &fallback_action_results;
@@ -3121,17 +3263,39 @@ static wolfsentry_errcode_t wolfsentry_route_stale_purge_1(
table = wolfsentry->routes;
while (table->purge_list.tail) {
- struct wolfsentry_route *route = WOLFSENTRY_ROUTE_PURGE_HEADER_TO_TABLE_ENT_HEADER(table->purge_list.tail);
- if (mode != 3) {
- if (route->meta.purge_after > now)
- break;
- if ((route->meta.connection_count > 0) &&
- (! (route->flags & WOLFSENTRY_ROUTE_FLAG_PENDING_DELETE)) &&
- ((table->max_purgeable_idle_time == 0) || (now - route->meta.last_hit_time > table->max_purgeable_idle_time)))
- {
+ struct wolfsentry_route *route = NULL;
+ int found = 0;
+ int requeue = 0;
+ wolfsentry_time_t idle = 0;
+ /* re-scan from the most stale entry each pass, because a delete runs
+ * application callbacks that can mutate the purge list; skip routes
+ * that still have a live connection within the idle limit so one
+ * protected route does not strand the expired routes behind it. */
+ for (cursor = table->purge_list.tail; cursor != NULL; cursor = cursor->prev) {
+ /* the per-dispatch purge runs on the packet path, so bound its scan */
+ if ((mode == 2) && (++examined > 8))
break;
+ route = WOLFSENTRY_ROUTE_PURGE_HEADER_TO_TABLE_ENT_HEADER(cursor);
+ if (mode != 3) {
+ if (route->meta.purge_after > now)
+ break;
+ /* a route never hit since insertion has been idle since its insertion */
+ idle = WOLFSENTRY_DIFF_TIME(now, (route->meta.last_hit_time > route->meta.insert_time) ? route->meta.last_hit_time : route->meta.insert_time);
+ if ((route->meta.connection_count > 0) &&
+ (! (route->flags & WOLFSENTRY_ROUTE_FLAG_PENDING_DELETE)) &&
+ ((table->max_purgeable_idle_time <= 0) || (idle <= table->max_purgeable_idle_time)))
+ {
+ if (mode != 2)
+ continue;
+ /* requeue it so later bounded scans reach the routes behind it */
+ requeue = 1;
+ }
}
+ found = 1;
+ break;
}
+ if (! found)
+ break;
#ifdef WOLFSENTRY_THREADSAFE
if (! have_mutex) {
if (mode == 2)
@@ -3146,6 +3310,19 @@ static wolfsentry_errcode_t wolfsentry_route_stale_purge_1(
have_mutex = 1;
}
#endif
+ if (requeue) {
+ wolfsentry_time_t delay = requeue_delay;
+ /* not before the margin, unless the idle limit expires sooner */
+ if ((table->max_purgeable_idle_time > 0) && (idle >= 0) &&
+ (table->max_purgeable_idle_time - idle < requeue_delay))
+ {
+ delay = table->max_purgeable_idle_time - idle + 1;
+ }
+ wolfsentry_list_ent_delete(&table->purge_list, &route->purge_links);
+ route->meta.purge_after = WOLFSENTRY_ADD_TIME(now, delay);
+ wolfsentry_route_purge_list_insert(table, route);
+ continue;
+ }
ret = wolfsentry_route_delete_0(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, table, NULL /* trigger_event */, route, action_results, 0 /* defer_p */);
if (ret < 0) {
#ifdef WOLFSENTRY_THREADSAFE
@@ -3395,10 +3572,13 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_increment_derogatory_count(
WOLFSENTRY_ATOMIC_INCREMENT_UNSIGNED_SAFELY(route->meta.derogatory_count, (unsigned)count_to_add, new_derogatory_count);
if (new_derogatory_count == 0)
WOLFSENTRY_ERROR_RETURN(OVERFLOW_AVERTED);
- } else {
- WOLFSENTRY_ATOMIC_DECREMENT_UNSIGNED_SAFELY(route->meta.derogatory_count, (unsigned)(-count_to_add), new_derogatory_count);
+ } else if (count_to_add < 0) {
+ /* negate in the unsigned domain so INT_MIN does not overflow. */
+ WOLFSENTRY_ATOMIC_DECREMENT_UNSIGNED_SAFELY(route->meta.derogatory_count, 0U - (unsigned)count_to_add, new_derogatory_count);
if (new_derogatory_count == MAX_UINT_OF(route->meta.derogatory_count))
WOLFSENTRY_ERROR_RETURN(OVERFLOW_AVERTED);
+ } else {
+ new_derogatory_count = WOLFSENTRY_ATOMIC_LOAD(route->meta.derogatory_count);
}
if (new_derogatory_count_ptr)
@@ -3421,10 +3601,13 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_increment_commendable_count
WOLFSENTRY_ATOMIC_INCREMENT_UNSIGNED_SAFELY(route->meta.commendable_count, (unsigned)count_to_add, new_commendable_count);
if (new_commendable_count == 0)
WOLFSENTRY_ERROR_RETURN(OVERFLOW_AVERTED);
- } else {
- WOLFSENTRY_ATOMIC_DECREMENT_UNSIGNED_SAFELY(route->meta.commendable_count, (unsigned)(-count_to_add), new_commendable_count);
+ } else if (count_to_add < 0) {
+ /* negate in the unsigned domain so INT_MIN does not overflow. */
+ WOLFSENTRY_ATOMIC_DECREMENT_UNSIGNED_SAFELY(route->meta.commendable_count, 0U - (unsigned)count_to_add, new_commendable_count);
if (new_commendable_count == MAX_UINT_OF(route->meta.commendable_count))
WOLFSENTRY_ERROR_RETURN(OVERFLOW_AVERTED);
+ } else {
+ new_commendable_count = WOLFSENTRY_ATOMIC_LOAD(route->meta.commendable_count);
}
if (new_commendable_count_ptr)
@@ -3483,11 +3666,24 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_set_wildcard(
}
if (wildcards_to_set & WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD) {
route->flags |= WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD;
- route->remote.addr_len = 0;
+ if (route->remote.addr_len > 0) {
+ /* the local address is stored immediately after the remote address,
+ * so dropping the remote address requires sliding the local bytes
+ * down to keep WOLFSENTRY_ROUTE_LOCAL_ADDR() valid. */
+ if (route->local.addr_len > 0)
+ memmove(WOLFSENTRY_ROUTE_REMOTE_ADDR(route),
+ WOLFSENTRY_ROUTE_LOCAL_ADDR(route),
+ WOLFSENTRY_ROUTE_LOCAL_ADDR_BYTES(route));
+ route->remote.addr_len = 0;
+ route->data_addr_size = (uint16_t)WOLFSENTRY_ROUTE_LOCAL_ADDR_BYTES(route);
+ }
}
if (wildcards_to_set & WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD) {
route->flags |= WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD;
- route->local.addr_len = 0;
+ if (route->local.addr_len > 0) {
+ route->local.addr_len = 0;
+ route->data_addr_size = (uint16_t)WOLFSENTRY_ROUTE_REMOTE_ADDR_BYTES(route);
+ }
}
if (wildcards_to_set & WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD) {
route->flags |= WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD;
@@ -3585,6 +3781,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_table_get_default_event(
if (table->default_event->label_len >= *event_label_len)
WOLFSENTRY_ERROR_UNLOCK_AND_RETURN(BUFFER_TOO_SMALL);
memcpy(event_label, table->default_event->label, (size_t)(table->default_event->label_len + 1));
+ *event_label_len = table->default_event->label_len;
WOLFSENTRY_UNLOCK_AND_RETURN_OK;
}
@@ -3926,9 +4123,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_address(
if (sa_family == WOLFSENTRY_AF_LINK || sa_family == WOLFSENTRY_AF_LINK64) {
unsigned int i;
- if ((addr_bits >> 3) * 3 > (size_t)*buflen)
+ unsigned int addr_bytes = (unsigned int)WOLFSENTRY_BITS_TO_BYTES(addr_bits);
+ if (addr_bytes * 3 > (size_t)*buflen)
WOLFSENTRY_ERROR_RETURN(BUFFER_TOO_SMALL);
- for (i=0; i < (addr_bits >> 3); ++i) {
+ for (i=0; i < addr_bytes; ++i) {
if (i > 0)
*buf++ = ':';
*buf++ = hexdigit_ntoa(addr[i] >> 4);
@@ -3955,7 +4153,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_address(
*buf++ = '0';
*buf++ = 'x';
for (i=0; i < 4; ++i) {
- if (i < (addr_bits >> 3)) {
+ if (i < (unsigned int)WOLFSENTRY_BITS_TO_BYTES(addr_bits)) {
*buf++ = hexdigit_ntoa(addr[i] >> 4);
*buf++ = hexdigit_ntoa(addr[i]);
} else {
@@ -4011,8 +4209,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_flag_assoc_by_name(const ch
if (len < 0)
len = (int)strlen(name);
for (i = wolfsentry_route_flag_names; i < end_ptr_of_array(wolfsentry_route_flag_names); ++i) {
- if ((strncmp(i->name, name, (size_t)len) == 0) &&
- (i->name[len] == 0))
+ /* compare lengths first so a shared NUL does not lead to reading
+ * i->name[len] past the end of the name literal. */
+ if ((strlen(i->name) == (size_t)len) &&
+ (strncmp(i->name, name, (size_t)len) == 0))
{
*flag = i->flag;
WOLFSENTRY_RETURN_OK;
@@ -4074,6 +4274,30 @@ static wolfsentry_errcode_t ws_itoa(int i, unsigned char **out, size_t *spc) {
#define unwrite_byte() do { --(*json_out); ++(*json_out_len); } while (0)
#define unwrite_bytes(l) do { size_t _l = (size_t)(l); (*json_out) -= _l; (*json_out_len) += _l; } while (0)
+/* labels and names are arbitrary bytes, so they must be escaped to keep the
+ * output parseable and to keep them from injecting JSON structure.
+ */
+static wolfsentry_errcode_t write_json_string(unsigned char **json_out, size_t *json_out_len, const char *s, size_t s_len) {
+ static const char hexdigits[] = "0123456789abcdef";
+ size_t i;
+
+ write_byte('"');
+ for (i = 0; i < s_len; ++i) {
+ unsigned char c = (unsigned char)s[i];
+ if ((c == '"') || (c == '\\')) {
+ write_byte('\\');
+ write_byte(c);
+ } else if (c < 0x20U) {
+ write_string("\\u00");
+ write_byte((unsigned char)hexdigits[c >> 4U]);
+ write_byte((unsigned char)hexdigits[c & 0xfU]);
+ } else
+ write_byte(c);
+ }
+ write_byte('"');
+ WOLFSENTRY_RETURN_OK;
+}
+
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_json(
WOLFSENTRY_CONTEXT_ARGS_IN,
const struct wolfsentry_route *r,
@@ -4085,9 +4309,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_json(
write_byte('{');
if (r->parent_event) {
- write_string("\"parent-event\":\"");
- write_bytes(r->parent_event->label, r->parent_event->label_len);
- write_string("\",");
+ write_string("\"parent-event\":");
+ WOLFSENTRY_RERETURN_IF_ERROR(write_json_string(json_out, json_out_len, r->parent_event->label, (size_t)r->parent_event->label_len));
+ write_byte(',');
}
{
const struct wolfsentry_route_flag_name_map_ent *i;
@@ -4119,10 +4343,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_json(
if (family_name_len + 2 > *json_out_len)
ret = WOLFSENTRY_ERROR_ENCODE(BUFFER_TOO_SMALL);
else {
- write_byte('"');
- write_bytes(family_name, family_name_len);
- write_byte('"');
- rendered_family = 1;
+ ret = write_json_string(json_out, json_out_len, family_name, family_name_len);
+ if (WOLFSENTRY_IS_SUCCESS(ret))
+ rendered_family = 1;
}
if (addr_family) {
wolfsentry_errcode_t drop_ret = wolfsentry_addr_family_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, addr_family, NULL /* action_results */ );
@@ -4151,18 +4374,18 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_json(
(r->sa_family == WOLFSENTRY_AF_INET6)))
{
char get_buf[256];
- struct protoent protoent, *p;
+ struct protoent protoent, *p = NULL;
/* note this is glibc-specific and non-standard; other libc
* implementations have same name but different args.
+ * an unknown number returns 0 with a null result pointer.
*/
- if (getprotobynumber_r(r->sa_proto,
- &protoent,
- get_buf, sizeof get_buf,
- &p) == 0)
+ if ((getprotobynumber_r(r->sa_proto,
+ &protoent,
+ get_buf, sizeof get_buf,
+ &p) == 0) &&
+ (p != NULL))
{
- write_byte('"');
- write_string(protoent.p_name);
- write_byte('"');
+ WOLFSENTRY_RERETURN_IF_ERROR(write_json_string(json_out, json_out_len, p->p_name, strlen(p->p_name)));
rendered_proto = 1;
}
}
@@ -4183,8 +4406,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_json(
if (have_r_attr(SA_REMOTE_ADDR)) {
#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
if (r->flags & WOLFSENTRY_ROUTE_FLAG_REMOTE_ADDR_BITMASK) {
- int len_in_out = (int)*json_out_len;
+ int len_in_out;
write_string("\"address\":\"");
+ len_in_out = (int)*json_out_len;
ret = wolfsentry_route_format_address(
WOLFSENTRY_CONTEXT_ARGS_OUT,
r->sa_family,
@@ -4211,8 +4435,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_json(
} else
#endif
{
- int len_in_out = (int)*json_out_len;
+ int len_in_out;
write_string("\"address\":\"");
+ len_in_out = (int)*json_out_len;
ret = wolfsentry_route_format_address(
WOLFSENTRY_CONTEXT_ARGS_OUT,
r->sa_family,
@@ -4251,8 +4476,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_json(
if (have_r_attr(SA_LOCAL_ADDR)) {
#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
if (r->flags & WOLFSENTRY_ROUTE_FLAG_LOCAL_ADDR_BITMASK) {
- int len_in_out = (int)*json_out_len;
+ int len_in_out;
write_string("\"address\":\"");
+ len_in_out = (int)*json_out_len;
ret = wolfsentry_route_format_address(
WOLFSENTRY_CONTEXT_ARGS_OUT,
r->sa_family,
@@ -4279,8 +4505,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_format_json(
} else
#endif
{
- int len_in_out = (int)*json_out_len;
+ int len_in_out;
write_string("\"address\":\"");
+ len_in_out = (int)*json_out_len;
ret = wolfsentry_route_format_address(
WOLFSENTRY_CONTEXT_ARGS_OUT,
r->sa_family,
@@ -4455,7 +4682,7 @@ static wolfsentry_errcode_t wolfsentry_route_render_address(WOLFSENTRY_CONTEXT_A
if (sa_family == WOLFSENTRY_AF_LINK || sa_family == WOLFSENTRY_AF_LINK64) {
unsigned int i;
- for (i=0; i < (addr_bits >> 3); ++i) {
+ for (i=0; i < (unsigned int)WOLFSENTRY_BITS_TO_BYTES(addr_bits); ++i) {
if (fprintf(f, "%s%02x", i ? ":" : "", (unsigned int)addr[i]) < 0)
WOLFSENTRY_ERROR_RETURN(IO_FAILED);
}
@@ -4813,6 +5040,8 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_route_table_clone_header(
((struct wolfsentry_route_table *)dest_table)->max_purgeable_routes =
((struct wolfsentry_route_table *)src_table)->max_purgeable_routes;
+ ((struct wolfsentry_route_table *)dest_table)->max_purgeable_idle_time =
+ ((struct wolfsentry_route_table *)src_table)->max_purgeable_idle_time;
((struct wolfsentry_route_table *)dest_table)->default_policy =
((struct wolfsentry_route_table *)src_table)->default_policy;
@@ -4837,14 +5066,17 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_route_table_clone_header(
}
if (((struct wolfsentry_route_table *)src_table)->fallthrough_route != NULL) {
+ /* take the source reference before releasing the destination's own
+ * route, so a failed increment leaves the destination route intact
+ * rather than a freed, still-published pointer. */
+ WOLFSENTRY_REFCOUNT_INCREMENT(((struct wolfsentry_route_table *)src_table)->fallthrough_route->header.refcount, ret);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
if (((struct wolfsentry_route_table *)dest_table)->fallthrough_route != NULL) {
WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_route_drop_reference_1(
WOLFSENTRY_CONTEXT_ARGS_OUT_EX(dest_context),
((struct wolfsentry_route_table *)dest_table)->fallthrough_route, NULL /* action_results */));
}
((struct wolfsentry_route_table *)dest_table)->fallthrough_route = ((struct wolfsentry_route_table *)src_table)->fallthrough_route;
- WOLFSENTRY_REFCOUNT_INCREMENT(((struct wolfsentry_route_table *)dest_table)->fallthrough_route->header.refcount, ret);
- WOLFSENTRY_RERETURN_IF_ERROR(ret);
}
if (WOLFSENTRY_CHECK_BITS(flags, WOLFSENTRY_CLONE_FLAG_NO_ROUTES))
@@ -4890,8 +5122,17 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_route_copy_metadata(
/* pointers are advanced inside the loop. */)
{
if (from_i->header.id == to_i->header.id) {
+ /* meta carries purge_after, which determines purge_list membership
+ * and ordering; refresh the list around the copy so it stays
+ * consistent with the new value.
+ */
+ if (to_i->meta.purge_after)
+ wolfsentry_list_ent_delete(&to_table->purge_list, &to_i->purge_links);
to_i->flags = from_i->flags;
to_i->meta = from_i->meta;
+ to_i->header.hitcount = from_i->header.hitcount;
+ if (to_i->meta.purge_after)
+ wolfsentry_route_purge_list_insert(to_table, to_i);
} else {
int cmpret = wolfsentry_route_key_cmp_1(from_i, to_i, 0 /* match_wildcards_p */, NULL /* inexact_matches */);
if (cmpret < 0) {
diff --git a/src/wolfip/packet_filter_glue.c b/src/wolfip/packet_filter_glue.c
index d3411ab6..c2860f3a 100644
--- a/src/wolfip/packet_filter_glue.c
+++ b/src/wolfip/packet_filter_glue.c
@@ -53,7 +53,13 @@ static int wolfip_cleanup_registered;
static int wolfip_filter_with_wolfsentry(void *arg, const struct wolfIP_filter_event *event);
-static wolfsentry_errcode_t wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_IN);
+static wolfsentry_errcode_t wolfip_apply_masks(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ uint32_t new_mask_eth,
+ uint32_t new_mask_ip,
+ uint32_t new_mask_icmp,
+ uint32_t new_mask_tcp,
+ uint32_t new_mask_udp);
static byte wolfip_if_idx_to_byte(unsigned int if_idx)
{
@@ -163,6 +169,10 @@ static int wolfip_dispatch_event(
if (WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE) < 0)
return -WOLFIP_EACCES;
+ /* fail closed: a failed dispatch leaves no trustworthy verdict. */
+ if (WOLFSENTRY_IS_FAILURE(ws_ret))
+ return -WOLFIP_EACCES;
+
if (wolfip_action_rejects(*action_results))
return -WOLFIP_EACCES;
@@ -209,7 +219,9 @@ static int wolfip_filter_ethernet(
action_results = WOLFSENTRY_ACTION_RES_SOCK_ERROR;
break;
default:
- return 0;
+ WOLFSENTRY_WARN("wolfIP filter: denying unmapped event reason %d\n",
+ (int)event->reason);
+ return -WOLFIP_EACCES;
}
wolfip_set_link_sockaddrs(&remote.remote, &local.local, event, outbound);
@@ -261,7 +273,9 @@ static int wolfip_filter_ipv4(
action_results = WOLFSENTRY_ACTION_RES_SOCK_ERROR;
break;
default:
- return 0;
+ WOLFSENTRY_WARN("wolfIP filter: denying unmapped event reason %d\n",
+ (int)event->reason);
+ return -WOLFIP_EACCES;
}
wolfip_set_ipv4_sockaddrs(&remote.remote, &local.local, event, outbound);
@@ -312,6 +326,7 @@ static int wolfip_filter_tcp(
case WOLFIP_FILT_CLOSE_WAIT:
route_flags |= outbound ? WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT :
WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ action_results = WOLFSENTRY_ACTION_RES_CLOSE_WAIT;
break;
case WOLFIP_FILT_PORT_UNREACHABLE:
route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
@@ -357,10 +372,12 @@ static int wolfip_filter_tcp(
break;
case WOLFIP_FILT_DISSOCIATE:
route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT;
- action_results = WOLFSENTRY_ACTION_RES_DEROGATORY;
+ action_results = WOLFSENTRY_ACTION_RES_CLOSED;
break;
default:
- return 0;
+ WOLFSENTRY_WARN("wolfIP filter: denying unmapped event reason %d\n",
+ (int)event->reason);
+ return -WOLFIP_EACCES;
}
wolfip_set_ipv4_sockaddrs(&remote.remote, &local.local, event, outbound);
@@ -441,8 +458,14 @@ static int wolfip_filter_udp(
action_results = WOLFSENTRY_ACTION_RES_STOPPED_LISTENING |
WOLFSENTRY_ACTION_RES_EXCLUDE_REJECT_ROUTES;
break;
+ case WOLFIP_FILT_DISSOCIATE:
+ route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT;
+ action_results = WOLFSENTRY_ACTION_RES_CLOSED;
+ break;
default:
- return 0;
+ WOLFSENTRY_WARN("wolfIP filter: denying unmapped event reason %d\n",
+ (int)event->reason);
+ return -WOLFIP_EACCES;
}
wolfip_set_ipv4_sockaddrs(&remote.remote, &local.local, event, outbound);
@@ -503,8 +526,25 @@ static int wolfip_filter_icmp(
route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT;
action_results = WOLFSENTRY_ACTION_RES_SOCK_ERROR;
break;
+ case WOLFIP_FILT_BINDING:
+ route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD;
+ action_results = WOLFSENTRY_ACTION_RES_BINDING |
+ WOLFSENTRY_ACTION_RES_EXCLUDE_REJECT_ROUTES;
+ /* socket lifecycle events carry the local endpoint in src_ip; orient
+ * the sockaddrs as locally originated so the bound address lands in
+ * local, while route_flags keeps the inbound binding semantics. */
+ outbound = 1;
+ break;
+ case WOLFIP_FILT_DISSOCIATE:
+ route_flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT;
+ action_results = WOLFSENTRY_ACTION_RES_CLOSED;
+ break;
default:
- return 0;
+ WOLFSENTRY_WARN("wolfIP filter: denying unmapped event reason %d\n",
+ (int)event->reason);
+ return -WOLFIP_EACCES;
}
wolfip_set_ipv4_sockaddrs(&remote.remote, &local.local, event, outbound);
@@ -518,8 +558,10 @@ static int wolfip_filter_with_wolfsentry(void *arg, const struct wolfIP_filter_e
{
struct wolfsentry_context *wolfsentry = (struct wolfsentry_context *)arg;
- if ((wolfsentry == NULL) || (event == NULL))
+ if (event == NULL)
return 0;
+ if (wolfsentry == NULL)
+ return -WOLFIP_EACCES;
switch (event->meta.ip_proto) {
case WOLFIP_FILTER_PROTO_ETH:
@@ -533,30 +575,55 @@ static int wolfip_filter_with_wolfsentry(void *arg, const struct wolfIP_filter_e
case WOLFIP_FILTER_PROTO_ICMP:
return wolfip_filter_icmp(wolfsentry, event);
default:
- return 0;
+ WOLFSENTRY_WARN("wolfIP filter: denying unmapped protocol %d\n",
+ (int)event->meta.ip_proto);
+ return -WOLFIP_EACCES;
}
}
-static wolfsentry_errcode_t wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_IN)
+static wolfsentry_errcode_t wolfip_apply_masks(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ uint32_t new_mask_eth,
+ uint32_t new_mask_ip,
+ uint32_t new_mask_icmp,
+ uint32_t new_mask_tcp,
+ uint32_t new_mask_udp)
{
- uint32_t combined = wolfip_mask_eth | wolfip_mask_ip | wolfip_mask_icmp |
- wolfip_mask_tcp | wolfip_mask_udp;
+ uint32_t combined = new_mask_eth | new_mask_ip | new_mask_icmp |
+ new_mask_tcp | new_mask_udp;
wolfsentry_errcode_t ret = WOLFSENTRY_ERROR_ENCODE(OK);
+ /* validate ownership before mutating any shared state, so a rejected call
+ * cannot perturb the owning context's masks or callback (fail-open). the
+ * check applies on the disable path too. */
+ if ((wolfip_filter_context != NULL) && (wolfip_filter_context != wolfsentry))
+ return WOLFSENTRY_ERROR_ENCODE(INCOMPATIBLE_STATE);
+
if (combined) {
- if (wolfip_filter_context && (wolfip_filter_context != wolfsentry))
- return WOLFSENTRY_ERROR_ENCODE(INCOMPATIBLE_STATE);
- if (!wolfip_cleanup_registered) {
+ if (! wolfip_cleanup_registered) {
ret = wolfsentry_cleanup_push(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry_cleanup_wolfip_filter_callbacks, NULL);
if (ret < 0)
return ret;
wolfip_cleanup_registered = 1;
}
+ }
+
+ wolfip_mask_eth = new_mask_eth;
+ wolfip_mask_ip = new_mask_ip;
+ wolfip_mask_icmp = new_mask_icmp;
+ wolfip_mask_tcp = new_mask_tcp;
+ wolfip_mask_udp = new_mask_udp;
+
+ if (combined) {
wolfip_filter_context = wolfsentry;
wolfIP_filter_set_callback(wolfip_filter_with_wolfsentry, wolfsentry);
} else {
wolfIP_filter_set_callback(NULL, NULL);
wolfip_filter_context = NULL;
+ /* release ownership of the cleanup registration so the next context to
+ * install receives its own cleanup hook rather than relying on a stale
+ * flag bound to a context that may since be torn down. */
+ wolfip_cleanup_registered = 0;
}
wolfIP_filter_set_eth_mask(wolfip_mask_eth);
@@ -570,9 +637,13 @@ static wolfsentry_errcode_t wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_IN)
WOLFSENTRY_API_VOID wolfsentry_cleanup_wolfip_filter_callbacks(WOLFSENTRY_CONTEXT_ARGS_IN, void *cleanup_arg)
{
- (void)wolfsentry;
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
(void)cleanup_arg;
+ /* a cleanup hook fires when its registering context is destroyed; only tear
+ * down the shared filter state if that context still owns it, so destroying
+ * a non-owner cannot silently disable a surviving owner's filtering. */
+ if ((wolfip_filter_context != NULL) && (wolfip_filter_context != wolfsentry))
+ return;
wolfip_mask_eth = wolfip_mask_ip = wolfip_mask_icmp = wolfip_mask_tcp = wolfip_mask_udp = 0;
wolfip_cleanup_registered = 0;
wolfIP_filter_set_callback(NULL, NULL);
@@ -592,8 +663,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_wolfip_filter_ethernet_ca
WOLFSENTRY_MUTEX_OR_RETURN();
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
- wolfip_mask_eth = ethernet_mask;
- ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT,
+ ethernet_mask, wolfip_mask_ip, wolfip_mask_icmp, wolfip_mask_tcp,
+ wolfip_mask_udp);
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
@@ -605,8 +677,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_wolfip_filter_ip4_callbac
WOLFSENTRY_MUTEX_OR_RETURN();
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
- wolfip_mask_ip = ip_mask;
- ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT,
+ wolfip_mask_eth, ip_mask, wolfip_mask_icmp, wolfip_mask_tcp,
+ wolfip_mask_udp);
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
@@ -618,11 +691,23 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_wolfip_filter_icmp_callba
WOLFSENTRY_MUTEX_OR_RETURN();
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
- wolfip_mask_icmp = icmp_mask;
- ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT,
+ wolfip_mask_eth, wolfip_mask_ip, icmp_mask, wolfip_mask_tcp,
+ wolfip_mask_udp);
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
+static uint32_t wolfip_normalize_tcp_mask(uint32_t tcp_mask)
+{
+ /* an accept is counted as a connection, so when accepts are requested also
+ * deliver the close and reset events that release it; teardown-only masks
+ * must not turn on accept counting. */
+ if (tcp_mask & WOLFIP_FILT_MASK(WOLFIP_FILT_ACCEPTING))
+ tcp_mask |= WOLFIP_FILT_MASK(WOLFIP_FILT_CLOSED) |
+ WOLFIP_FILT_MASK(WOLFIP_FILT_REMOTE_RESET);
+ return tcp_mask;
+}
+
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_wolfip_filter_tcp_callback(
WOLFSENTRY_CONTEXT_ARGS_IN,
uint32_t tcp_mask)
@@ -631,14 +716,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_wolfip_filter_tcp_callbac
WOLFSENTRY_MUTEX_OR_RETURN();
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
- if (tcp_mask & (WOLFIP_FILT_MASK(WOLFIP_FILT_ACCEPTING) |
- WOLFIP_FILT_MASK(WOLFIP_FILT_CLOSED) |
- WOLFIP_FILT_MASK(WOLFIP_FILT_REMOTE_RESET)))
- tcp_mask |= WOLFIP_FILT_MASK(WOLFIP_FILT_ACCEPTING) |
- WOLFIP_FILT_MASK(WOLFIP_FILT_CLOSED) |
- WOLFIP_FILT_MASK(WOLFIP_FILT_REMOTE_RESET);
- wolfip_mask_tcp = tcp_mask;
- ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT,
+ wolfip_mask_eth, wolfip_mask_ip, wolfip_mask_icmp,
+ wolfip_normalize_tcp_mask(tcp_mask), wolfip_mask_udp);
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
@@ -650,8 +730,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_wolfip_filter_udp_callbac
WOLFSENTRY_MUTEX_OR_RETURN();
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
- wolfip_mask_udp = udp_mask;
- ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT,
+ wolfip_mask_eth, wolfip_mask_ip, wolfip_mask_icmp, wolfip_mask_tcp,
+ udp_mask);
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
@@ -667,12 +748,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_install_wolfip_filter_callbacks(
WOLFSENTRY_MUTEX_OR_RETURN();
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
- wolfip_mask_eth = ethernet_mask;
- wolfip_mask_ip = ip_mask;
- wolfip_mask_icmp = icmp_mask;
- wolfip_mask_tcp = tcp_mask;
- wolfip_mask_udp = udp_mask;
- ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ ret = wolfip_apply_masks(WOLFSENTRY_CONTEXT_ARGS_OUT,
+ ethernet_mask, ip_mask, icmp_mask, wolfip_normalize_tcp_mask(tcp_mask),
+ udp_mask);
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
}
diff --git a/src/wolfsentry_internal.c b/src/wolfsentry_internal.c
index 33f6e221..2555f4d0 100644
--- a/src/wolfsentry_internal.c
+++ b/src/wolfsentry_internal.c
@@ -275,6 +275,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_coupled_table_clone(
goto out;
new1->parent_table = dest_table1;
new2->parent_table = dest_table2;
+ new1->prev = prev;
if (prev)
prev->next = new1;
else
@@ -317,6 +318,7 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_id_allocate(
)
{
wolfsentry_errcode_t ret;
+ wolfsentry_ent_id_t first_candidate = WOLFSENTRY_ENT_ID_NONE;
WOLFSENTRY_HAVE_MUTEX_OR_RETURN();
@@ -326,7 +328,20 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_id_allocate(
if (ret < 0)
break;
} else {
- ent->id = ++wolfsentry->mk_id_cb_state.id_counter;
+ ++wolfsentry->mk_id_cb_state.id_counter;
+ /* the counter wraps past the reserved null ID; collisions with
+ * live IDs are handled by the ITEM_ALREADY_PRESENT retry.
+ */
+ if (wolfsentry->mk_id_cb_state.id_counter == WOLFSENTRY_ENT_ID_NONE)
+ ++wolfsentry->mk_id_cb_state.id_counter;
+ if (first_candidate == WOLFSENTRY_ENT_ID_NONE)
+ first_candidate = wolfsentry->mk_id_cb_state.id_counter;
+ else if (wolfsentry->mk_id_cb_state.id_counter == first_candidate) {
+ ent->id = WOLFSENTRY_ENT_ID_NONE;
+ ret = WOLFSENTRY_ERROR_ENCODE(SYS_RESOURCE_FAILED);
+ break;
+ }
+ ent->id = wolfsentry->mk_id_cb_state.id_counter;
}
ret = wolfsentry_table_ent_insert_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, ent);
@@ -429,8 +444,8 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_table_ent_delete_by_id_1(WOLFSE
--wolfsentry->ents_by_id.n_ents;
++wolfsentry->ents_by_id.n_deletes;
- /* if possible, reclaim the ID */
- if ((! wolfsentry->mk_id_cb) && (ent->id == (wolfsentry->mk_id_cb_state.id_counter - 1)))
+ /* if possible, reclaim the ID (the counter equals the newest issued ID). */
+ if ((! wolfsentry->mk_id_cb) && (ent->id == wolfsentry->mk_id_cb_state.id_counter))
--wolfsentry->mk_id_cb_state.id_counter;
ent->id = WOLFSENTRY_ENT_ID_NONE;
@@ -555,7 +570,10 @@ WOLFSENTRY_LOCAL wolfsentry_errcode_t wolfsentry_table_free_ents(WOLFSENTRY_CONT
* e.g., a coupled wolfsentry_addr_family_byname is freed when the
* corresponding wolfsentry_addr_family_bynumber is freed.
*/
- table->free_fn(WOLFSENTRY_CONTEXT_ARGS_OUT, i, NULL /* action_results */);
+ ret = table->free_fn(WOLFSENTRY_CONTEXT_ARGS_OUT, i, NULL /* action_results */);
+ /* teardown must run to completion and release its lock, so report a
+ * failed destructor and keep freeing instead of abandoning the table. */
+ WOLFSENTRY_WARN_ON_FAILURE(ret);
i = next;
}
WOLFSENTRY_RETURN_OK;
diff --git a/src/wolfsentry_internal.h b/src/wolfsentry_internal.h
index 484c5911..c13556a4 100644
--- a/src/wolfsentry_internal.h
+++ b/src/wolfsentry_internal.h
@@ -340,6 +340,7 @@ struct wolfsentry_event {
* policy for the ephemeral route.
*/
+ wolfsentry_refcount_t aux_owner_count; /* events whose aux_event is this one */
byte label_len;
char label[WOLFSENTRY_FLEXIBLE_ARRAY_SIZE];
@@ -494,6 +495,7 @@ struct wolfsentry_context {
#endif
struct wolfsentry_table_header ents_by_id;
struct wolfsentry_list_header cleanup_hooks;
+ int action_dispatch_depth; /* nonzero while an action dispatch loop is active, so list mutators refuse to free a live entry */
};
#ifdef WOLFSENTRY_THREADSAFE
diff --git a/src/wolfsentry_ll.h b/src/wolfsentry_ll.h
index 59ec7514..bc7bb82b 100644
--- a/src/wolfsentry_ll.h
+++ b/src/wolfsentry_ll.h
@@ -71,8 +71,10 @@ static inline void wolfsentry_list_ent_insert_before(struct wolfsentry_list_head
if (point_ent->prev) {
new_ent->prev = point_ent->prev;
new_ent->prev->next = new_ent;
- } else
+ } else {
+ new_ent->prev = NULL;
list->head = new_ent;
+ }
point_ent->prev = new_ent;
++list->len;
}
@@ -89,8 +91,10 @@ static inline void wolfsentry_list_ent_insert_after(struct wolfsentry_list_heade
if (point_ent->next) {
new_ent->next = point_ent->next;
new_ent->next->prev = new_ent;
- } else
+ } else {
+ new_ent->next = NULL;
list->tail = new_ent;
+ }
point_ent->next = new_ent;
++list->len;
}
diff --git a/src/wolfsentry_util.c b/src/wolfsentry_util.c
index fc590a01..152c121b 100644
--- a/src/wolfsentry_util.c
+++ b/src/wolfsentry_util.c
@@ -85,7 +85,7 @@ WOLFSENTRY_API const char *wolfsentry_errcode_source_string(wolfsentry_errcode_t
}
static const char *user_defined_errors[WOLFSENTRY_ERROR_ID_MAX - WOLFSENTRY_ERROR_ID_USER_BASE + 1] = {0};
-static const char *user_defined_successes[WOLFSENTRY_ERROR_ID_MAX + WOLFSENTRY_SUCCESS_ID_USER_BASE + 1] = {0};
+static const char *user_defined_successes[WOLFSENTRY_ERROR_ID_MAX - WOLFSENTRY_SUCCESS_ID_USER_BASE + 1] = {0};
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_error_string_set(enum wolfsentry_error_id wolfsentry_error_id, const char *message_string) {
if (wolfsentry_error_id < 0) {
@@ -586,15 +586,23 @@ static void *wolfsentry_builtin_memalign(
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
if (alignment && size) {
- uint32_t hdr_size = sizeof(uint16_t) + (alignment - 1);
- void *p = pvPortMalloc(size + hdr_size);
- if (p) {
+ size_t hdr_size = sizeof(uint16_t) + (alignment - 1);
+ /* require power-of-two alignment, a base offset that fits the stored
+ * uint16_t, and no overflow when reserving the header.
+ */
+ if (((alignment & (alignment - 1)) == 0) &&
+ (hdr_size <= (size_t)MAX_UINT_OF(uint16_t)) &&
+ (size <= (size_t)MAX_UINT_OF(size_t) - hdr_size))
+ {
+ void *p = pvPortMalloc(size + hdr_size);
+ if (p) {
#ifdef WOLFSENTRY_MALLOC_DEBUG
- WOLFSENTRY_ATOMIC_INCREMENT(n_mallocs, 1);
+ WOLFSENTRY_ATOMIC_INCREMENT(n_mallocs, 1);
#endif
- /* Align to powers of two */
- ptr = (void *) ((((uintptr_t)p + sizeof(uint16_t)) + (alignment - 1)) & ~(alignment - 1));
- *((uint16_t *)ptr - 1) = (uint16_t)((uintptr_t)ptr - (uintptr_t)p);
+ /* Align to powers of two */
+ ptr = (void *) ((((uintptr_t)p + sizeof(uint16_t)) + (alignment - 1)) & ~(alignment - 1));
+ *((uint16_t *)ptr - 1) = (uint16_t)((uintptr_t)ptr - (uintptr_t)p);
+ }
}
}
WOLFSENTRY_RETURN_VALUE(ptr);
@@ -668,7 +676,10 @@ static void *wolfsentry_builtin_realloc(
void *ret = realloc(ptr, size);
if ((ptr == NULL) && (ret != NULL))
WOLFSENTRY_ATOMIC_INCREMENT(n_mallocs, 1);
- else if ((ptr != NULL) && (ret == NULL))
+ /* a failed nonzero-size realloc leaves the original block live, so
+ * only a zero-size request counts as a deallocation.
+ */
+ else if ((ptr != NULL) && (ret == NULL) && (size == 0))
WOLFSENTRY_ATOMIC_DECREMENT(n_mallocs, 1);
return ret;
}
@@ -687,14 +698,22 @@ static void *wolfsentry_builtin_memalign(
WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
if (alignment && size) {
size_t hdr_size = sizeof(uint16_t) + (alignment - 1);
- void *p = malloc(size + hdr_size);
- if (p) {
- /* Align to powers of two */
- ptr = (void *) ((((uintptr_t)p + sizeof(uint16_t)) + (alignment - 1)) & ~(alignment - 1));
- *((uint16_t *)ptr - 1) = (uint16_t)((uintptr_t)ptr - (uintptr_t)p);
- }
- /* cppcheck-suppress memleak
+ /* require power-of-two alignment, a base offset that fits the stored
+ * uint16_t, and no overflow when reserving the header.
*/
+ if (((alignment & (alignment - 1)) == 0) &&
+ (hdr_size <= (size_t)MAX_UINT_OF(uint16_t)) &&
+ (size <= (size_t)MAX_UINT_OF(size_t) - hdr_size))
+ {
+ void *p = malloc(size + hdr_size);
+ if (p) {
+ /* Align to powers of two */
+ ptr = (void *) ((((uintptr_t)p + sizeof(uint16_t)) + (alignment - 1)) & ~(alignment - 1));
+ *((uint16_t *)ptr - 1) = (uint16_t)((uintptr_t)ptr - (uintptr_t)p);
+ }
+ /* cppcheck-suppress memleak
+ */
+ }
}
#ifdef WOLFSENTRY_MALLOC_DEBUG
if (ptr != NULL)
@@ -912,31 +931,34 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_free_thread_context(struct wolfse
WOLFSENTRY_RETURN_OK;
}
-static wolfsentry_time_t usecs_to_wolfsentry_time(struct wolfsentry_context *wolfsentry, long usecs) {
- wolfsentry_time_t wolfsentry_time;
- wolfsentry_errcode_t ret = WOLFSENTRY_INTERVAL_FROM_SECONDS(0, usecs * 1000L, &wolfsentry_time);
- if (ret < 0) {
- WOLFSENTRY_WARN_ON_FAILURE(ret);
- return 0;
- }
- else
- return wolfsentry_time;
+static wolfsentry_errcode_t usecs_to_wolfsentry_time(struct wolfsentry_context *wolfsentry, long usecs, wolfsentry_time_t *wolfsentry_time) {
+ /* split into seconds and nanoseconds so the full long range of usecs is
+ * preserved without overflowing the intermediate multiplication.
+ */
+ WOLFSENTRY_RERETURN_IF_ERROR(WOLFSENTRY_INTERVAL_FROM_SECONDS(usecs / 1000000L, (usecs % 1000000L) * 1000L, wolfsentry_time));
+ WOLFSENTRY_RETURN_OK;
}
-static long wolfsentry_time_to_usecs(struct wolfsentry_context *wolfsentry, wolfsentry_time_t ts) {
+static wolfsentry_errcode_t wolfsentry_time_to_usecs(struct wolfsentry_context *wolfsentry, wolfsentry_time_t ts, long *usecs) {
time_t howlong_secs;
long howlong_nsecs;
- wolfsentry_errcode_t ret = WOLFSENTRY_INTERVAL_TO_SECONDS(ts, &howlong_secs, &howlong_nsecs);
- if (ret < 0) {
- WOLFSENTRY_WARN_ON_FAILURE(ret);
- return 0;
- }
- else if (howlong_secs * (time_t)1000000 >= (time_t)MAX_SINT_OF(long) - (time_t)1000000L)
- return (long)MAX_SINT_OF(long);
- else if (howlong_secs * (time_t)1000000 <= (time_t)MIN_SINT_OF(long))
- return (long)MIN_SINT_OF(long);
+ long howlong_usecs;
+ WOLFSENTRY_RERETURN_IF_ERROR(WOLFSENTRY_INTERVAL_TO_SECONDS(ts, &howlong_secs, &howlong_nsecs));
+ howlong_usecs = howlong_nsecs / 1000L;
+ /* compare the seconds quotient and then the remainder so a representable
+ * value is never saturated, and the multiply never overflows long.
+ */
+ if ((howlong_secs > (time_t)(MAX_SINT_OF(long) / (int64_t)1000000)) ||
+ ((howlong_secs == (time_t)(MAX_SINT_OF(long) / (int64_t)1000000)) &&
+ (howlong_usecs > (long)(MAX_SINT_OF(long) % (int64_t)1000000))))
+ *usecs = (long)MAX_SINT_OF(long);
+ else if ((howlong_secs < (time_t)(MIN_SINT_OF(long) / (int64_t)1000000)) ||
+ ((howlong_secs == (time_t)(MIN_SINT_OF(long) / (int64_t)1000000)) &&
+ (howlong_usecs < (long)(MIN_SINT_OF(long) % (int64_t)1000000))))
+ *usecs = (long)MIN_SINT_OF(long);
else
- return ((long)howlong_secs * 1000000L) + (howlong_nsecs / 1000L);
+ *usecs = ((long)howlong_secs * 1000000L) + howlong_usecs;
+ WOLFSENTRY_RETURN_OK;
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_set_deadline_rel(WOLFSENTRY_CONTEXT_ARGS_IN, wolfsentry_time_t rel_when) {
@@ -963,7 +985,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_set_deadline_rel(WOLFSENTRY_CONTE
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_set_deadline_rel_usecs(WOLFSENTRY_CONTEXT_ARGS_IN, long usecs) {
- return wolfsentry_set_deadline_rel(WOLFSENTRY_CONTEXT_ARGS_OUT, usecs_to_wolfsentry_time(wolfsentry, usecs));
+ wolfsentry_time_t rel_when;
+ wolfsentry_errcode_t ret = usecs_to_wolfsentry_time(wolfsentry, usecs, &rel_when);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ return wolfsentry_set_deadline_rel(WOLFSENTRY_CONTEXT_ARGS_OUT, rel_when);
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_get_deadline_rel(WOLFSENTRY_CONTEXT_ARGS_IN, wolfsentry_time_t *rel_when) {
@@ -1004,8 +1029,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_get_deadline_rel_usecs(WOLFSENTRY
{
return ret;
}
- if (usecs)
- *usecs = wolfsentry_time_to_usecs(wolfsentry, rel_when);
+ if (usecs) {
+ wolfsentry_errcode_t conv_ret = wolfsentry_time_to_usecs(wolfsentry, rel_when, usecs);
+ WOLFSENTRY_RERETURN_IF_ERROR(conv_ret);
+ }
return ret;
}
@@ -1538,16 +1565,47 @@ static int freertos_sem_destroy( sem_t * sem )
}
WOLFSENTRY_RETURN_VALUE(0);
}
+ #ifndef EAGAIN
+ #define EAGAIN 11 /* Try again */
+ #endif
+ #ifndef EINTR
+ #define EINTR 4 /* Interrupted system call */
+ #endif
+
#define sem_timedwait threadx_sem_timedwait
static int threadx_sem_timedwait( sem_t * sem,
const struct timespec * abstime )
{
- if (tx_semaphore_get(sem, (uint32_t)(TX_TICK_TIME_MS *
- (abstime->tv_sec * 1000) + (abstime->tv_nsec / 1000000))) != TX_SUCCESS) {
- errno = EINVAL;
- WOLFSENTRY_RETURN_VALUE(-1);
+ ULONG wait_ticks = TX_WAIT_FOREVER;
+ UINT tx_ret;
+
+ if (abstime != NULL) {
+ uint64_t deadline_ticks, now_ticks;
+ if ((abstime->tv_sec < 0) || (abstime->tv_nsec < 0) || (abstime->tv_nsec >= 1000000000L)) {
+ errno = EINVAL;
+ WOLFSENTRY_RETURN_VALUE(-1);
+ }
+ /* abstime is on the builtin get_time clock, so invert that tick mapping, rounding up so a sub-tick future deadline still waits a tick. */
+ deadline_ticks = ((uint64_t)abstime->tv_sec * (uint64_t)TX_TICK_TIME_MS) + (((uint64_t)abstime->tv_nsec + (uint64_t)999999) / (uint64_t)1000000);
+ now_ticks = (uint64_t)tx_time_get();
+ if (deadline_ticks <= now_ticks)
+ wait_ticks = TX_NO_WAIT;
+ else if (deadline_ticks - now_ticks >= (uint64_t)TX_WAIT_FOREVER)
+ wait_ticks = (ULONG)(TX_WAIT_FOREVER - 1);
+ else
+ wait_ticks = (ULONG)(deadline_ticks - now_ticks);
}
- WOLFSENTRY_RETURN_VALUE(0);
+
+ tx_ret = tx_semaphore_get(sem, wait_ticks);
+ if (tx_ret == TX_SUCCESS)
+ WOLFSENTRY_RETURN_VALUE(0);
+ if (tx_ret == TX_NO_INSTANCE)
+ errno = ETIMEDOUT;
+ else if (tx_ret == TX_WAIT_ABORTED)
+ errno = EINTR;
+ else
+ errno = EINVAL;
+ WOLFSENTRY_RETURN_VALUE(-1);
}
#define sem_wait threadx_sem_wait
static int threadx_sem_wait( sem_t * sem )
@@ -1561,11 +1619,16 @@ static int freertos_sem_destroy( sem_t * sem )
#define sem_trywait threadx_sem_trywait
static int threadx_sem_trywait( sem_t * sem )
{
- if (tx_semaphore_get(sem, TX_NO_WAIT) != TX_SUCCESS) {
+ UINT tx_ret = tx_semaphore_get(sem, TX_NO_WAIT);
+ if (tx_ret == TX_SUCCESS)
+ WOLFSENTRY_RETURN_VALUE(0);
+ if (tx_ret == TX_NO_INSTANCE)
+ errno = EAGAIN;
+ else if (tx_ret == TX_WAIT_ABORTED)
+ errno = EINTR;
+ else
errno = EINVAL;
- WOLFSENTRY_RETURN_VALUE(-1);
- }
- WOLFSENTRY_RETURN_VALUE(0);
+ WOLFSENTRY_RETURN_VALUE(-1);
}
static int threadx_sem_destroy( sem_t * sem )
{
@@ -3018,9 +3081,13 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_lock_unlock(struct wolfsentry_rwl
WOLFSENTRY_THREAD_ASSERT_NULL_OR_INITED(thread);
- /* unlocking a recursive mutex, like recursively locking one, can be done lock-free. */
+ /* unlocking a recursive mutex, like recursively locking one, can be done
+ * lock-free, except when unwinding to the promotion depth, which must
+ * evaluate the downgrade logic below.
+ */
if ((WOLFSENTRY_ATOMIC_LOAD(lock->write_lock_holder) == WOLFSENTRY_THREAD_GET_ID) &&
- (lock->holder_count.write > 1))
+ (lock->holder_count.write > 1) &&
+ (lock->holder_count.write - 1 != lock->promoted_at_count))
{
--lock->holder_count.write;
if (thread)
@@ -3127,16 +3194,25 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_lock_unlock(struct wolfsentry_rwl
if (lock->promoted_at_count == lock->holder_count.write) {
lock->promoted_at_count = 0;
if ((flags & WOLFSENTRY_LOCK_FLAG_AUTO_DOWNGRADE) && (! thread->tracked_shared_lock)) {
- WOLFSENTRY_ATOMIC_STORE(lock->state, WOLFSENTRY_LOCK_SHARED);
+ /* same bookkeeping as wolfsentry_lock_mutex2shared(): writer count becomes reader count. */
if (flags & (WOLFSENTRY_LOCK_FLAG_TRY_RESERVATION_TOO | WOLFSENTRY_LOCK_FLAG_GET_RESERVATION_TOO)) {
+ if (lock->read2write_reservation_holder != WOLFSENTRY_THREAD_NO_ID) /* can't happen, but be sure. */ {
+ ret = WOLFSENTRY_ERROR_ENCODE(INTERNAL_CHECK_FATAL);
+ goto out;
+ }
+ ++thread->mutex_and_reservation_count;
WOLFSENTRY_ATOMIC_STORE(lock->read2write_reservation_holder, WOLFSENTRY_THREAD_GET_ID);
+ lock->read2write_waiter_read_count = lock->holder_count.write;
/* note, not incrementing write_waiter_count, to allow shared lockers to get locks until the redemption phase. */
- lock->read2write_waiter_read_count = lock->holder_count.read;
+ }
+ WOLFSENTRY_ATOMIC_STORE(lock->state, WOLFSENTRY_LOCK_SHARED);
+ WOLFSENTRY_ATOMIC_STORE(lock->write_lock_holder, WOLFSENTRY_THREAD_NO_ID);
+ thread->mutex_and_reservation_count -= lock->holder_count.write;
+ thread->shared_count += lock->holder_count.read;
+ thread->recursion_of_tracked_lock = lock->holder_count.read;
+ thread->tracked_shared_lock = lock;
+ if (flags & (WOLFSENTRY_LOCK_FLAG_TRY_RESERVATION_TOO | WOLFSENTRY_LOCK_FLAG_GET_RESERVATION_TOO)) {
++lock->holder_count.read; /* suppress posts to sem_read2write_waiters until wolfsentry_lock_shared2mutex_redeem() is entered. */
- thread->mutex_and_reservation_count -= lock->holder_count.write - 1; /* -1 for the reservation */
- thread->shared_count += lock->holder_count.read;
- thread->tracked_shared_lock = lock;
- thread->recursion_of_tracked_lock = lock->holder_count.read;
ret = WOLFSENTRY_SUCCESS_ENCODE(LOCK_OK_AND_GOT_RESV);
} else
ret = WOLFSENTRY_ERROR_ENCODE(OK);
@@ -3167,11 +3243,12 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_lock_unlock(struct wolfsentry_rwl
}
}
} else if (lock->read_waiter_count > 0) {
- int i;
- lock->holder_count.read += lock->read_waiter_count;
+ /* post once per queued reader, not per holder, as existing shared holders need no wakeup. */
+ int read_waiter_count = lock->read_waiter_count;
+ lock->holder_count.read += read_waiter_count;
lock->read_waiter_count = 0;
WOLFSENTRY_ATOMIC_STORE(lock->state, WOLFSENTRY_LOCK_SHARED);
- for (i = 0; i < lock->holder_count.read; ++i) {
+ for (; read_waiter_count > 0; --read_waiter_count) {
if (sem_post(&lock->sem_read_waiters) < 0) {
ret = WOLFSENTRY_ERROR_ENCODE(SYS_OP_FATAL);
goto out;
@@ -3471,7 +3548,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_time_now_plus_delta(struct wolfse
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_time_to_timespec(struct wolfsentry_context *wolfsentry, wolfsentry_time_t t, struct timespec *ts) {
time_t epoch_secs;
long int epoch_nsecs;
- WOLFSENTRY_TO_EPOCH_TIME(t, &epoch_secs, &epoch_nsecs);
+ wolfsentry_errcode_t ret = WOLFSENTRY_TO_EPOCH_TIME(t, &epoch_secs, &epoch_nsecs);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
ts->tv_sec = epoch_secs;
ts->tv_nsec = epoch_nsecs;
WOLFSENTRY_RETURN_OK;
@@ -3483,7 +3561,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_time_now_plus_delta_timespec(stru
long int epoch_nsecs;
wolfsentry_errcode_t ret = WOLFSENTRY_GET_TIME(&now);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
- WOLFSENTRY_TO_EPOCH_TIME(WOLFSENTRY_ADD_TIME(now, td), &epoch_secs, &epoch_nsecs);
+ ret = WOLFSENTRY_TO_EPOCH_TIME(WOLFSENTRY_ADD_TIME(now, td), &epoch_secs, &epoch_nsecs);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
ts->tv_sec = epoch_secs;
ts->tv_nsec = epoch_nsecs;
WOLFSENTRY_RETURN_OK;
@@ -3571,9 +3650,22 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_object_checkout(WOLFSENTRY_CONTEX
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_object_release(WOLFSENTRY_CONTEXT_ARGS_IN, void *object, wolfsentry_action_res_t *action_results) {
- if (object == NULL)
+ struct wolfsentry_table_ent_header *ent = (struct wolfsentry_table_ent_header *)object;
+ if (ent == NULL)
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
- WOLFSENTRY_ERROR_RERETURN(((struct wolfsentry_table_ent_header *)object)->parent_table->free_fn(WOLFSENTRY_CONTEXT_ARGS_OUT, (struct wolfsentry_table_ent_header *)object, action_results));
+ if (ent->parent_table == NULL) {
+ /* the fallthrough route is never linked into a table, but is documented as releasable here. */
+ if ((wolfsentry->routes != NULL) &&
+ (wolfsentry->routes->fallthrough_route != NULL) &&
+ (object == (void *)wolfsentry->routes->fallthrough_route))
+ {
+ WOLFSENTRY_ERROR_RERETURN(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, wolfsentry->routes->fallthrough_route, action_results));
+ }
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ }
+ if (ent->parent_table->free_fn == NULL)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ WOLFSENTRY_ERROR_RERETURN(ent->parent_table->free_fn(WOLFSENTRY_CONTEXT_ARGS_OUT, ent, action_results));
}
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_eventconfig_init(
@@ -3605,6 +3697,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_eventconfig_check(
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
if (config->derogatory_threshold_for_penaltybox > MAX_UINT_OF(instance_of_field(struct wolfsentry_route, meta.derogatory_count)))
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+ if (config->max_connection_count > MAX_UINT_OF(instance_of_field(struct wolfsentry_route, meta.connection_count)))
+ WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG);
ret = wolfsentry_route_check_flags_sensical(config->route_flags_to_add_on_insert);
WOLFSENTRY_RERETURN_IF_ERROR(ret);
@@ -3743,8 +3837,13 @@ static wolfsentry_errcode_t wolfsentry_context_free_1(
)
{
#ifdef WOLFSENTRY_THREADSAFE
- wolfsentry_errcode_t ret = wolfsentry_lock_have_mutex(&(*wolfsentry)->lock, thread, WOLFSENTRY_LOCK_FLAG_NONE);
- WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ wolfsentry_errcode_t ret;
+ /* a context whose lock never came up is still private to its allocator, so no mutex is possible or needed. */
+ int lock_inited = (WOLFSENTRY_ATOMIC_LOAD((*wolfsentry)->lock.state) != WOLFSENTRY_LOCK_UNINITED);
+ if (lock_inited) {
+ ret = wolfsentry_lock_have_mutex(&(*wolfsentry)->lock, thread, WOLFSENTRY_LOCK_FLAG_NONE);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ }
#endif
if ((*wolfsentry)->routes != NULL)
@@ -3763,10 +3862,12 @@ static wolfsentry_errcode_t wolfsentry_context_free_1(
#endif
#ifdef WOLFSENTRY_THREADSAFE
- ret = wolfsentry_lock_unlock(&(*wolfsentry)->lock, thread, WOLFSENTRY_LOCK_FLAG_NONE);
- WOLFSENTRY_RERETURN_IF_ERROR(ret);
- ret = wolfsentry_lock_destroy(&(*wolfsentry)->lock, thread, WOLFSENTRY_LOCK_FLAG_NONE);
- WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ if (lock_inited) {
+ ret = wolfsentry_lock_unlock(&(*wolfsentry)->lock, thread, WOLFSENTRY_LOCK_FLAG_NONE);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ ret = wolfsentry_lock_destroy(&(*wolfsentry)->lock, thread, WOLFSENTRY_LOCK_FLAG_NONE);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ }
#endif
WOLFSENTRY_FREE_1((*wolfsentry)->hpi.allocator, *wolfsentry);
@@ -3774,6 +3875,16 @@ static wolfsentry_errcode_t wolfsentry_context_free_1(
WOLFSENTRY_RETURN_OK;
}
+static void *wolfsentry_context_alloc_zeroed(
+ WOLFSENTRY_CONTEXT_ARGS_IN_EX(const struct wolfsentry_host_platform_interface *hpi),
+ size_t size)
+{
+ void *ret = WOLFSENTRY_MALLOC_1(hpi->allocator, size);
+ if (ret != NULL)
+ memset(ret, 0, size);
+ WOLFSENTRY_RETURN_VALUE(ret);
+}
+
static wolfsentry_errcode_t wolfsentry_context_init_1(
struct wolfsentry_context *wolfsentry
#ifdef WOLFSENTRY_THREADSAFE
@@ -3817,36 +3928,29 @@ static wolfsentry_errcode_t wolfsentry_context_alloc_1(
)
{
wolfsentry_errcode_t ret;
- if ((*wolfsentry = (struct wolfsentry_context *)WOLFSENTRY_MALLOC_1(hpi->allocator, sizeof **wolfsentry)) == NULL)
+ if ((*wolfsentry = (struct wolfsentry_context *)wolfsentry_context_alloc_zeroed(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(hpi), sizeof **wolfsentry)) == NULL)
WOLFSENTRY_ERROR_RETURN(SYS_RESOURCE_FAILED);
- memset(*wolfsentry, 0, sizeof **wolfsentry);
-
(*wolfsentry)->hpi = *hpi;
- if ((((*wolfsentry)->events = (struct wolfsentry_event_table *)WOLFSENTRY_MALLOC_1(hpi->allocator, sizeof *(*wolfsentry)->events)) == NULL) ||
- (((*wolfsentry)->actions = (struct wolfsentry_action_table *)WOLFSENTRY_MALLOC_1(hpi->allocator, sizeof *(*wolfsentry)->actions)) == NULL) ||
- (((*wolfsentry)->routes = (struct wolfsentry_route_table *)WOLFSENTRY_MALLOC_1(hpi->allocator, sizeof *(*wolfsentry)->routes)) == NULL) ||
- (((*wolfsentry)->user_values = (struct wolfsentry_kv_table *)WOLFSENTRY_MALLOC_1(hpi->allocator, sizeof *(*wolfsentry)->user_values)) == NULL) ||
- (((*wolfsentry)->addr_families_bynumber = (struct wolfsentry_addr_family_bynumber_table *)WOLFSENTRY_MALLOC_1(hpi->allocator, sizeof *(*wolfsentry)->addr_families_bynumber)) == NULL)
+ /* each table is zeroed as allocated, so a partial failure leaves only well-formed tables for free_1 to tear down. */
+ if ((((*wolfsentry)->events = (struct wolfsentry_event_table *)wolfsentry_context_alloc_zeroed(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(hpi), sizeof *(*wolfsentry)->events)) == NULL) ||
+ (((*wolfsentry)->actions = (struct wolfsentry_action_table *)wolfsentry_context_alloc_zeroed(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(hpi), sizeof *(*wolfsentry)->actions)) == NULL) ||
+ (((*wolfsentry)->routes = (struct wolfsentry_route_table *)wolfsentry_context_alloc_zeroed(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(hpi), sizeof *(*wolfsentry)->routes)) == NULL) ||
+ (((*wolfsentry)->user_values = (struct wolfsentry_kv_table *)wolfsentry_context_alloc_zeroed(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(hpi), sizeof *(*wolfsentry)->user_values)) == NULL) ||
+ (((*wolfsentry)->addr_families_bynumber = (struct wolfsentry_addr_family_bynumber_table *)wolfsentry_context_alloc_zeroed(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(hpi), sizeof *(*wolfsentry)->addr_families_bynumber)) == NULL)
#ifdef WOLFSENTRY_PROTOCOL_NAMES
- || (((*wolfsentry)->addr_families_byname = (struct wolfsentry_addr_family_byname_table *)WOLFSENTRY_MALLOC_1(hpi->allocator, sizeof *(*wolfsentry)->addr_families_byname)) == NULL)
+ || (((*wolfsentry)->addr_families_byname = (struct wolfsentry_addr_family_byname_table *)wolfsentry_context_alloc_zeroed(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(hpi), sizeof *(*wolfsentry)->addr_families_byname)) == NULL)
#endif
)
{
- (void)wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT));
WOLFSENTRY_ERROR_RETURN(SYS_RESOURCE_FAILED);
}
- memset((*wolfsentry)->events, 0, sizeof *(*wolfsentry)->events);
- memset((*wolfsentry)->actions, 0, sizeof *(*wolfsentry)->actions);
- memset((*wolfsentry)->routes, 0, sizeof *(*wolfsentry)->routes);
- memset((*wolfsentry)->user_values, 0, sizeof *(*wolfsentry)->user_values);
- memset((*wolfsentry)->addr_families_bynumber, 0, sizeof *(*wolfsentry)->addr_families_bynumber);
#ifdef WOLFSENTRY_PROTOCOL_NAMES
- memset((*wolfsentry)->addr_families_byname, 0, sizeof *(*wolfsentry)->addr_families_byname);
if ((ret = wolfsentry_addr_family_table_pair(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(*wolfsentry), (*wolfsentry)->addr_families_bynumber, (*wolfsentry)->addr_families_byname)) < 0) {
- (void)wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT));
WOLFSENTRY_ERROR_RERETURN(ret);
}
#endif
@@ -3856,7 +3960,12 @@ static wolfsentry_errcode_t wolfsentry_context_alloc_1(
, lock_flags
#endif
)) < 0) {
- (void)wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT);
+#ifdef WOLFSENTRY_THREADSAFE
+ /* init_1 can fail after the lock is up, and free_1 then needs the mutex, which no one else can hold yet. */
+ if (WOLFSENTRY_ATOMIC_LOAD((*wolfsentry)->lock.state) != WOLFSENTRY_LOCK_UNINITED)
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_lock_mutex_abstimed(&(*wolfsentry)->lock, NULL /* thread */, ×pec_deadline_now, WOLFSENTRY_LOCK_FLAG_NONE));
+#endif
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL /* thread */)));
WOLFSENTRY_ERROR_RERETURN(ret);
}
@@ -3873,6 +3982,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_init_ex(
struct wolfsentry_host_platform_interface hpi;
#ifdef WOLFSENTRY_THREADSAFE
wolfsentry_lock_flags_t lock_flags = WOLFSENTRY_LOCK_FLAG_NONE;
+ wolfsentry_errcode_t cleanup_ret;
#endif
wolfsentry_errcode_t ret;
@@ -3952,8 +4062,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_init_ex(
* because wolfsentry_interval_from_seconds() needs a valid
* wolfsentry_context (circular dependency). fix it now that we can.
*/
- hpi.timecbs.interval_from_seconds((long int)(*wolfsentry)->config.config.penaltybox_duration, 0 /* howlong_nsecs */, &((*wolfsentry)->config.config.penaltybox_duration));
- hpi.timecbs.interval_from_seconds((long int)(*wolfsentry)->config.config.route_idle_time_for_purge, 0 /* howlong_nsecs */, &((*wolfsentry)->config.config.route_idle_time_for_purge));
+ if ((ret = hpi.timecbs.interval_from_seconds((time_t)(*wolfsentry)->config.config.penaltybox_duration, 0 /* howlong_nsecs */, &((*wolfsentry)->config.config.penaltybox_duration))) < 0)
+ goto out;
+ if ((ret = hpi.timecbs.interval_from_seconds((time_t)(*wolfsentry)->config.config.route_idle_time_for_purge, 0 /* howlong_nsecs */, &((*wolfsentry)->config.config.route_idle_time_for_purge))) < 0)
+ goto out;
(*wolfsentry)->config_at_creation = (*wolfsentry)->config;
@@ -3968,7 +4080,15 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_init_ex(
out:
if (ret < 0) {
- (void)wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT);
+#ifdef WOLFSENTRY_THREADSAFE
+ /* the new context is still private, so tear it down with no thread context, which a read-only thread or an expired deadline cannot block. */
+ cleanup_ret = wolfsentry_lock_mutex_abstimed(&(*wolfsentry)->lock, NULL /* thread */, ×pec_deadline_now, WOLFSENTRY_LOCK_FLAG_NONE);
+ if (cleanup_ret >= 0)
+ cleanup_ret = wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(wolfsentry, NULL /* thread */));
+ WOLFSENTRY_WARN_ON_FAILURE(cleanup_ret);
+#else
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT));
+#endif
}
WOLFSENTRY_ERROR_RERETURN(ret);
}
@@ -4147,6 +4267,9 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_context_clone(
wolfsentry_clone_flags_t flags)
{
wolfsentry_errcode_t ret;
+#ifdef WOLFSENTRY_THREADSAFE
+ wolfsentry_errcode_t cleanup_ret;
+#endif
#ifdef WOLFSENTRY_THREADSAFE
WOLFSENTRY_SHARED_OR_RETURN();
@@ -4154,8 +4277,14 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_context_clone(
if ((ret = wolfsentry_context_alloc_1(&wolfsentry->hpi, thread, clone, wolfsentry->lock.flags)) < 0)
WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
- if ((ret = wolfsentry_context_lock_mutex_abstimed(*clone, thread, NULL)) < 0)
- goto out;
+ if ((ret = wolfsentry_context_lock_mutex_abstimed(*clone, thread, NULL)) < 0) {
+ /* the clone is still private, so tear it down with no thread context, which a read-only thread or an expired deadline cannot block. */
+ cleanup_ret = wolfsentry_lock_mutex_abstimed(&(*clone)->lock, NULL /* thread */, ×pec_deadline_now, WOLFSENTRY_LOCK_FLAG_NONE);
+ if (cleanup_ret >= 0)
+ cleanup_ret = wolfsentry_context_free_1(WOLFSENTRY_CONTEXT_ARGS_OUT_EX4(clone, NULL /* thread */));
+ WOLFSENTRY_WARN_ON_FAILURE(cleanup_ret);
+ WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
+ }
#else
if ((ret = wolfsentry_context_alloc_1(&wolfsentry->hpi, clone)) < 0)
WOLFSENTRY_ERROR_RERETURN(ret);
@@ -4202,7 +4331,8 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_context_clone(
#endif
if (WOLFSENTRY_CHECK_BITS(flags, WOLFSENTRY_CLONE_FLAG_AS_AT_CREATION)) {
- ret = WOLFSENTRY_ERROR_ENCODE(OK);
+ /* the fresh route table needs its own fallthrough route, as wolfsentry_init_ex() provides. */
+ ret = wolfsentry_route_table_fallthrough_route_alloc(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(*clone), (*clone)->routes);
goto out;
}
@@ -4249,6 +4379,14 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_context_exchange(WOLFSENTRY_CONTE
}
#endif
+ /* refuse to swap tables out from under an action dispatch in progress on
+ * either context, which would strand the live list's reentrancy guard.
+ */
+ if ((wolfsentry->action_dispatch_depth != 0) || (wolfsentry2->action_dispatch_depth != 0)) {
+ ret = WOLFSENTRY_ERROR_ENCODE(BUSY);
+ goto out;
+ }
+
/* now that we have a mutex on both contexts, coherently copy the route
* metadata from the current context to the new one to be swapped in.
*/
@@ -4289,6 +4427,10 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_context_exchange(WOLFSENTRY_CONTE
wolfsentry2->ents_by_id = scratch.ents_by_id;
+ /* keep the lock-free copy of the default policy in step with the swapped route tables. */
+ WOLFSENTRY_ATOMIC_STORE(wolfsentry->default_policy, wolfsentry->routes->default_policy);
+ WOLFSENTRY_ATOMIC_STORE(wolfsentry2->default_policy, wolfsentry2->routes->default_policy);
+
/* note that wolfsentry->lock and wolfsentry->cleanup_hooks are not copied.
* both of these follow the context pointer itself, which is necessarily
* invariant once allocated by wolfsentry_init(), until final deallocation
@@ -4358,13 +4500,15 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_base64_decode(const char *src, si
else
WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
} else if (this_char == (uint32_t)'@') {
- if ((src + 1 < src_end) && (base64_inv_lut[*((unsigned char *)src + 1)] != '@'))
- WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
++pad_chars;
decoded <<= 6U;
continue;
}
+ /* data must not appear after padding, in strict or lenient mode */
+ if (pad_chars != 0)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+
decoded <<= 6U;
decoded |= this_char;
decoded_bits += 6U;
@@ -4391,12 +4535,15 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_base64_decode(const char *src, si
case 0:
break;
case 1:
+ if (decoded_bits != 18U)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
*dest++ = (byte)(decoded >> 16U);
*dest++ = (byte)(decoded >> 8U);
dest_len += 2;
break;
- /* fall through */
case 2:
+ if (decoded_bits != 12U)
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
*dest++ = (byte)(decoded >> 16U);
++dest_len;
break;
diff --git a/tests/filter-clang-analyzer.py b/tests/filter-clang-analyzer.py
index f4e199fa..72bdbea1 100644
--- a/tests/filter-clang-analyzer.py
+++ b/tests/filter-clang-analyzer.py
@@ -8,9 +8,9 @@
ALLOWED_RESULTS = {
- ("core.uninitialized.Assign", "src/json/centijson_value.c", 1285),
- ("core.NullDereference", "src/json/centijson_value.c", 1483),
- ("core.NullDereference", "src/json/centijson_value.c", 1520),
+ ("core.uninitialized.Assign", "src/json/centijson_value.c", 1366),
+ ("core.NullDereference", "src/json/centijson_value.c", 1564),
+ ("core.NullDereference", "src/json/centijson_value.c", 1601),
}
diff --git a/tests/unittests.c b/tests/unittests.c
index 7a756860..824840b1 100644
--- a/tests/unittests.c
+++ b/tests/unittests.c
@@ -33,6 +33,11 @@
#define PRIVATE_DATA_ALIGNMENT 16
#endif
+#ifdef __GNUC__
+/* the list tests call the inline list primitives directly from run-once code */
+#pragma GCC diagnostic ignored "-Winline"
+#endif
+
#include "src/wolfsentry_internal.h"
#if (defined(_POSIX_C_SOURCE) || defined(__MACH__)) && !defined(FREERTOS)
@@ -272,6 +277,139 @@ static wolfsentry_errcode_t test_init(void) {
WOLFSENTRY_ERROR_RERETURN(ret);
}
+static wolfsentry_errcode_t test_base64_decode_lenient_padding(void) {
+ /* A correctly padded value followed by ignorable whitespace must be
+ * accepted when lenient parsing is requested.
+ */
+ static const char input[] = "QQ==\n";
+ byte outbuf[8];
+ size_t outbuf_spc = sizeof outbuf;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_base64_decode(input, strlen(input), outbuf, &outbuf_spc, 1 /* ignore_junk_p */));
+ WOLFSENTRY_EXIT_ON_FALSE((outbuf_spc == 1) && (outbuf[0] == 'A'));
+
+ /* lenient parsing still rejects data appearing after padding */
+ outbuf_spc = sizeof outbuf;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_base64_decode("Q=Q=", 4, outbuf, &outbuf_spc, 1 /* ignore_junk_p */));
+ outbuf_spc = sizeof outbuf;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_base64_decode("QQ==QUJD", 8, outbuf, &outbuf_spc, 1 /* ignore_junk_p */));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_base64_decode_quantum(void) {
+ /* A padded quantum must carry enough data sextets for its pad count; a
+ * single data sextet followed by padding is structurally invalid.
+ */
+ byte outbuf[8];
+ size_t outbuf_spc;
+
+ outbuf_spc = sizeof outbuf;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_base64_decode("A=", 2, outbuf, &outbuf_spc, 0 /* ignore_junk_p */));
+
+ outbuf_spc = sizeof outbuf;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_base64_decode("A==", 3, outbuf, &outbuf_spc, 0 /* ignore_junk_p */));
+
+ /* A well-formed padded quantum is still accepted. */
+ outbuf_spc = sizeof outbuf;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_base64_decode("QQ==", 4, outbuf, &outbuf_spc, 0 /* ignore_junk_p */));
+ WOLFSENTRY_EXIT_ON_FALSE((outbuf_spc == 1) && (outbuf[0] == 'A'));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_eventconfig_overlap_bits(void) {
+ /* A bit cannot be simultaneously requested for addition and clearing. */
+ struct wolfsentry_eventconfig config;
+
+ memset(&config, 0, sizeof config);
+ config.route_flags_to_add_on_insert = WOLFSENTRY_ROUTE_FLAG_GREENLISTED;
+ config.route_flags_to_clear_on_insert = WOLFSENTRY_ROUTE_FLAG_GREENLISTED;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_eventconfig_check(&config));
+
+ memset(&config, 0, sizeof config);
+ config.action_res_filter_bits_set = WOLFSENTRY_ACTION_RES_CONNECT;
+ config.action_res_filter_bits_unset = WOLFSENTRY_ACTION_RES_CONNECT;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_eventconfig_check(&config));
+
+ memset(&config, 0, sizeof config);
+ config.action_res_bits_to_add = WOLFSENTRY_ACTION_RES_CONNECT;
+ config.action_res_bits_to_clear = WOLFSENTRY_ACTION_RES_CONNECT;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_eventconfig_check(&config));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_eventconfig_alignment(void) {
+ struct wolfsentry_eventconfig config;
+
+ /* Non-zero alignment with zero private-data size is contradictory. */
+ memset(&config, 0, sizeof config);
+ config.route_private_data_alignment = sizeof(void *);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_eventconfig_check(&config));
+
+ /* Alignment smaller than a pointer is rejected. */
+ memset(&config, 0, sizeof config);
+ config.route_private_data_size = 32;
+ config.route_private_data_alignment = 1;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_eventconfig_check(&config));
+
+ /* Non-power-of-two alignment is rejected. */
+ memset(&config, 0, sizeof config);
+ config.route_private_data_size = 64;
+ config.route_private_data_alignment = (3 * sizeof(void *));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_eventconfig_check(&config));
+
+ /* Alignment larger than the private-data size is rejected. */
+ memset(&config, 0, sizeof config);
+ config.route_private_data_size = sizeof(void *);
+ config.route_private_data_alignment = (2 * sizeof(void *));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_eventconfig_check(&config));
+
+ /* A private-data size beyond the data_addr_offset field is rejected. */
+ memset(&config, 0, sizeof config);
+ config.route_private_data_size = (size_t)0x10000;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(NUMERIC_ARG_TOO_BIG, wolfsentry_eventconfig_check(&config));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_eventconfig_derogatory_threshold(void) {
+ /* derogatory_threshold_for_penaltybox must fit the uint16_t per-route
+ * derogatory_count counter.
+ */
+ struct wolfsentry_eventconfig config;
+
+ memset(&config, 0, sizeof config);
+ /* the route's derogatory_count counter is 16-bit; only test the
+ * out-of-range rejection when the configured field can exceed that.
+ */
+ if (MAX_UINT_OF(config.derogatory_threshold_for_penaltybox) > (uint64_t)0xffffU) {
+ config.derogatory_threshold_for_penaltybox = (wolfsentry_hitcount_t)0x10000U;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_eventconfig_check(&config));
+ }
+
+ config.derogatory_threshold_for_penaltybox = (wolfsentry_hitcount_t)0xffffU;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_check(&config));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#ifdef WOLFSENTRY_ERROR_STRINGS
+static wolfsentry_errcode_t test_user_string_table_bounds(void) {
+ /* Guard that the user-defined success/error string tables are sized to
+ * hold their highest valid index.
+ */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_error_string_set((enum wolfsentry_error_id)WOLFSENTRY_ERROR_ID_MAX, "boundary success string"));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ITEM_ALREADY_PRESENT, wolfsentry_user_error_string_set((enum wolfsentry_error_id)WOLFSENTRY_ERROR_ID_MAX, "boundary success string"));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_error_string_set((enum wolfsentry_error_id)(-WOLFSENTRY_ERROR_ID_MAX), "boundary error string"));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ITEM_ALREADY_PRESENT, wolfsentry_user_error_string_set((enum wolfsentry_error_id)(-WOLFSENTRY_ERROR_ID_MAX), "boundary error string"));
+
+ WOLFSENTRY_RETURN_OK;
+}
+#endif /* WOLFSENTRY_ERROR_STRINGS */
+
#endif /* TEST_INIT */
static __attribute_maybe_unused__ wolfsentry_errcode_t my_addr_family_parser(
@@ -1387,132 +1525,208 @@ TEST_SKIP(test_rw_locks)
#endif /* WOLFSENTRY_THREADSAFE */
-#endif /* TEST_RWLOCKS */
+#if defined(WOLFSENTRY_THREADSAFE)
-#ifdef TEST_STATIC_ROUTES
+static int test_rw_locks_auto_downgrade(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_rwlock *lock;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ struct wolfsentry_eventconfig config = { 32, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
-static wolfsentry_errcode_t replace_rule_transactionally(
- WOLFSENTRY_CONTEXT_ARGS_IN,
- const char *event_label,
- int event_label_len,
- const struct wolfsentry_sockaddr *del_remote,
- const struct wolfsentry_sockaddr *del_local,
- wolfsentry_route_flags_t del_flags,
- const struct wolfsentry_sockaddr *ins_remote,
- const struct wolfsentry_sockaddr *ins_local,
- wolfsentry_route_flags_t ins_flags,
- wolfsentry_ent_id_t *ins_id,
- wolfsentry_action_res_t *action_results)
-{
- wolfsentry_errcode_t ret;
- struct wolfsentry_context *new_wolfsentry_ctx = NULL;
- int n_deleted;
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_PROMOTABLE_OR_RETURN();
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_init_ex(wolfsentry_build_settings,
+ WOLFSENTRY_TEST_HPI,
+ thread,
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_alloc(wolfsentry_get_hpi(wolfsentry), thread, &lock, WOLFSENTRY_LOCK_FLAG_NONE));
- ret = wolfsentry_context_clone(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &new_wolfsentry_ctx,
- WOLFSENTRY_CLONE_FLAG_NONE);
- if (ret < 0)
- goto out;
+ /* promote through a redeemed reservation, which records the promotion depth. */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(LOCK_OK_AND_GOT_RESV, wolfsentry_lock_shared(lock, thread, WOLFSENTRY_LOCK_FLAG_GET_RESERVATION_TOO));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_shared2mutex_redeem(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_mutex(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(HAVE_MUTEX, wolfsentry_lock_have_mutex(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
- ret = wolfsentry_route_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX(new_wolfsentry_ctx),
- NULL /* caller_arg */,
- del_remote,
- del_local,
- del_flags,
- event_label,
- event_label_len,
- action_results,
- &n_deleted);
- if (ret < 0)
- goto out;
+ /* unwinding to the promotion depth with AUTO_DOWNGRADE must leave a shared lock. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_unlock(lock, thread, WOLFSENTRY_LOCK_FLAG_AUTO_DOWNGRADE));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(HAVE_READ_LOCK, wolfsentry_lock_have_shared(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(LACKING_MUTEX, wolfsentry_lock_have_mutex(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FALSE(thread->shared_count == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(thread->mutex_and_reservation_count == 0);
+ WOLFSENTRY_EXIT_ON_FALSE(thread->tracked_shared_lock == lock);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_unlock(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_destroy(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
- ret = wolfsentry_route_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX(new_wolfsentry_ctx),
- NULL /* caller_arg */,
- ins_remote,
- ins_local,
- ins_flags,
- event_label,
- event_label_len,
- ins_id,
- action_results);
- if (ret < 0)
- goto out;
+ /* same again, but downgrade with a new reservation and redeem it. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_init(wolfsentry_get_hpi(wolfsentry), thread, lock, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(LOCK_OK_AND_GOT_RESV, wolfsentry_lock_shared(lock, thread, WOLFSENTRY_LOCK_FLAG_GET_RESERVATION_TOO));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_shared2mutex_redeem(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_mutex(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(LOCK_OK_AND_GOT_RESV, wolfsentry_lock_unlock(lock, thread, WOLFSENTRY_LOCK_FLAG_AUTO_DOWNGRADE | WOLFSENTRY_LOCK_FLAG_GET_RESERVATION_TOO));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(HAVE_READ_LOCK, wolfsentry_lock_have_shared(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_have_shared2mutex_reservation(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_shared2mutex_redeem(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(HAVE_MUTEX, wolfsentry_lock_have_mutex(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_unlock(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
- ret = wolfsentry_context_exchange(WOLFSENTRY_CONTEXT_ARGS_OUT, new_wolfsentry_ctx);
+ /* unwinding past the promotion depth without AUTO_DOWNGRADE stays exclusive. */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(LOCK_OK_AND_GOT_RESV, wolfsentry_lock_shared(lock, thread, WOLFSENTRY_LOCK_FLAG_GET_RESERVATION_TOO));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_shared2mutex_redeem(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_mutex(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_unlock(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(HAVE_MUTEX, wolfsentry_lock_have_mutex(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_unlock(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
-out:
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_free(&lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(&wolfsentry, thread));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- if (new_wolfsentry_ctx != NULL) {
- WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&new_wolfsentry_ctx)));
- }
+ WOLFSENTRY_RETURN_OK;
+}
+static void *wr_timeout_routine(struct rwlock_args *args) {
+ WOLFSENTRY_THREAD_HEADER(WOLFSENTRY_THREAD_FLAG_NONE);
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_GET_ERROR);
+ INCREMENT_PHASE(args);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(TIMED_OUT, wolfsentry_lock_mutex_timed(args->lock, thread, args->max_wait, WOLFSENTRY_LOCK_FLAG_NONE));
+ INCREMENT_PHASE(args);
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ return 0;
+}
- WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
+static int test_rw_locks_reader_wakeup(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_rwlock *lock;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ struct wolfsentry_eventconfig config = { 32, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+ volatile int measured_sequence[8], measured_sequence_i = 0;
+ pthread_t reader_thread, writer_thread;
+ struct rwlock_args reader_args, writer_args;
+ int reader_phase;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ (void)alarm(2);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_init_ex(wolfsentry_build_settings,
+ WOLFSENTRY_TEST_HPI,
+ thread,
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_alloc(wolfsentry_get_hpi(wolfsentry), thread, &lock, WOLFSENTRY_LOCK_FLAG_NONE));
+
+ memset(&reader_args, 0, sizeof reader_args);
+ reader_args.wolfsentry = wolfsentry;
+ reader_args.measured_sequence = measured_sequence;
+ reader_args.measured_sequence_i = &measured_sequence_i;
+ reader_args.lock = lock;
+ writer_args = reader_args;
+ reader_args.thread_id = 1;
+ reader_args.max_wait = -1;
+ writer_args.thread_id = 2;
+ writer_args.max_wait = MAX_WAIT;
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_mutex_init(&reader_args.thread_phase_lock, NULL));
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_mutex_init(&writer_args.thread_phase_lock, NULL));
+
+ /* hold the lock shared twice, so the first unlock leaves a shared holder in place. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_shared(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_shared(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+
+ /* a timed writer queues, which forces the subsequent reader to queue behind it. */
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_create(&writer_thread, 0 /* attr */, (void *(*)(void *))wr_timeout_routine, (void *)&writer_args));
+ WAIT_FOR_PHASE(writer_args, 1);
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_kill(writer_thread, 0));
+ usleep(10000);
+
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_create(&reader_thread, 0 /* attr */, (void *(*)(void *))rd_routine, (void *)&reader_args));
+ WAIT_FOR_PHASE(reader_args, 1);
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_kill(reader_thread, 0));
+ usleep(10000);
+
+ /* the writer times out and dequeues itself, leaving the reader queued. */
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_join(writer_thread, 0 /* retval */));
+
+ /* the reader must still be parked, not already holding the lock, else the scenario was not reached. */
+ usleep(10000);
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_mutex_lock(&reader_args.thread_phase_lock));
+ reader_phase = reader_args.thread_phase;
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_mutex_unlock(&reader_args.thread_phase_lock));
+ WOLFSENTRY_EXIT_ON_FALSE(reader_phase == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ATOMIC_LOAD(measured_sequence_i) == 0);
+
+ /* this unlock releases the queued reader, and must post exactly one wakeup. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_unlock(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_join(reader_thread, 0 /* retval */));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ATOMIC_LOAD(measured_sequence_i) == 2);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_unlock(lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+
+ /* a surplus wakeup token is detected as an inconsistency at destroy time. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_lock_free(&lock, thread, WOLFSENTRY_LOCK_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_mutex_destroy(&reader_args.thread_phase_lock));
+ WOLFSENTRY_EXIT_ON_FAILURE_PTHREAD(pthread_mutex_destroy(&writer_args.thread_phase_lock));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(&wolfsentry, thread));
+
+ (void)alarm(0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
}
-static int test_static_routes(void) {
+#else
+
+TEST_SKIP(test_rw_locks_auto_downgrade)
+TEST_SKIP(test_rw_locks_reader_wakeup)
+
+#endif /* WOLFSENTRY_THREADSAFE */
+static int test_object_release_tableless(void) {
struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *main_routes;
+ const struct wolfsentry_route *fallthrough_route;
+ struct wolfsentry_route *route_ref = NULL;
wolfsentry_action_res_t action_results;
- int n_deleted;
- wolfsentry_ent_id_t id;
- wolfsentry_route_flags_t inexact_matches;
-
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
struct {
struct wolfsentry_sockaddr sa;
byte addr_buf[4];
- } remote, local, remote_wildcard, local_wildcard;
-
- struct wolfsentry_eventconfig config = {
+ } remote, local;
#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
- .route_private_data_size = PRIVATE_DATA_SIZE,
- .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT,
- .max_connection_count = 10,
- .derogatory_threshold_for_penaltybox = 4,
- .penaltybox_duration = 1, /* denominated in seconds when passing to wolfsentry_init(). */
- .route_idle_time_for_purge = 0,
- .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
#else
- PRIVATE_DATA_SIZE,
- PRIVATE_DATA_ALIGNMENT,
- 10,
- 4,
- 1, /* denominated in seconds when passing to wolfsentry_init(). */
- 0,
- WOLFSENTRY_EVENTCONFIG_FLAG_NONE,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0
+ struct wolfsentry_eventconfig config = { 32, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
#endif
- };
- wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS, flags_wildcard;
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- struct wolfsentry_route_table *main_routes;
- struct wolfsentry_route *route_ref;
- wolfsentry_ent_id_t route_id;
- int prefixlen;
- byte *private_data;
- size_t private_data_size;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_init_ex(wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_init_ex(
- wolfsentry_build_settings,
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
- &config,
- &wolfsentry,
- WOLFSENTRY_INIT_FLAG_NONE));
+ /* the fallthrough route is never linked into a table, but is documented as releasable. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_fallthrough_route_get(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &fallthrough_route));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry_get_object_type(fallthrough_route) == WOLFSENTRY_OBJECT_TYPE_UNINITED);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_object_release(WOLFSENTRY_CONTEXT_ARGS_OUT, (void *)fallthrough_route, NULL /* action_results */));
+ /* a checked-out route that has since been deleted is table-less too, and must be rejected, not dereferenced. */
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
remote.sa.sa_family = local.sa.sa_family = AF_INET;
remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
remote.sa.sa_port = 12345;
@@ -1522,372 +1736,784 @@ static int test_static_routes(void) {
memcpy(remote.sa.addr,"\0\1\2\3",sizeof remote.addr_buf);
memcpy(local.sa.addr,"\377\376\375\374",sizeof local.addr_buf);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert_and_check_out(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label */, 0 /* event_label_len */, &route_ref, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry_get_object_type(route_ref) == WOLFSENTRY_OBJECT_TYPE_ROUTE);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, wolfsentry_get_object_id(route_ref), 0 /* trigger_label */, 0 /* trigger_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry_get_object_type(route_ref) == WOLFSENTRY_OBJECT_TYPE_UNINITED);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(INVALID_ARG, wolfsentry_object_release(WOLFSENTRY_CONTEXT_ARGS_OUT, route_ref, NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route_ref, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
+#if defined(WOLFSENTRY_THREADSAFE)
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_FALLTHROUGH));
+static int test_context_clone_readonly_thread(void) {
+ struct wolfsentry_context *wolfsentry, *clone = NULL;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ struct wolfsentry_eventconfig config = { 32, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_init_ex(wolfsentry_build_settings,
+ WOLFSENTRY_TEST_HPI,
+ thread,
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
+ /* a read-only thread can clone, but cannot take the clone's mutex, so the clone must be torn down. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_set_thread_readonly(thread));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(NOT_PERMITTED, wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &clone, WOLFSENTRY_CLONE_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FALSE(clone == NULL);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_set_thread_readwrite(thread));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_FALLTHROUGH));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(&wolfsentry, thread));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
-#ifdef WOLFSENTRY_UNITTEST_BENCHMARKS
- {
- struct timespec start_at, end_at;
- uint64_t start_at_cycles, end_at_cycles;
- int i;
- double ns_per_call, cycles_per_call;
+#else
- WOLFSENTRY_EXIT_ON_SYSFAILURE(clock_gettime(CLOCK_MONOTONIC, &start_at));
- start_at_cycles = get_intel_cycles();
- for (i=0; i<1000000; ++i) {
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
- }
- end_at_cycles = get_intel_cycles();
- WOLFSENTRY_EXIT_ON_SYSFAILURE(clock_gettime(CLOCK_MONOTONIC, &end_at));
- ns_per_call = ((double)(end_at.tv_sec - start_at.tv_sec) * 1000000000.0 + (double)(end_at.tv_nsec - start_at.tv_nsec)) / (double)i;
- cycles_per_call = (double)(end_at_cycles - start_at_cycles) / (double)i;
- printf("benchmark wolfsentry_route_event_dispatch() with empty route table: %.2f ns/call %.2f cycles/call\n", ns_per_call, cycles_per_call);
-#ifdef WOLFSENTRY_MAX_CYCLES_PER_CALL_EMPTY_TABLE
- if (cycles_per_call > (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_EMPTY_TABLE) {
- (void)fprintf(stderr, "benchmark wolfsentry_route_event_dispatch() with empty route table: measured %.2f cycles/call exceeds max %.2f\n", cycles_per_call, (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_EMPTY_TABLE);
- WOLFSENTRY_EXIT_ON_TRUE(cycles_per_call > (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_EMPTY_TABLE);
- }
+TEST_SKIP(test_context_clone_readonly_thread)
+
+#endif /* WOLFSENTRY_THREADSAFE */
+
+static int test_context_clone_fallthrough(void) {
+ struct wolfsentry_context *wolfsentry, *clone = NULL;
+ struct wolfsentry_route_table *clone_routes;
+ const struct wolfsentry_route *fallthrough_route;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ struct wolfsentry_eventconfig config = { 32, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
#endif
- }
-#endif /* WOLFSENTRY_UNITTEST_BENCHMARKS */
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_init_ex(wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- /* non-decisional default_policy values (STOP/ERROR alone) must be rejected. */
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- INVALID_ARG,
- wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_STOP));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- INVALID_ARG,
- wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ERROR));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- INVALID_ARG,
- wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_STOP | WOLFSENTRY_ACTION_RES_ERROR));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ /* a full-flush clone starts with an empty route table, which must still carry a fallthrough route. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &clone, WOLFSENTRY_CLONE_FLAG_AS_AT_CREATION));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(clone), &clone_routes));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_fallthrough_route_get(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(clone), clone_routes, &fallthrough_route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_object_release(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(clone), (void *)fallthrough_route, NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&clone)));
- memcpy(remote.sa.addr,"\4\5\6\7",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+static wolfsentry_errcode_t failing_interval_from_seconds(time_t howlong_secs, long howlong_nsecs, wolfsentry_time_t *howlong) {
+ (void)howlong_secs;
+ (void)howlong_nsecs;
+ (void)howlong;
+ WOLFSENTRY_ERROR_RETURN(NUMERIC_ARG_TOO_BIG);
+}
-#if 0
- puts("table after first 2 inserts:");
- for (struct wolfsentry_route *i = (struct wolfsentry_route *)wolfsentry->routes.header.head;
- i;
- i = (struct wolfsentry_route *)(i->header.next))
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_render(i, stdout));
- putchar('\n');
+static int test_init_interval_conversion_failure(void) {
+ struct wolfsentry_context *wolfsentry, *wolfsentry2 = NULL;
+ struct wolfsentry_host_platform_interface hpi;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10, .penaltybox_duration = 1 };
+#else
+ struct wolfsentry_eventconfig config = { 32, 0, 10, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
#endif
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
-
- WOLFSENTRY_EXIT_ON_SUCCESS(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_init_ex(wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
- WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+ /* borrow the working platform interface, then make the seconds conversion fail. */
+ hpi = *wolfsentry_get_hpi(wolfsentry);
+ hpi.timecbs.interval_from_seconds = failing_interval_from_seconds;
- WOLFSENTRY_EXIT_ON_SUCCESS(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
+ /* a rejected duration must fail initialization and leave no context behind. */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(NUMERIC_ARG_TOO_BIG, wolfsentry_init_ex(wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&hpi),
+ &config,
+ &wolfsentry2,
+ WOLFSENTRY_INIT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry2 == NULL);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED);
- memcpy(remote.sa.addr,"\3\4\5\6",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED);
- memcpy(remote.sa.addr,"\2\3\4\5",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
+static int test_context_exchange_default_policy(void) {
+ struct wolfsentry_context *wolfsentry, *clone = NULL;
+ wolfsentry_action_res_t policy;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ struct wolfsentry_eventconfig config = { 32, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_init_ex(wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_SUCCESS(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_REJECT));
- flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+ /* stage a new policy in a clone, then swap it in, as a config reload does. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &clone, WOLFSENTRY_CLONE_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(clone), WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_exchange(WOLFSENTRY_CONTEXT_ARGS_OUT, clone));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_get(WOLFSENTRY_CONTEXT_ARGS_OUT, &policy));
+ WOLFSENTRY_EXIT_ON_FALSE(policy == WOLFSENTRY_ACTION_RES_ACCEPT);
+ /* the lock-free dispatch fallback reads the context-level copy, which must follow the swap. */
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->default_policy == WOLFSENTRY_ACTION_RES_ACCEPT);
+ WOLFSENTRY_EXIT_ON_FALSE(clone->default_policy == WOLFSENTRY_ACTION_RES_REJECT);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&clone)));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_reference(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- &remote.sa,
- &local.sa,
- flags,
- 0 /* event_label_len */,
- 0 /* event_label */,
- 1 /* exact_p */,
- &inexact_matches,
- &route_ref));
+#endif /* TEST_RWLOCKS */
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+#ifdef TEST_STATIC_ROUTES
- WOLFSENTRY_EXIT_ON_FALSE(wolfsentry_get_object_type(route_ref) == WOLFSENTRY_OBJECT_TYPE_ROUTE);
+static wolfsentry_errcode_t test_ll_insert_before_head_links(void) {
+ struct wolfsentry_list_header list;
+ struct wolfsentry_list_ent_header a, b, c, d;
+ const struct wolfsentry_list_ent_header *cursor;
+ wolfsentry_hitcount_t n;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_private_data(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- route_ref,
- (void **)&private_data,
- &private_data_size));
+ WOLFSENTRY_LIST_HEADER_RESET(list);
+ wolfsentry_list_ent_append(&list, &a);
+ wolfsentry_list_ent_append(&list, &b);
+ wolfsentry_list_ent_append(&list, &c);
- if (private_data_size < PRIVATE_DATA_SIZE) {
- printf("private_data_size is " SIZET_FMT " but expected %d.\n",private_data_size,PRIVATE_DATA_SIZE);
- WOLFSENTRY_ERROR_RETURN(NOT_OK);
- }
- if ((PRIVATE_DATA_ALIGNMENT > 0) && ((uintptr_t)private_data % (uintptr_t)PRIVATE_DATA_ALIGNMENT)) {
- printf("private_data (%p) is not aligned to %d.\n", private_data, PRIVATE_DATA_ALIGNMENT);
- WOLFSENTRY_ERROR_RETURN(NOT_OK);
+ /* unlink a middle entry and reinsert it before the head, as purge-list
+ * refresh does; the unlink leaves its old links, so the head insertion
+ * must clear prev.
+ */
+ wolfsentry_list_ent_delete(&list, &b);
+ wolfsentry_list_ent_insert_before(&list, list.head, &b);
+
+ if (list.head != &b)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ if (list.head->prev != NULL)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ if (list.tail->next != NULL)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+
+ wolfsentry_list_ent_insert_before(&list, list.head, &d);
+ if ((list.head != &d) || (list.head->prev != NULL))
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+
+ n = 0;
+ for (cursor = list.head; cursor != NULL; cursor = cursor->next) {
+ ++n;
+ if (n > list.len)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ if ((cursor->next == NULL) && (cursor != list.tail))
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
}
+ if (n != list.len)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
- {
- byte *i;
- const byte *i_end;
- for (i = private_data, i_end = (private_data + private_data_size); i < i_end; ++i)
- *i = 'x';
+ n = 0;
+ for (cursor = list.tail; cursor != NULL; cursor = cursor->prev) {
+ ++n;
+ if (n > list.len)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ if ((cursor->prev == NULL) && (cursor != list.head))
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
}
+ if (n != list.len)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
-#if 0
- puts("table after deleting 4.5.6.7 and inserting 3 more:");
- for (struct wolfsentry_route *i = (struct wolfsentry_route *)wolfsentry->routes.header.head;
- i;
- i = (struct wolfsentry_route *)(i->header.next))
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_render(i, stdout));
-#endif
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route_ref, &action_results));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+static wolfsentry_errcode_t replace_rule_transactionally(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const char *event_label,
+ int event_label_len,
+ const struct wolfsentry_sockaddr *del_remote,
+ const struct wolfsentry_sockaddr *del_local,
+ wolfsentry_route_flags_t del_flags,
+ const struct wolfsentry_sockaddr *ins_remote,
+ const struct wolfsentry_sockaddr *ins_local,
+ wolfsentry_route_flags_t ins_flags,
+ wolfsentry_ent_id_t *ins_id,
+ wolfsentry_action_res_t *action_results)
+{
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_context *new_wolfsentry_ctx = NULL;
+ int n_deleted;
- /* now test basic eventless dispatch using exact-match ents in the static table. */
+ WOLFSENTRY_PROMOTABLE_OR_RETURN();
- WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ ret = wolfsentry_context_clone(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &new_wolfsentry_ctx,
+ WOLFSENTRY_CLONE_FLAG_NONE);
+ if (ret < 0)
+ goto out;
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
- memcpy(remote.sa.addr,"\3\4\5\6",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
+ ret = wolfsentry_route_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(new_wolfsentry_ctx),
+ NULL /* caller_arg */,
+ del_remote,
+ del_local,
+ del_flags,
+ event_label,
+ event_label_len,
+ action_results,
+ &n_deleted);
+ if (ret < 0)
+ goto out;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
+ ret = wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(new_wolfsentry_ctx),
+ NULL /* caller_arg */,
+ ins_remote,
+ ins_local,
+ ins_flags,
+ event_label,
+ event_label_len,
+ ins_id,
+ action_results);
+ if (ret < 0)
+ goto out;
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == 0);
+ ret = wolfsentry_context_exchange(WOLFSENTRY_CONTEXT_ARGS_OUT, new_wolfsentry_ctx);
- flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+out:
- WOLFSENTRY_EXIT_ON_FALSE(
- WOLFSENTRY_SUCCESS_CODE_IS(
- wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results),
- USED_FALLBACK));
- WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == (WOLFSENTRY_ROUTE_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD));
+ if (new_wolfsentry_ctx != NULL) {
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&new_wolfsentry_ctx)));
+ }
- memcpy(remote.sa.addr,"\2\3\4\5",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_ERROR_UNLOCK_AND_RERETURN(ret);
+}
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == 0);
+static wolfsentry_errcode_t test_ll_insert_after_tail_links(void) {
+ struct wolfsentry_list_header list;
+ struct wolfsentry_list_ent_header a, b, c;
+ struct wolfsentry_list_ent_header sentinel;
+ const struct wolfsentry_list_ent_header *cursor;
+ wolfsentry_hitcount_t n;
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_LIST_HEADER_RESET(list);
+ wolfsentry_list_ent_append(&list, &a);
+ wolfsentry_list_ent_append(&list, &b);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == 0);
+ /* a freshly allocated entry carries an unrelated non-NULL next, which a
+ * correct tail insertion must overwrite with NULL.
+ */
+ sentinel.prev = NULL;
+ sentinel.next = NULL;
+ c.prev = &sentinel;
+ c.next = &sentinel;
+
+ wolfsentry_list_ent_insert_after(&list, list.tail, &c);
+
+ if (list.tail != &c)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ if (list.tail->next != NULL)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ if (list.head->prev != NULL)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+
+ n = 0;
+ for (cursor = list.head; cursor != NULL; cursor = cursor->next) {
+ ++n;
+ if (n > list.len)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ if ((cursor->next == NULL) && (cursor != list.tail))
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ }
+ if (n != list.len)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
- memcpy(remote.sa.addr,"\0\1\2\3",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\377\376\375\374",sizeof local.addr_buf);
- WOLFSENTRY_EXIT_ON_FALSE(
- WOLFSENTRY_SUCCESS_CODE_IS(
- wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results),
- USED_FALLBACK));
- WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == (WOLFSENTRY_ROUTE_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD));
+ n = 0;
+ for (cursor = list.tail; cursor != NULL; cursor = cursor->prev) {
+ ++n;
+ if (n > list.len)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ if ((cursor->prev == NULL) && (cursor != list.head))
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
+ }
+ if (n != list.len)
+ WOLFSENTRY_ERROR_RETURN(UNIT_TEST_FAILURE);
- flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == 0);
+static int test_static_routes(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ int n_deleted;
+ wolfsentry_ent_id_t id;
+ wolfsentry_route_flags_t inexact_matches;
- /* now test eventless dispatch using wildcard/prefix matches in the static table. */
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local, remote_wildcard, local_wildcard;
+ struct wolfsentry_eventconfig config = {
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ .route_private_data_size = PRIVATE_DATA_SIZE,
+ .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT,
+ .max_connection_count = 10,
+ .derogatory_threshold_for_penaltybox = 4,
+ .penaltybox_duration = 1, /* denominated in seconds when passing to wolfsentry_init(). */
+ .route_idle_time_for_purge = 0,
+ .flags = WOLFSENTRY_EVENTCONFIG_FLAG_NONE
+#else
+ PRIVATE_DATA_SIZE,
+ PRIVATE_DATA_ALIGNMENT,
+ 10,
+ 4,
+ 1, /* denominated in seconds when passing to wolfsentry_init(). */
+ 0,
+ WOLFSENTRY_EVENTCONFIG_FLAG_NONE,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+#endif
+ };
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED);
- WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
- WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
- memcpy(remote.sa.addr,"\4\5\6\7",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS, flags_wildcard;
- for (prefixlen = sizeof remote.addr_buf * BITS_PER_BYTE;
- prefixlen >= 8;
- --prefixlen) {
- remote.sa.addr_len = (wolfsentry_addr_bits_t)prefixlen;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ struct wolfsentry_route_table *main_routes;
+ struct wolfsentry_route *route_ref;
+ wolfsentry_ent_id_t route_id;
+ int prefixlen;
+ byte *private_data;
+ size_t private_data_size;
- remote.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_TRUE(prefixlen < (int)(sizeof remote.addr_buf * BITS_PER_BYTE) ? ! WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD) : WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD));
+ WOLFSENTRY_EXIT_ON_FAILURE(test_ll_insert_before_head_links());
+ WOLFSENTRY_EXIT_ON_FAILURE(test_ll_insert_after_tail_links());
- remote.sa.addr_len = (wolfsentry_addr_bits_t)prefixlen;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
- WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr,"\0\1\2\3",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\377\376\375\374",sizeof local.addr_buf);
- local.sa.addr_len = (wolfsentry_addr_bits_t)prefixlen;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
- local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_TRUE(prefixlen < (int)(sizeof local.addr_buf * BITS_PER_BYTE) ? ! WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD) : WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
- local.sa.addr_len = (wolfsentry_addr_bits_t)prefixlen;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
- WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
- }
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_FALLTHROUGH));
- remote_wildcard = remote;
- local_wildcard = local;
- flags_wildcard = flags;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_REJECT));
- remote_wildcard.sa.sa_port = 0;
- WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
- {
- struct wolfsentry_route *new_route = NULL;
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_FALLTHROUGH));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert_and_check_out(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &new_route, &action_results));
- id = wolfsentry_get_object_id(new_route);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, new_route, &action_results));
+#ifdef WOLFSENTRY_UNITTEST_BENCHMARKS
+ {
+ struct timespec start_at, end_at;
+ uint64_t start_at_cycles, end_at_cycles;
+ int i;
+ double ns_per_call, cycles_per_call;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
+ WOLFSENTRY_EXIT_ON_SYSFAILURE(clock_gettime(CLOCK_MONOTONIC, &start_at));
+ start_at_cycles = get_intel_cycles();
+ for (i=0; i<1000000; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ }
+ end_at_cycles = get_intel_cycles();
+ WOLFSENTRY_EXIT_ON_SYSFAILURE(clock_gettime(CLOCK_MONOTONIC, &end_at));
+ ns_per_call = ((double)(end_at.tv_sec - start_at.tv_sec) * 1000000000.0 + (double)(end_at.tv_nsec - start_at.tv_nsec)) / (double)i;
+ cycles_per_call = (double)(end_at_cycles - start_at_cycles) / (double)i;
+ printf("benchmark wolfsentry_route_event_dispatch() with empty route table: %.2f ns/call %.2f cycles/call\n", ns_per_call, cycles_per_call);
+#ifdef WOLFSENTRY_MAX_CYCLES_PER_CALL_EMPTY_TABLE
+ if (cycles_per_call > (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_EMPTY_TABLE) {
+ (void)fprintf(stderr, "benchmark wolfsentry_route_event_dispatch() with empty route table: measured %.2f cycles/call exceeds max %.2f\n", cycles_per_call, (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_EMPTY_TABLE);
+ WOLFSENTRY_EXIT_ON_TRUE(cycles_per_call > (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_EMPTY_TABLE);
+ }
+#endif
}
+#endif /* WOLFSENTRY_UNITTEST_BENCHMARKS */
- local.sa.sa_port = 8765;
- WOLFSENTRY_EXIT_ON_FALSE(
- WOLFSENTRY_SUCCESS_CODE_IS(
- wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results),
- USED_FALLBACK));
- WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == (WOLFSENTRY_ROUTE_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD));
- local.sa.sa_port = local_wildcard.sa.sa_port;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
+ /* non-decisional default_policy values (STOP/ERROR alone) must be rejected. */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_STOP));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ERROR));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_STOP | WOLFSENTRY_ACTION_RES_ERROR));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, route_id, NULL /* event_label */, 0 /* event_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+ memcpy(remote.sa.addr,"\4\5\6\7",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
- remote_wildcard = remote;
- local_wildcard = local;
- flags_wildcard = flags;
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
- local_wildcard.sa.sa_port = 0;
- WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+#if 0
+ puts("table after first 2 inserts:");
+ for (struct wolfsentry_route *i = (struct wolfsentry_route *)wolfsentry->routes.header.head;
+ i;
+ i = (struct wolfsentry_route *)(i->header.next))
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_render(i, stdout));
+ putchar('\n');
+#endif
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD));
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
- /* make sure the fixup (clamp_wildcard_fields_to_zero()) works as expected. */
- local_wildcard.sa.sa_port = 123;
+ WOLFSENTRY_EXIT_ON_SUCCESS(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+ WOLFSENTRY_EXIT_ON_SUCCESS(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
- remote_wildcard = remote;
- local_wildcard = local;
- flags_wildcard = flags;
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED);
+ memcpy(remote.sa.addr,"\3\4\5\6",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
- remote_wildcard.sa.sa_port = local_wildcard.sa.sa_port = 0;
- remote_wildcard.sa.sa_proto = local_wildcard.sa.sa_proto = 0;
- WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD);
- WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD);
- WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED);
+ memcpy(remote.sa.addr,"\2\3\4\5",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
- &route_id, &inexact_matches, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
- WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+ WOLFSENTRY_EXIT_ON_SUCCESS(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
- remote_wildcard = remote;
- local_wildcard = local;
+ flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ &remote.sa,
+ &local.sa,
+ flags,
+ 0 /* event_label_len */,
+ 0 /* event_label */,
+ 1 /* exact_p */,
+ &inexact_matches,
+ &route_ref));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry_get_object_type(route_ref) == WOLFSENTRY_OBJECT_TYPE_ROUTE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_private_data(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route_ref,
+ (void **)&private_data,
+ &private_data_size));
+
+ if (private_data_size < PRIVATE_DATA_SIZE) {
+ printf("private_data_size is " SIZET_FMT " but expected %d.\n",private_data_size,PRIVATE_DATA_SIZE);
+ WOLFSENTRY_ERROR_RETURN(NOT_OK);
+ }
+ if ((PRIVATE_DATA_ALIGNMENT > 0) && ((uintptr_t)private_data % (uintptr_t)PRIVATE_DATA_ALIGNMENT)) {
+ printf("private_data (%p) is not aligned to %d.\n", private_data, PRIVATE_DATA_ALIGNMENT);
+ WOLFSENTRY_ERROR_RETURN(NOT_OK);
+ }
+
+ {
+ byte *i;
+ const byte *i_end;
+ for (i = private_data, i_end = (private_data + private_data_size); i < i_end; ++i)
+ *i = 'x';
+ }
+
+#if 0
+ puts("table after deleting 4.5.6.7 and inserting 3 more:");
+ for (struct wolfsentry_route *i = (struct wolfsentry_route *)wolfsentry->routes.header.head;
+ i;
+ i = (struct wolfsentry_route *)(i->header.next))
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_render(i, stdout));
+#endif
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route_ref, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+
+ /* now test basic eventless dispatch using exact-match ents in the static table. */
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+ memcpy(remote.sa.addr,"\3\4\5\6",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == 0);
+
+ flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_SUCCESS_CODE_IS(
+ wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results),
+ USED_FALLBACK));
+ WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == (WOLFSENTRY_ROUTE_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD));
+
+ memcpy(remote.sa.addr,"\2\3\4\5",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == 0);
+
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == 0);
+
+ memcpy(remote.sa.addr,"\0\1\2\3",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\377\376\375\374",sizeof local.addr_buf);
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_SUCCESS_CODE_IS(
+ wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results),
+ USED_FALLBACK));
+ WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == (WOLFSENTRY_ROUTE_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD));
+
+ flags |= WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == 0);
+
+
+ /* now test eventless dispatch using wildcard/prefix matches in the static table. */
+
+
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED);
+ WOLFSENTRY_CLEAR_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+ memcpy(remote.sa.addr,"\4\5\6\7",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\373\372\371\370",sizeof local.addr_buf);
+
+ for (prefixlen = sizeof remote.addr_buf * BITS_PER_BYTE;
+ prefixlen >= 8;
+ --prefixlen) {
+ remote.sa.addr_len = (wolfsentry_addr_bits_t)prefixlen;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+
+ remote.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_TRUE(prefixlen < (int)(sizeof remote.addr_buf * BITS_PER_BYTE) ? ! WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD) : WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD));
+
+ remote.sa.addr_len = (wolfsentry_addr_bits_t)prefixlen;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
+ WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+
+
+ local.sa.addr_len = (wolfsentry_addr_bits_t)prefixlen;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+
+ local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_TRUE(prefixlen < (int)(sizeof local.addr_buf * BITS_PER_BYTE) ? ! WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD) : WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD));
+
+ local.sa.addr_len = (wolfsentry_addr_bits_t)prefixlen;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa, flags, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
+ WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+
+ }
+
+
+ remote_wildcard = remote;
+ local_wildcard = local;
+ flags_wildcard = flags;
+
+ remote_wildcard.sa.sa_port = 0;
+ WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD);
+
+ {
+ struct wolfsentry_route *new_route = NULL;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert_and_check_out(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &new_route, &action_results));
+
+ id = wolfsentry_get_object_id(new_route);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, new_route, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
+ }
+
+ local.sa.sa_port = 8765;
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_SUCCESS_CODE_IS(
+ wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results),
+ USED_FALLBACK));
+ WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == (WOLFSENTRY_ROUTE_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD));
+
+ local.sa.sa_port = local_wildcard.sa.sa_port;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, route_id, NULL /* event_label */, 0 /* event_label_len */, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+
+ remote_wildcard = remote;
+ local_wildcard = local;
+ flags_wildcard = flags;
+
+ local_wildcard.sa.sa_port = 0;
+ WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD));
+
+ /* make sure the fixup (clamp_wildcard_fields_to_zero()) works as expected. */
+ local_wildcard.sa.sa_port = 123;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
+ WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+
+
+ remote_wildcard = remote;
+ local_wildcard = local;
+ flags_wildcard = flags;
+
+ remote_wildcard.sa.sa_port = local_wildcard.sa.sa_port = 0;
+ remote_wildcard.sa.sa_proto = local_wildcard.sa.sa_proto = 0;
+ WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD);
+ WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD);
+ WOLFSENTRY_SET_BITS(flags_wildcard, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &id, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags, NULL /* event_label */, 0 /* event_label_len */, NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote_wildcard.sa, &local_wildcard.sa, flags_wildcard, 0 /* event_label_len */, 0 /* event_label */, &action_results, &n_deleted));
+ WOLFSENTRY_EXIT_ON_FALSE(n_deleted == 1);
+
+
+ remote_wildcard = remote;
+ local_wildcard = local;
flags_wildcard = flags;
local_wildcard.sa.addr_len = 0;
@@ -2832,59 +3458,37 @@ static int test_static_routes(void) {
WOLFSENTRY_RETURN_OK;
}
-#endif /* TEST_STATIC_ROUTES */
+struct regression_sockaddr_v4 {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+};
-#ifdef TEST_DYNAMIC_RULES
-
-static wolfsentry_errcode_t wolfsentry_action_dummy_callback(
- WOLFSENTRY_CONTEXT_ARGS_IN,
- const struct wolfsentry_action *action,
- void *handler_context,
- void *caller_arg,
- const struct wolfsentry_event *event,
- wolfsentry_action_type_t action_type,
- const struct wolfsentry_route *target_route,
- struct wolfsentry_route_table *route_table,
- struct wolfsentry_route *rule_route,
- wolfsentry_action_res_t *action_results)
-{
- WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
- (void)action;
- (void)handler_context;
- (void)caller_arg;
- (void)event;
- (void)action_type;
- (void)target_route;
- (void)route_table;
- (void)rule_route;
- (void)action_results;
-
- WOLFSENTRY_RETURN_OK;
+static void regression_sockaddr_v4_init(struct regression_sockaddr_v4 *sa, const byte *addr, wolfsentry_port_t port, wolfsentry_proto_t proto, byte interface) {
+ memset(sa, 0, sizeof *sa);
+ sa->sa.sa_family = AF_INET;
+ sa->sa.sa_proto = proto;
+ sa->sa.sa_port = port;
+ sa->sa.addr_len = sizeof sa->addr_buf * BITS_PER_BYTE;
+ sa->sa.interface = interface;
+ memcpy(sa->sa.addr, addr, sizeof sa->addr_buf);
}
+#define REGRESSION_AF_WILDCARD_FLAGS (WOLFSENTRY_ROUTE_FLAG_SA_FAMILY_WILDCARD | \
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD | \
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD | \
+ WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD | \
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD | \
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD | \
+ WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD | \
+ WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD)
-static int test_dynamic_rules(void) {
-
+static int test_route_lookup_af_wildcard_span_terminates(void) {
struct wolfsentry_context *wolfsentry;
-#if 0
wolfsentry_action_res_t action_results;
- int n_deleted;
- int ret;
- struct {
- struct wolfsentry_sockaddr sa;
- byte addr_buf[4];
- } src, dst;
-#endif
-
- wolfsentry_ent_id_t id;
-
-#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
- static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
- static const struct wolfsentry_eventconfig config2 = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 15 };
-#else
- static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- static const struct wolfsentry_eventconfig config2 = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-#endif
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_route_flags_t flags;
+ wolfsentry_ent_id_t id_inet, id_wild_in, id_wild_out, route_id;
+ struct regression_sockaddr_v4 remote, local, remote_wild, local_wild;
WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
@@ -2892,575 +3496,717 @@ static int test_dynamic_rules(void) {
wolfsentry_init_ex(
wolfsentry_build_settings,
WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
- &config,
+ NULL /* config */,
&wolfsentry,
WOLFSENTRY_INIT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ regression_sockaddr_v4_init(&remote, (const byte *)"\0\1\2\3", 12345, IPPROTO_TCP, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\377\376\375\374", 443, IPPROTO_TCP, 1);
+ flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "connect",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id_inet, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(
- WOLFSENTRY_ERROR_CODE_IS(
- wolfsentry_event_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_BUILTIN_LABEL_PREFIX "connect",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id),
- NOT_PERMITTED));
+ memset(&remote_wild, 0, sizeof remote_wild);
+ memset(&local_wild, 0, sizeof local_wild);
- /* track port scanning */
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "connection_refused",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
+ NULL /* caller_arg */,
+ &remote_wild.sa, &local_wild.sa,
+ REGRESSION_AF_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id_wild_in, &action_results));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "disconnect",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
+ NULL /* caller_arg */,
+ &remote_wild.sa, &local_wild.sa,
+ REGRESSION_AF_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id_wild_out, &action_results));
+
+ /* a port with no exact match forces the full reverse scan through the
+ * wildcard-AF span.
+ */
+ remote.sa.sa_port = 54321;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
+ wolfsentry_route_event_dispatch(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "authentication_succeeded",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id_wild_in);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(inexact_matches, WOLFSENTRY_ROUTE_FLAG_SA_FAMILY_WILDCARD));
+
+ remote.sa.sa_port = 12345;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
+ wolfsentry_route_event_dispatch(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "negotiation_abandoned",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
-
- {
- wolfsentry_ent_id_t auth_failed_id;
- struct wolfsentry_event *auth_failed_event;
- const struct wolfsentry_event *insertion_side_effect_demo_event;
- const char *insertion_side_effect_demo_label;
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id_inet);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "authentication_failed",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id_wild_out, NULL /* event_label */, 0 /* event_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id_wild_in, NULL /* event_label */, 0 /* event_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id_inet, NULL /* event_label */, 0 /* event_label_len */, &action_results));
- auth_failed_id = id;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "insertion_side_effect_demo",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_set_aux_event(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "authentication_failed",
- -1,
- "insertion_side_effect_demo",
- -1));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_get_reference(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "authentication_failed",
- -1,
- &auth_failed_event));
+static int test_route_lookup_exact_match_honors_exclude_reject(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_route_flags_t flags;
+ wolfsentry_ent_id_t id_pbox, route_id;
+ struct regression_sockaddr_v4 remote, local;
- WOLFSENTRY_EXIT_ON_FALSE(auth_failed_id == wolfsentry_get_object_id(auth_failed_event));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FALSE(
- insertion_side_effect_demo_event = wolfsentry_event_get_aux_event(auth_failed_event)
- );
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FALSE(
- insertion_side_effect_demo_label = wolfsentry_event_get_label(insertion_side_effect_demo_event)
- );
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(insertion_side_effect_demo_label, "insertion_side_effect_demo") == 0);
+ regression_sockaddr_v4_init(&remote, (const byte *)"\0\1\2\3", 12345, IPPROTO_TCP, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\377\376\375\374", 443, IPPROTO_TCP, 1);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_drop_reference(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- auth_failed_event,
- NULL /* action_results */));
- }
+ /* the parent-event wildcard makes the route key identical to the lookup
+ * target, so the exact-seek short circuit is the path under test.
+ */
+ flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS
+ | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "match_side_effect_demo",
- -1 /* label_len */,
- 10,
- NULL /* config */,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id_pbox, &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_SET_BITS(action_results, WOLFSENTRY_ACTION_RES_EXCLUDE_REJECT_ROUTES);
+ route_id = WOLFSENTRY_ENT_ID_NONE;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(
+ USED_FALLBACK,
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(route_id == id_pbox);
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_insert(
+ wolfsentry_route_event_dispatch(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "deletion_side_effect_demo",
- -1 /* label_len */,
- 10,
- &config2,
- WOLFSENTRY_EVENT_FLAG_NONE,
- &id));
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id_pbox);
- {
- struct wolfsentry_eventconfig eventconfig;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id_pbox, NULL /* event_label */, 0 /* event_label_len */, &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_get_config(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "deletion_side_effect_demo",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &eventconfig));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == config2.route_private_data_size) &&
- (eventconfig.route_private_data_alignment == config2.route_private_data_alignment) &&
- (eventconfig.max_connection_count == config2.max_connection_count));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_update_config(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "deletion_side_effect_demo",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &config));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_get_config(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "deletion_side_effect_demo",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &eventconfig));
+static int test_route_lookup_action_res_filter_bits_unset(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_eventconfig event_config;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_route_flags_t flags;
+ wolfsentry_ent_id_t event_id, id_filtered, route_id;
+ struct regression_sockaddr_v4 remote, local;
+ static const char event_label[] = "filter-bits-unset-test";
- WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == config.route_private_data_size) &&
- (eventconfig.route_private_data_alignment == config.route_private_data_alignment) &&
- (eventconfig.max_connection_count == config.max_connection_count));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "deletion_side_effect_demo",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_SUCCESS(
- wolfsentry_event_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "deletion_side_effect_demo",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- NULL /* action_results */));
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &event_config));
+ event_config.action_res_filter_bits_unset = WOLFSENTRY_ACTION_RES_USER2;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_insert(
+ wolfsentry_event_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "insert_always",
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ event_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0 /* priority */,
+ &event_config,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &event_id));
- WOLFSENTRY_EXIT_ON_FALSE(
- WOLFSENTRY_ERROR_CODE_IS(
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_BUILTIN_LABEL_PREFIX "insert_always",
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id),
- NOT_PERMITTED));
+ regression_sockaddr_v4_init(&remote, (const byte *)"\0\1\2\3", 12345, IPPROTO_TCP, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\377\376\375\374", 443, IPPROTO_TCP, 1);
+ flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_insert(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "insert_alway",
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ event_label, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id_filtered, &action_results));
- {
- static char too_long_label[WOLFSENTRY_MAX_LABEL_BYTES + 2];
- memset(too_long_label, 'x', sizeof too_long_label - 1);
+ /* the forbidden bit is present, so the route must be skipped. */
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_SET_BITS(action_results, WOLFSENTRY_ACTION_RES_USER2);
+ route_id = WOLFSENTRY_ENT_ID_NONE;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(
+ USED_FALLBACK,
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(route_id == id_filtered);
- too_long_label[sizeof too_long_label - 1] = 0;
+ /* a different user bit is not forbidden, so the route must match. */
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_SET_BITS(action_results, WOLFSENTRY_ACTION_RES_USER1);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id_filtered);
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- STRING_ARG_TOO_LONG,
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- too_long_label,
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id_filtered, NULL /* event_label */, 0 /* event_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, event_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- STRING_ARG_TOO_LONG,
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- too_long_label,
- sizeof too_long_label - 1,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- too_long_label,
- sizeof too_long_label - 2,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- STRING_ARG_TOO_LONG,
- wolfsentry_action_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- too_long_label,
- sizeof too_long_label - 1,
- NULL /* action_results */));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- STRING_ARG_TOO_LONG,
- wolfsentry_action_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- too_long_label,
- -1,
- NULL /* action_results */));
+static int test_route_lookup_full_scan_honors_exclude_reject(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_route_flags_t flags;
+ wolfsentry_ent_id_t id_pbox, route_id;
+ struct regression_sockaddr_v4 remote, local;
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- INVALID_ARG,
- wolfsentry_action_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- NULL,
- -1,
- NULL /* action_results */));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- too_long_label[sizeof too_long_label - 2] = 0;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- ITEM_ALREADY_PRESENT,
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- too_long_label,
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- INVALID_ARG,
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- NULL,
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ regression_sockaddr_v4_init(&remote, (const byte *)"\0\1\2\0", 12345, IPPROTO_TCP, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\377\376\375\374", 443, IPPROTO_TCP, 1);
+ flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- INVALID_ARG,
- wolfsentry_action_insert(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- too_long_label,
- 0 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ /* a /24 rule never seeks to an exact hit for a /32 target, so the reverse
+ * scan is the path under test.
+ */
+ remote.sa.addr_len = 24;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags | WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id_pbox, &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- too_long_label,
- -1 /* label_len */,
- NULL /* action_results */));
- }
+ regression_sockaddr_v4_init(&remote, (const byte *)"\0\1\2\3", 12345, IPPROTO_TCP, 1);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_SET_BITS(action_results, WOLFSENTRY_ACTION_RES_EXCLUDE_REJECT_ROUTES);
+ route_id = WOLFSENTRY_ENT_ID_NONE;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(
+ USED_FALLBACK,
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(route_id == id_pbox);
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_insert(
+ wolfsentry_route_event_dispatch(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "set_connect_wildcards",
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id_pbox);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id_pbox, NULL /* event_label */, 0 /* event_label_len */, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t regression_failing_insert_action(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_context,
+ void *caller_arg,
+ const struct wolfsentry_event *event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)action;
+ (void)handler_context;
+ (void)caller_arg;
+ (void)event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+
+ WOLFSENTRY_ERROR_RETURN(NOT_PERMITTED);
+}
+
+static int test_route_insert_action_failure_detaches_route(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_eventconfig event_config;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t id, route_id;
+ wolfsentry_hitcount_t n_ents_before, purge_len_before;
+ const struct wolfsentry_route *last_af_wildcard_before;
+ wolfsentry_priority_t highest_priority_before;
+ struct regression_sockaddr_v4 remote, local, remote_wild, local_wild;
+ static const char event_label[] = "failing-insert-parent";
+ static const char action_label[] = "fail-on-insert";
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
WOLFSENTRY_EXIT_ON_FAILURE(
wolfsentry_action_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "set_connectionreset_wildcards",
- -1 /* label_len */,
+ action_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
+ regression_failing_insert_action,
NULL /* handler_context */,
&id));
+ /* a purge deadline puts the route on the purge list, which must be undone
+ * along with the wildcard-AF bookkeeping when the insert is rolled back.
+ */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &event_config));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 3600, 0, &event_config.route_idle_time_for_purge));
+
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_insert(
+ wolfsentry_event_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "increment_derogatory",
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
+ event_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0 /* priority */,
+ &event_config,
+ WOLFSENTRY_EVENT_FLAG_NONE,
&id));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_insert(
+ wolfsentry_event_action_append(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "increment_commendable",
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
- &id));
+ event_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_INSERT,
+ action_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ n_ents_before = wolfsentry->routes->header.n_ents;
+ purge_len_before = wolfsentry->routes->purge_list.len;
+ last_af_wildcard_before = wolfsentry->routes->last_af_wildcard_route;
+ highest_priority_before = wolfsentry->routes->highest_priority_route_in_table;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ memset(&remote_wild, 0, sizeof remote_wild);
+ memset(&local_wild, 0, sizeof local_wild);
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote_wild.sa, &local_wild.sa,
+ REGRESSION_AF_WILDCARD_FLAGS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ event_label, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->routes->header.n_ents == n_ents_before);
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->routes->purge_list.len == purge_len_before);
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->routes->last_af_wildcard_route == last_af_wildcard_before);
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->routes->highest_priority_route_in_table == highest_priority_before);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED));
+
+ /* the lookup and purge paths that consult the auxiliary structures must
+ * still work after the rollback.
+ */
+ regression_sockaddr_v4_init(&remote, (const byte *)"\0\1\2\3", 12345, IPPROTO_TCP, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\377\376\375\374", 443, IPPROTO_TCP, 1);
+ route_id = WOLFSENTRY_ENT_ID_NONE;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(
+ USED_FALLBACK,
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, event_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_action_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, action_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+static int test_route_insert_action_failure_releases_bitmask_af(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id;
+ wolfsentry_addr_family_t n_bitmask_afs_before;
+ struct wolfsentry_route_exports exp_route;
+ static const byte exp_remote_addr_and_mask[8] = { 0xC0, 0xA8, 0x01, 0x00, 0xFF, 0xFF, 0xFF, 0x00 };
+ static const char event_label[] = "failing-insert-bitmask-parent";
+ static const char action_label[] = "fail-on-bitmask-insert";
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
WOLFSENTRY_EXIT_ON_FAILURE(
wolfsentry_action_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "check_counts",
- -1 /* label_len */,
+ action_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
+ regression_failing_insert_action,
NULL /* handler_context */,
&id));
- {
- struct wolfsentry_action *action;
- wolfsentry_action_flags_t flags;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ event_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0 /* priority */,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- ITEM_NOT_FOUND,
- wolfsentry_action_get_reference(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "checXXXounts",
- -1 /* label_len */,
- &action));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ event_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_INSERT,
+ action_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- INVALID_ARG,
- wolfsentry_action_get_reference(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "checXXXounts",
- 0 /* label_len */,
- &action));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ n_bitmask_afs_before = wolfsentry->routes->n_bitmask_matching_afs;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- STRING_ARG_TOO_LONG,
- wolfsentry_action_get_reference(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "checXXXounts",
- WOLFSENTRY_MAX_LABEL_BYTES + 1 /* label_len */,
- &action));
+ memset(&exp_route, 0, sizeof exp_route);
+ exp_route.parent_event_label = event_label;
+ exp_route.parent_event_label_len = WOLFSENTRY_LENGTH_NULL_TERMINATED;
+ exp_route.sa_family = AF_INET;
+ exp_route.sa_proto = IPPROTO_TCP;
+ exp_route.flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS
+ | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_ADDR_BITMASK
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD;
+ exp_route.remote_address = exp_remote_addr_and_mask;
+ exp_route.remote.addr_len = sizeof exp_remote_addr_and_mask * BITS_PER_BYTE;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_get_reference(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "check_counts",
- -1 /* label_len */,
- &action));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &exp_route,
+ &id, &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_get_flags(
- action,
- &flags));
- WOLFSENTRY_EXIT_ON_FALSE(flags == WOLFSENTRY_ACTION_FLAG_NONE);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->routes->n_bitmask_matching_afs == n_bitmask_afs_before);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- {
- wolfsentry_action_flags_t flags_before, flags_after;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_update_flags(
- action,
- WOLFSENTRY_ACTION_FLAG_DISABLED,
- WOLFSENTRY_ACTION_FLAG_NONE,
- &flags_before,
- &flags_after));
- WOLFSENTRY_EXIT_ON_FALSE(flags_before == WOLFSENTRY_ACTION_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FALSE(flags_after == WOLFSENTRY_ACTION_FLAG_DISABLED);
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, event_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_action_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, action_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_get_flags(
- action,
- &flags));
- WOLFSENTRY_EXIT_ON_FALSE(flags == WOLFSENTRY_ACTION_FLAG_DISABLED);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- {
- wolfsentry_action_flags_t flags_before, flags_after;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_update_flags(
- action,
- WOLFSENTRY_ACTION_FLAG_NONE,
- WOLFSENTRY_ACTION_FLAG_DISABLED,
- &flags_before,
- &flags_after));
- WOLFSENTRY_EXIT_ON_FALSE(flags_before == WOLFSENTRY_ACTION_FLAG_DISABLED);
- WOLFSENTRY_EXIT_ON_FALSE(flags_after == WOLFSENTRY_ACTION_FLAG_NONE);
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_get_flags(
- action,
- &flags));
- WOLFSENTRY_EXIT_ON_FALSE(flags == WOLFSENTRY_ACTION_FLAG_NONE);
+ WOLFSENTRY_RETURN_OK;
+}
+#endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, action, NULL));
- }
+static wolfsentry_errcode_t regression_noop_insert_action(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_context,
+ void *caller_arg,
+ const struct wolfsentry_event *event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)action;
+ (void)handler_context;
+ (void)caller_arg;
+ (void)event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_route_insert_with_actions_marks_parent_event(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t flags;
+ wolfsentry_ent_id_t id, id_route;
+ struct regression_sockaddr_v4 remote, local;
+ static const char parent_label[] = "parent-with-insert-actions";
+ static const char other_label[] = "would-be-aux-user";
+ static const char action_label[] = "noop-on-insert";
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
WOLFSENTRY_EXIT_ON_FAILURE(
wolfsentry_action_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "add_to_greenlist",
- -1 /* label_len */,
+ action_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
+ regression_noop_insert_action,
NULL /* handler_context */,
&id));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_action_insert(
+ wolfsentry_event_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "del_from_greenlist",
- -1 /* label_len */,
- WOLFSENTRY_ACTION_FLAG_NONE,
- wolfsentry_action_dummy_callback,
- NULL /* handler_context */,
+ parent_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0 /* priority */,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
&id));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_action_append(
+ wolfsentry_event_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "match_side_effect_demo",
- -1,
- WOLFSENTRY_ACTION_TYPE_MATCH,
- "del_from_greenlist",
- -1));
+ other_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0 /* priority */,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
- WOLFSENTRY_EXIT_ON_SUCCESS(
- wolfsentry_event_action_prepend(
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "match_side_effect_demo",
- -1,
- WOLFSENTRY_ACTION_TYPE_MATCH,
- "del_from_greenlist",
- -1));
+ parent_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_INSERT,
+ action_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ regression_sockaddr_v4_init(&remote, (const byte *)"\0\1\2\3", 12345, IPPROTO_TCP, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\377\376\375\374", 443, IPPROTO_TCP, 1);
+ flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_action_prepend(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "match_side_effect_demo",
- -1,
- WOLFSENTRY_ACTION_TYPE_MATCH,
- "add_to_greenlist",
- -1));
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ parent_label, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id_route, &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_action_insert_after(
+ /* an event that already parents a route can't become an aux event. */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "match_side_effect_demo",
- -1,
- WOLFSENTRY_ACTION_TYPE_MATCH,
- "check_counts",
- -1,
- "add_to_greenlist",
- -1));
+ other_label, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ parent_label, WOLFSENTRY_LENGTH_NULL_TERMINATED));
- {
- struct wolfsentry_action_list_ent *cursor;
- const char *action_label;
- int action_label_len;
- static const char *labels[] = { "add_to_greenlist", "check_counts", "del_from_greenlist" };
- int label_i = 0;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id_route, NULL /* event_label */, 0 /* event_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, other_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, parent_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_action_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, action_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_action_list_start(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "match_side_effect_demo",
- -1,
- WOLFSENTRY_ACTION_TYPE_MATCH,
- &cursor));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- while (wolfsentry_event_action_list_next(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &cursor,
- &action_label,
- &action_label_len) >= 0) {
- WOLFSENTRY_EXIT_ON_TRUE((size_t)label_i >= sizeof labels / sizeof labels[0]);
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(action_label, labels[label_i++]) == 0);
- }
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FALSE(label_i == sizeof labels / sizeof labels[0]);
+static int test_route_insert_rejects_expired_purge_after(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id;
+ wolfsentry_time_t now, one_hour;
+ struct wolfsentry_route_exports exp_route;
+ static const byte exp_remote_addr[4] = { 0, 1, 2, 3 };
+ static const byte exp_local_addr[4] = { 255, 254, 253, 252 };
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_action_list_done(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &cursor));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(&exp_route, 0, sizeof exp_route);
+ exp_route.sa_family = AF_INET;
+ exp_route.sa_proto = IPPROTO_TCP;
+ exp_route.flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ exp_route.remote_address = exp_remote_addr;
+ exp_route.remote.addr_len = sizeof exp_remote_addr * BITS_PER_BYTE;
+ exp_route.remote.sa_port = 12345;
+ exp_route.local_address = exp_local_addr;
+ exp_route.local.addr_len = sizeof exp_local_addr * BITS_PER_BYTE;
+ exp_route.local.sa_port = 443;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_time_now_plus_delta(wolfsentry, 0, &now));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 3600, 0, &one_hour));
+
+ /* a deadline at or before the insert time can never be served. */
+ exp_route.meta.purge_after = now;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &exp_route,
+ &id, &action_results));
+ exp_route.meta.purge_after = now - one_hour;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &exp_route,
+ &id, &action_results));
+
+ exp_route.meta.purge_after = now + one_hour;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_event_action_delete(
+ wolfsentry_route_insert_by_exports(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "match_side_effect_demo",
- -1,
- WOLFSENTRY_ACTION_TYPE_MATCH,
- "del_from_greenlist",
- -1));
+ NULL /* caller_arg */,
+ &exp_route,
+ &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id, NULL /* event_label */, 0 /* event_label_len */, &action_results));
WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
@@ -3469,64 +4215,66 @@ static int test_dynamic_rules(void) {
WOLFSENTRY_RETURN_OK;
}
-#endif /* TEST_DYNAMIC_RULES */
-
-#ifdef TEST_USER_VALUES
-
-#include
+#if !defined(WOLFSENTRY_NO_JSON) || defined(WOLFSENTRY_JSON_DUMP_UTILS)
-static wolfsentry_errcode_t test_kv_validator(
+/* renders a route to JSON through every output budget from empty up to the
+ * full length, checking that a short budget is reported rather than
+ * overrun.
+ */
+static int regression_route_format_json_sweep(
WOLFSENTRY_CONTEXT_ARGS_IN,
- struct wolfsentry_kv_pair *kv)
+ const struct wolfsentry_route *route,
+ unsigned char *full_buf,
+ size_t full_buf_size,
+ size_t *full_len)
{
- WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
- switch (WOLFSENTRY_KV_TYPE(kv)) {
- case WOLFSENTRY_KV_NONE:
- case WOLFSENTRY_KV_NULL:
- case WOLFSENTRY_KV_TRUE:
- case WOLFSENTRY_KV_FALSE:
- WOLFSENTRY_RETURN_OK;
- case WOLFSENTRY_KV_UINT:
- if (WOLFSENTRY_KV_V_UINT(kv) == 12345678UL)
- WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
- else
- WOLFSENTRY_RETURN_OK;
- case WOLFSENTRY_KV_SINT:
- if (WOLFSENTRY_KV_V_SINT(kv) == -12345678L)
- WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
- else
- WOLFSENTRY_RETURN_OK;
- case WOLFSENTRY_KV_FLOAT:
- if (WOLFSENTRY_KV_V_FLOAT(kv) > 100.0)
- WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
- else
- WOLFSENTRY_RETURN_OK;
- case WOLFSENTRY_KV_STRING:
- if (WOLFSENTRY_KV_V_STRING_LEN(kv) != 8)
- WOLFSENTRY_RETURN_OK;
- if (strncmp(WOLFSENTRY_KV_V_STRING(kv), "deadbeef", WOLFSENTRY_KV_V_STRING_LEN(kv)) == 0)
- WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
- else
- WOLFSENTRY_RETURN_OK;
- case WOLFSENTRY_KV_BYTES:
- if (WOLFSENTRY_KV_V_BYTES_LEN(kv) != 10)
- WOLFSENTRY_RETURN_OK;
- if (memcmp(WOLFSENTRY_KV_V_STRING(kv), "abcdefghij", WOLFSENTRY_KV_V_BYTES_LEN(kv)) == 0)
- WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
- else
- WOLFSENTRY_RETURN_OK;
- default:
- break;
+ static const unsigned char guard_byte = 0xA5;
+ unsigned char guard_buf[2048];
+ unsigned char *json_out;
+ size_t json_out_len, n, i;
+ wolfsentry_errcode_t ret;
+
+ json_out = full_buf;
+ json_out_len = full_buf_size;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_format_json(WOLFSENTRY_CONTEXT_ARGS_OUT, route, &json_out, &json_out_len, WOLFSENTRY_FORMAT_FLAG_NONE));
+ *full_len = full_buf_size - json_out_len;
+ WOLFSENTRY_EXIT_ON_FALSE(*full_len > 0);
+ WOLFSENTRY_EXIT_ON_FALSE(*full_len + 512 < sizeof guard_buf);
+
+ for (n = 0; n < *full_len; ++n) {
+ memset(guard_buf, guard_byte, sizeof guard_buf);
+ json_out = guard_buf;
+ json_out_len = n;
+ ret = wolfsentry_route_format_json(WOLFSENTRY_CONTEXT_ARGS_OUT, route, &json_out, &json_out_len, WOLFSENTRY_FORMAT_FLAG_NONE);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(ret, BUFFER_TOO_SMALL));
+ WOLFSENTRY_EXIT_ON_FALSE((size_t)(json_out - guard_buf) <= n);
+ WOLFSENTRY_EXIT_ON_FALSE(json_out_len <= n);
+ for (i = n; i < sizeof guard_buf; ++i)
+ WOLFSENTRY_EXIT_ON_FALSE(guard_buf[i] == guard_byte);
}
- WOLFSENTRY_ERROR_RETURN(WRONG_TYPE);
+
+ memset(guard_buf, guard_byte, sizeof guard_buf);
+ json_out = guard_buf;
+ json_out_len = *full_len;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_format_json(WOLFSENTRY_CONTEXT_ARGS_OUT, route, &json_out, &json_out_len, WOLFSENTRY_FORMAT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FALSE(json_out_len == 0);
+ WOLFSENTRY_EXIT_ON_FALSE(memcmp(guard_buf, full_buf, *full_len) == 0);
+ WOLFSENTRY_EXIT_ON_FALSE(guard_buf[*full_len] == guard_byte);
+
+ WOLFSENTRY_RETURN_OK;
}
-static int test_user_values(void) {
+static int test_route_format_json_truncation_is_bounded(void) {
struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *main_routes;
+ struct wolfsentry_route *route;
wolfsentry_action_res_t action_results;
-
- wolfsentry_kv_type_t kv_type;
- struct wolfsentry_kv_pair_internal *kv_ref;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_route_flags_t flags;
+ wolfsentry_ent_id_t id;
+ unsigned char full_buf[1024];
+ size_t full_len;
+ struct regression_sockaddr_v4 remote, local;
WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
@@ -3538,691 +4286,975 @@ static int test_user_values(void) {
&wolfsentry,
WOLFSENTRY_INIT_FLAG_NONE));
- action_results = WOLFSENTRY_ACTION_RES_NONE;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_set_validator(WOLFSENTRY_CONTEXT_ARGS_OUT, test_kv_validator, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(action_results == WOLFSENTRY_ACTION_RES_NONE);
+ regression_sockaddr_v4_init(&remote, (const byte *)"\xC0\xA8\x01\x01", 12345, IPPROTO_TCP, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\12\0\0\1", 443, IPPROTO_TCP, 2);
+ flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_store_null(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_null",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 0));
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_store_bool(
+ wolfsentry_route_get_reference(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_KV_TRUE,
- 0));
+ main_routes,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ 1 /* exact_p */,
+ &inexact_matches,
+ &route));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_format_json_sweep(WOLFSENTRY_CONTEXT_ARGS_OUT, route, full_buf, sizeof full_buf, &full_len));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_bool(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &kv_type));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route, NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id, NULL /* event_label */, 0 /* event_label_len */, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(kv_type == WOLFSENTRY_KV_TRUE);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_route_format_json_escapes_parent_event_label(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *main_routes;
+ struct wolfsentry_route *route;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_route_flags_t flags;
+ wolfsentry_ent_id_t id;
+ unsigned char full_buf[1024];
+ size_t full_len;
+ struct regression_sockaddr_v4 remote, local;
+ static const char event_label[] = "q\"b\\s\001c";
+ static const char expected[] = "\"parent-event\":\"q\\\"b\\\\s\\u0001c\",";
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_store_bool(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_KV_FALSE,
- 1));
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_bool(
+ wolfsentry_event_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
+ event_label,
WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &kv_type));
+ 0 /* priority */,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
- WOLFSENTRY_EXIT_ON_FALSE(kv_type == WOLFSENTRY_KV_FALSE);
+ regression_sockaddr_v4_init(&remote, (const byte *)"\xC0\xA8\x01\x01", 12345, IPPROTO_TCP, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\12\0\0\1", 443, IPPROTO_TCP, 2);
+ flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- WRONG_TYPE,
- wolfsentry_user_value_store_bool(
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- WOLFSENTRY_KV_NONE,
- 1));
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ event_label, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_type(
+ wolfsentry_route_get_reference(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &kv_type));
+ main_routes,
+ &remote.sa, &local.sa, flags,
+ event_label, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 1 /* exact_p */,
+ &inexact_matches,
+ &route));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_format_json_sweep(WOLFSENTRY_CONTEXT_ARGS_OUT, route, full_buf, sizeof full_buf - 1, &full_len));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FALSE(kv_type == WOLFSENTRY_KV_FALSE);
+ full_buf[full_len] = 0;
+ WOLFSENTRY_EXIT_ON_FALSE(strstr((const char *)full_buf, expected) != NULL);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_type(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
- strlen("test_bool"),
- &kv_type));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route, NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id, NULL /* event_label */, 0 /* event_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, event_label, WOLFSENTRY_LENGTH_NULL_TERMINATED, &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(kv_type == WOLFSENTRY_KV_FALSE);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
- WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- ITEM_NOT_FOUND,
- wolfsentry_user_value_get_type(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bool",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &kv_type));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_store_uint(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_uint",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 123UL,
- 0));
+#ifndef WOLFSENTRY_NO_GETPROTOBY
+#include
+#endif
- {
- uint64_t value = 0;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_uint(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_uint",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value));
+static int test_route_format_json_unknown_protocol_numeric(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *main_routes;
+ struct wolfsentry_route *route;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_route_flags_t flags;
+ wolfsentry_ent_id_t id;
+ unsigned char full_buf[1024];
+ size_t full_len, i;
+ struct regression_sockaddr_v4 remote, local;
+ wolfsentry_proto_t unknown_proto = 0;
+ char expected[32];
+ /* IANA experimental and unassigned numbers, checked against the host
+ * database at runtime because a local protocols file can name any of
+ * them.
+ */
+ static const wolfsentry_proto_t candidates[] = { 253, 254, 143, 144, 145 };
- WOLFSENTRY_EXIT_ON_FALSE(value == 123UL);
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+#ifndef WOLFSENTRY_NO_GETPROTOBY
+ for (i = 0; i < sizeof candidates / sizeof candidates[0]; ++i) {
+ if (getprotobynumber((int)candidates[i]) == NULL) {
+ unknown_proto = candidates[i];
+ break;
+ }
+ }
+ if (unknown_proto == 0) {
+ printf("skipping unknown-protocol JSON check: host protocol database names every candidate.\n");
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
}
+#else
+ (void)i;
+ unknown_proto = candidates[0];
+#endif
+ WOLFSENTRY_EXIT_ON_FALSE(snprintf(expected, sizeof expected, "\"protocol\":%u,", (unsigned int)unknown_proto) > 0);
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- BAD_VALUE,
- wolfsentry_user_value_store_uint(
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ regression_sockaddr_v4_init(&remote, (const byte *)"\xC0\xA8\x01\x01", 0, unknown_proto, 1);
+ regression_sockaddr_v4_init(&local, (const byte *)"\12\0\0\1", 0, unknown_proto, 2);
+ flags = WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "bad_uint",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 12345678UL,
- 0));
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_store_sint(
+ wolfsentry_route_get_reference(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_sint",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- -123L,
- 0));
+ main_routes,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ 1 /* exact_p */,
+ &inexact_matches,
+ &route));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_format_json_sweep(WOLFSENTRY_CONTEXT_ARGS_OUT, route, full_buf, sizeof full_buf - 1, &full_len));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- {
- int64_t value = 0;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_sint(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_sint",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value));
+ full_buf[full_len] = 0;
+ WOLFSENTRY_EXIT_ON_FALSE(strstr((const char *)full_buf, expected) != NULL);
- WOLFSENTRY_EXIT_ON_FALSE(value == -123L);
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route, NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id, NULL /* event_label */, 0 /* event_label_len */, &action_results));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- BAD_VALUE,
- wolfsentry_user_value_store_sint(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "bad_sint",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- -12345678L,
- 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#endif /* !WOLFSENTRY_NO_JSON || WOLFSENTRY_JSON_DUMP_UTILS */
+
+static int test_static_routes_clone_addr_family_links(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_context *clone = NULL;
+ wolfsentry_action_res_t action_results;
+ const struct wolfsentry_table_ent_header *i, *prev;
+ wolfsentry_addr_bits_t bits;
+ unsigned int n;
+ static const char * const names[3] = { "clone_AF_0", "clone_AF_1", "clone_AF_2" };
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_store_double(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_float",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 1.234,
- 0));
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- {
- double value = 0.0;
+ for (n = 0; n < 3; ++n) {
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_float(
+ wolfsentry_addr_family_handler_install(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_float",
+ (wolfsentry_addr_family_t)(WOLFSENTRY_AF_USER_OFFSET + n),
+ names[n],
WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value));
-
- WOLFSENTRY_EXIT_ON_FALSE(fabs(value - 1.234) < 0.000001);
+ my_addr_family_parser,
+ my_addr_family_formatter,
+ (int)(8 * (n + 1)) /* max_addr_bits */));
}
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- BAD_VALUE,
- wolfsentry_user_value_store_double(
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_context_clone(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "bad_float",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 123.45678,
- 0));
+ &clone,
+ WOLFSENTRY_CLONE_FLAG_NONE));
+
+ prev = NULL;
+ n = 0;
+ for (i = clone->addr_families_bynumber->header.head; i != NULL; i = i->next) {
+ WOLFSENTRY_EXIT_ON_FALSE(i->prev == prev);
+ WOLFSENTRY_EXIT_ON_FALSE(i->parent_table == &clone->addr_families_bynumber->header);
+ prev = i;
+ ++n;
+ }
+ WOLFSENTRY_EXIT_ON_FALSE(n == 3);
+ WOLFSENTRY_EXIT_ON_FALSE(n == clone->addr_families_bynumber->header.n_ents);
+ WOLFSENTRY_EXIT_ON_FALSE(prev == clone->addr_families_bynumber->header.tail);
- {
- static const char test_string[] = "abc123";
- const char *value = NULL;
- int value_len = -1;
- int mutable = -1;
- wolfsentry_errcode_t ret;
+ /* removing the middle family from the clone must leave its neighbors
+ * reachable.
+ */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_remove_bynumber(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(clone),
+ (wolfsentry_addr_family_t)(WOLFSENTRY_AF_USER_OFFSET + 1),
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_store_string(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- test_string,
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_max_addr_bits(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(clone),
+ (wolfsentry_addr_family_t)WOLFSENTRY_AF_USER_OFFSET,
+ &bits));
+ WOLFSENTRY_EXIT_ON_FALSE(bits == 8);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_NOT_FOUND,
+ wolfsentry_addr_family_max_addr_bits(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(clone),
+ (wolfsentry_addr_family_t)(WOLFSENTRY_AF_USER_OFFSET + 1),
+ &bits));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_max_addr_bits(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(clone),
+ (wolfsentry_addr_family_t)(WOLFSENTRY_AF_USER_OFFSET + 2),
+ &bits));
+ WOLFSENTRY_EXIT_ON_FALSE(bits == 24);
+
+ WOLFSENTRY_EXIT_ON_FALSE(clone->addr_families_bynumber->header.n_ents == 2);
+ WOLFSENTRY_EXIT_ON_FALSE(clone->addr_families_bynumber->header.head != NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(clone->addr_families_bynumber->header.head->prev == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(clone->addr_families_bynumber->header.head->next == clone->addr_families_bynumber->header.tail);
+ WOLFSENTRY_EXIT_ON_FALSE(clone->addr_families_bynumber->header.tail->prev == clone->addr_families_bynumber->header.head);
+
+ /* the source context must be untouched by edits to the clone. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_max_addr_bits(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (wolfsentry_addr_family_t)(WOLFSENTRY_AF_USER_OFFSET + 1),
+ &bits));
+ WOLFSENTRY_EXIT_ON_FALSE(bits == 16);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_mutability(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &mutable));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&clone)));
- WOLFSENTRY_EXIT_ON_FALSE(mutable == 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_set_mutability(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_mutability(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &mutable));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FALSE(mutable == 0);
+static int test_static_routes_id_wrap(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id1, id2;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
- WOLFSENTRY_EXIT_ON_SUCCESS(
- ret = wolfsentry_user_value_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(NOT_PERMITTED, ret);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_string(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value,
- &value_len,
- &kv_ref));
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr, "\0\1\2\3", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf);
- WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(test_string));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, test_string) == 0);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_release_record(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &kv_ref));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ wolfsentry->mk_id_cb_state.id_counter = (wolfsentry_ent_id_t)MAX_UINT_OF(wolfsentry_ent_id_t) - 1U;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id1, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(id1 == (wolfsentry_ent_id_t)MAX_UINT_OF(wolfsentry_ent_id_t));
+
+ /* the next allocation wraps the counter and must still yield a valid,
+ * unique ID.
+ */
+ local.sa.sa_port = 444;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id2, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(id2 != WOLFSENTRY_ENT_ID_NONE);
+ WOLFSENTRY_EXIT_ON_FALSE(id2 != id1);
-#ifdef WOLFSENTRY_HAVE_JSON_DOM
- /* strings rendered as JSON must escape quotes and backslashes. */
- {
- static const char raw[] = "a\"b\\c";
- const struct wolfsentry_kv_pair *kv_exports;
- char render_buf[64];
- int render_buf_space = (int)sizeof render_buf;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id2,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id1,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
- memset(render_buf, 0, sizeof render_buf);
+ /* after the wrap the counter must never rest on the reserved null ID. */
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->mk_id_cb_state.id_counter != WOLFSENTRY_ENT_ID_NONE);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_store_string(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "json_escape_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- raw,
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 0));
- {
- const char *value = NULL;
- int value_len = -1;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_get_string(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "json_escape_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value, &value_len, &kv_ref));
- }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_pair_export(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_ref, &kv_exports));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_render_value(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_exports, render_buf, &render_buf_space));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FALSE(strstr(render_buf, "\\\"") != NULL);
- WOLFSENTRY_EXIT_ON_FALSE(strstr(render_buf, "\\\\") != NULL);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_release_record(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_ref));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_delete(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "json_escape_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED));
- }
-#endif /* WOLFSENTRY_HAVE_JSON_DOM */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- BAD_VALUE,
- wolfsentry_user_value_store_string(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "bad_string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- "deadbeef",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- {
- static const byte test_bytes[] = { 0, 1, 2, 3, 4 };
- const byte *value = NULL;
- int value_len = -1;
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_store_bytes(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bytes",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- test_bytes,
- sizeof test_bytes,
- 0));
+static int test_static_routes_id_reclaim(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id1, id2, id3, counter_after_insert;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS;
+ unsigned int n;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_bytes(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "test_bytes",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value,
- &value_len,
- &kv_ref));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)sizeof test_bytes);
- WOLFSENTRY_EXIT_ON_FALSE(memcmp(value, test_bytes, (size_t)value_len) == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr, "\0\1\2\3", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id1, &action_results));
+ counter_after_insert = wolfsentry->mk_id_cb_state.id_counter;
+ WOLFSENTRY_EXIT_ON_FALSE(id1 == counter_after_insert);
+
+ /* deleting and re-inserting the newest entry repeatedly must reuse its
+ * ID rather than grow the counter.
+ */
+ for (n = 0; n < 8; ++n) {
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_release_record(
+ wolfsentry_route_delete_by_id(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- &kv_ref));
+ NULL /* caller_arg */,
+ id1,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->mk_id_cb_state.id_counter == counter_after_insert - 1U);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id2, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(id2 == id1);
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->mk_id_cb_state.id_counter == counter_after_insert);
}
+ /* deleting an older entry must not disturb the counter, and the next
+ * allocation must not collide with the surviving newest entry.
+ */
+ local.sa.sa_port = 444;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id2, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(id2 == counter_after_insert + 1U);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id1,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->mk_id_cb_state.id_counter == id2);
+ local.sa.sa_port = 445;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id3, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(id3 == id2 + 1U);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id3,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id2,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(wolfsentry->mk_id_cb_state.id_counter == id2 - 1U);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_route_insert_requires_direction(WOLFSENTRY_CONTEXT_ARGS_IN) {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS;
+
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 4001;
+ local.sa.sa_port = 4002;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ memcpy(remote.sa.addr, "\x0a\x0b\x0c\x0d", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\x0a\x0b\x0c\x0e", sizeof local.addr_buf);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- BAD_VALUE,
- wolfsentry_user_value_store_bytes(
+ INVALID_ARG,
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "bad_bytes",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (const byte *)"abcdefghij",
- 10,
- 0));
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
-#ifndef WOLFSENTRY_NO_STDIO
- {
- wolfsentry_errcode_t ret;
- struct wolfsentry_cursor *cursor;
- const struct wolfsentry_kv_pair *kv_exports;
- const char *val_type;
- char val_buf[256];
- int val_buf_space;
- wolfsentry_hitcount_t n_seen = 0;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
- for (ret = wolfsentry_user_values_iterate_current(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref);
- ret >= 0;
- ret = wolfsentry_user_values_iterate_next(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref)) {
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_pair_export(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_ref, &kv_exports));
- val_buf_space = sizeof val_buf;
- if (wolfsentry_kv_type_to_string(WOLFSENTRY_KV_TYPE(kv_exports), &val_type) < 0)
- val_type = "?";
- if (wolfsentry_kv_render_value(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_exports, val_buf, &val_buf_space) < 0)
- strcpy(val_buf,"?");
- printf("{ \"%.*s\" : { \"%s\" : %s } }\n",
- (int)WOLFSENTRY_KV_KEY_LEN(kv_exports),
- WOLFSENTRY_KV_KEY(kv_exports),
- val_type,
- val_buf);
- ++n_seen;
- }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FALSE(n_seen == wolfsentry->user_values->header.n_ents);
- WOLFSENTRY_EXIT_ON_FALSE(n_seen == 6);
- }
-#endif
+ /* the same route is accepted once it has a direction. */
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
- {
- static const struct {
- const char *q;
- const char *a;
- } base64_qna[] = {
- { "", "" },
- { "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", "Many hands make light work." },
- { "bGlnaHQgd29yay4=", "light work." },
- { "bGlnaHQgd29yay4", "light work." },
- { "bGlnaHQgd29yaw==", "light work" },
- { "bGlnaHQgd29yaw", "light work" },
- { "bGlnaHQgd29y", "light wor" },
- { "bGlnaHQgd28=", "light wo" },
- { "bGlnaHQgdw==", "light w" },
- { "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV"
- "1hZWjAxMjM0NTY3ODkhQCMkJV4mKigpXy0rPXxcYH5bXXt9OzonIiw8Lj4vPw==",
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
- "!@#$%^&*()_-+=|\\`~[]{};:'\",<.>/?" },
- { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+",
- "\x69\xb7\x1d\x79\xf8\x21\x8a\x39\x25\x9a\x7a\x29\xaa\xbb\x2d\xba"
- "\xfc\x31\xcb\x30\x01\x08\x31\x05\x18\x72\x09\x28\xb3\x0d\x38\xf4"
- "\x11\x49\x35\x15\x59\x76\x19\xd3\x5d\xb7\xe3\x9e\xbb\xf3\xdf\xfe"
- }
- };
+ return 0;
+}
- int i;
- byte outbuf[256];
- size_t outbuf_spc;
+static int test_route_insert_by_exports_requires_direction(WOLFSENTRY_CONTEXT_ARGS_IN) {
+ struct wolfsentry_route_exports exp_route;
+ byte exp_remote_addr[4];
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id;
- for (i=0; i < (int)(sizeof base64_qna / sizeof base64_qna[0]); ++i) {
- outbuf_spc = sizeof outbuf;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_base64_decode(base64_qna[i].q, strlen(base64_qna[i].q), outbuf, &outbuf_spc, 0 /* ignore_junk_p */));
- WOLFSENTRY_EXIT_ON_FALSE((outbuf_spc == strlen(base64_qna[i].a)) && (memcmp(outbuf, base64_qna[i].a, outbuf_spc) == 0));
- }
+ memset(&exp_route, 0, sizeof exp_route);
+ exp_route.sa_family = AF_INET;
+ exp_route.sa_proto = IPPROTO_TCP;
+ exp_route.flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD;
+ memcpy(exp_remote_addr, "\x0a\x0b\x0c\x0d", sizeof exp_remote_addr);
+ exp_route.remote_address = exp_remote_addr;
+ exp_route.remote.addr_len = sizeof exp_remote_addr * BITS_PER_BYTE;
+ exp_route.local.addr_len = 0;
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &exp_route,
+ &id, &action_results));
+
+ /* the same route is accepted once it has a direction. */
+ WOLFSENTRY_SET_BITS(exp_route.flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &exp_route,
+ &id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
+
+ return 0;
+}
+
+static int test_route_flags_sensical_validation(WOLFSENTRY_CONTEXT_ARGS_IN) {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id;
+ wolfsentry_route_flags_t proto_flags, all_flags;
+ unsigned int i;
+ static const wolfsentry_route_flags_t family_deps[] = {
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD,
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD,
+ WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD
+ };
+ static const wolfsentry_route_flags_t proto_deps[] = {
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD,
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ };
+
+ proto_flags = WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD;
+ all_flags = proto_flags
+ | WOLFSENTRY_ROUTE_FLAG_SA_FAMILY_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_check_flags_sensical(WOLFSENTRY_ROUTE_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_check_flags_sensical(proto_flags));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_check_flags_sensical(all_flags));
+
+ /* a family wildcard needs both address wildcards and the protocol wildcard. */
+ for (i = 0; i < sizeof family_deps / sizeof family_deps[0]; ++i) {
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_check_flags_sensical(all_flags & ~family_deps[i]));
}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ /* a protocol wildcard needs both port wildcards. */
+ for (i = 0; i < sizeof proto_deps / sizeof proto_deps[0]; ++i) {
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_check_flags_sensical(proto_flags & ~proto_deps[i]));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_check_flags_sensical(all_flags & ~proto_deps[i]));
+ }
- WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ /* the validation also applies on the insert path. */
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ memcpy(remote.sa.addr, "\x0a\x0b\x0c\x0f", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\x0a\x0b\x0c\x10", sizeof local.addr_buf);
- WOLFSENTRY_RETURN_OK;
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN | WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN | WOLFSENTRY_ROUTE_FLAG_SA_FAMILY_WILDCARD | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD | proto_flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
+
+ return 0;
}
-#endif /* TEST_USER_VALUES */
+static int insert_route_for_update_flags(WOLFSENTRY_CONTEXT_ARGS_IN, struct wolfsentry_route **route_out, wolfsentry_ent_id_t *id_out) {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id;
-#ifdef TEST_USER_ADDR_FAMILIES
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 4003;
+ local.sa.sa_port = 4004;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ memcpy(remote.sa.addr, "\x0a\x0b\x0c\x11", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\x0a\x0b\x0c\x12", sizeof local.addr_buf);
-static int test_user_addr_families(void) {
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
- struct wolfsentry_context *wolfsentry;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)route_out));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ *id_out = id;
+
+ return 0;
+}
+
+static int test_route_update_flags_rejects_penaltybox_and_greenlist(WOLFSENTRY_CONTEXT_ARGS_IN) {
+ struct wolfsentry_route *route;
+ wolfsentry_ent_id_t id;
+ wolfsentry_route_flags_t flags_before, flags_after;
wolfsentry_action_res_t action_results;
- WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+ WOLFSENTRY_EXIT_ON_FALSE(insert_route_for_update_flags(WOLFSENTRY_CONTEXT_ARGS_OUT, &route, &id) == 0);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED | WOLFSENTRY_ROUTE_FLAG_GREENLISTED,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_ATOMIC_LOAD(route->flags) & (WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED | WOLFSENTRY_ROUTE_FLAG_GREENLISTED));
+ /* either flag alone is accepted. */
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_init_ex(
- wolfsentry_build_settings,
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
- NULL /* config */,
- &wolfsentry,
- WOLFSENTRY_INIT_FLAG_NONE));
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_GREENLISTED,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ATOMIC_LOAD(route->flags) & WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_handler_install(
+ wolfsentry_route_delete_by_id(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET,
- "my_AF",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- my_addr_family_parser,
- my_addr_family_formatter,
- 24 /* max_addr_bits */));
+ NULL /* caller_arg */,
+ id,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
+
+ return 0;
+}
+static int test_route_update_flags_protects_immutable_flags(WOLFSENTRY_CONTEXT_ARGS_IN) {
+ struct wolfsentry_route *route;
+ wolfsentry_ent_id_t id;
+ wolfsentry_route_flags_t flags_before, flags_after, flags_at_entry;
+ wolfsentry_action_res_t action_results;
+
+ WOLFSENTRY_EXIT_ON_FALSE(insert_route_for_update_flags(WOLFSENTRY_CONTEXT_ARGS_OUT, &route, &id) == 0);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ flags_at_entry = WOLFSENTRY_ATOMIC_LOAD(route->flags);
+ WOLFSENTRY_EXIT_ON_FALSE(flags_at_entry & WOLFSENTRY_ROUTE_FLAG_IN_TABLE);
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ATOMIC_LOAD(route->flags) == flags_at_entry);
+
+ /* mutable flags can still be changed. */
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_handler_remove_bynumber(
+ wolfsentry_route_update_flags(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_GREENLISTED,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(flags_after == (flags_at_entry | WOLFSENTRY_ROUTE_FLAG_GREENLISTED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id,
+ NULL /* event_label */, 0 /* event_label_len */,
&action_results));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+ return 0;
+}
+
+static int test_route_update_flags_protects_internal_flags(WOLFSENTRY_CONTEXT_ARGS_IN) {
+ struct wolfsentry_route *route;
+ wolfsentry_ent_id_t id;
+ wolfsentry_route_flags_t flags_before, flags_after, flags_at_entry;
+ wolfsentry_action_res_t action_results;
+
+ WOLFSENTRY_EXIT_ON_FALSE(insert_route_for_update_flags(WOLFSENTRY_CONTEXT_ARGS_OUT, &route, &id) == 0);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ flags_at_entry = WOLFSENTRY_ATOMIC_LOAD(route->flags);
+ WOLFSENTRY_EXIT_ON_FALSE(flags_at_entry & WOLFSENTRY_ROUTE_FLAG_IN_TABLE);
+ WOLFSENTRY_EXIT_ON_TRUE(flags_at_entry & WOLFSENTRY_ROUTE_FLAG_PENDING_DELETE);
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_PENDING_DELETE,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_IN_TABLE,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ WOLFSENTRY_ROUTE_FLAG_IN_TABLE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_route_update_flags(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ WOLFSENTRY_ROUTE_FLAG_NONE,
+ WOLFSENTRY_ROUTE_FLAG_PENDING_DELETE,
+ &flags_before, &flags_after, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ATOMIC_LOAD(route->flags) == flags_at_entry);
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_handler_install(
+ wolfsentry_route_delete_by_id(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET,
- "my_AF",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- my_addr_family_parser,
- my_addr_family_formatter,
- 24 /* max_addr_bits */));
+ NULL /* caller_arg */,
+ id,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &action_results));
- action_results = 0;
+ return 0;
+}
- /* exercise the plugins to disambiguate failures in the plugins from
- * JSON-specific failures.
- */
- {
- byte addr_internal[3];
- wolfsentry_addr_bits_t addr_internal_len;
- char addr_text[13];
- int addr_text_len;
+static int test_route_table_max_purgeable_routes_shrink(WOLFSENTRY_CONTEXT_ARGS_IN) {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ struct wolfsentry_route_table *main_routes;
+ struct wolfsentry_route *route;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_hitcount_t max_purgeable_routes;
+ wolfsentry_ent_id_t ids[3];
+ unsigned int i;
+ byte addr[4] = { 0x0a, 0x0b, 0x0c, 0x00 };
- addr_internal_len = (wolfsentry_addr_bits_t)(sizeof addr_internal * 8);
- WOLFSENTRY_EXIT_ON_FAILURE(
- my_addr_family_parser(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "377/377/377",
- strlen("377/377/377"),
- addr_internal,
- &addr_internal_len));
- WOLFSENTRY_EXIT_ON_FALSE(addr_internal_len == (wolfsentry_addr_bits_t)(sizeof addr_internal * 8));
- WOLFSENTRY_EXIT_ON_FALSE(memcmp(addr_internal, "\377\377\377", sizeof addr_internal) == 0);
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 4005;
+ local.sa.sa_port = 4006;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ memcpy(local.sa.addr, "\x0a\x0b\x0c\x14", sizeof local.addr_buf);
- addr_text_len = (int)sizeof addr_text;
- WOLFSENTRY_EXIT_ON_FAILURE(
- my_addr_family_formatter(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- addr_internal,
- (int)(sizeof addr_internal * 8),
- addr_text,
- &addr_text_len));
- WOLFSENTRY_EXIT_ON_FALSE(addr_text_len == strlen("377/377/377"));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(addr_text, "377/377/377") == 0);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- addr_internal_len = (wolfsentry_addr_bits_t)(sizeof addr_internal * 8);
+ /* three routes with no connections, all already due for purging. */
+ for (i = 0; i < sizeof ids / sizeof ids[0]; ++i) {
+ addr[3] = (byte)(0x20 + i);
+ memcpy(remote.sa.addr, addr, sizeof addr);
WOLFSENTRY_EXIT_ON_FAILURE(
- my_addr_family_parser(
+ wolfsentry_route_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "0/0/0",
- strlen("0/0/0"),
- addr_internal,
- &addr_internal_len));
- WOLFSENTRY_EXIT_ON_FALSE(addr_internal_len == (wolfsentry_addr_bits_t)(sizeof addr_internal * 8));
- WOLFSENTRY_EXIT_ON_FALSE(memcmp(addr_internal, "\0\0\0", sizeof addr_internal) == 0);
-
- addr_internal_len = (wolfsentry_addr_bits_t)(sizeof addr_internal * 8);
- WOLFSENTRY_EXIT_ON_FAILURE(
- my_addr_family_parser(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "377/377/",
- strlen("377/377/"),
- addr_internal,
- &addr_internal_len));
- WOLFSENTRY_EXIT_ON_FALSE(addr_internal_len == 16);
- WOLFSENTRY_EXIT_ON_FALSE(memcmp(addr_internal, "\377\377", 2) == 0);
-
- addr_text_len = (int)sizeof addr_text;
- WOLFSENTRY_EXIT_ON_FAILURE(
- my_addr_family_formatter(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- addr_internal,
- 16,
- addr_text,
- &addr_text_len));
- WOLFSENTRY_EXIT_ON_FALSE(addr_text_len == strlen("377/377/"));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(addr_text, "377/377/") == 0);
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &ids[i], &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, ids[i], (struct wolfsentry_table_ent_header **)&route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_purge_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, route, 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ }
+ WOLFSENTRY_EXIT_ON_FALSE(main_routes->purge_list.len == 3);
- addr_internal_len = (wolfsentry_addr_bits_t)(sizeof addr_internal * 8);
- WOLFSENTRY_EXIT_ON_FAILURE(
- my_addr_family_parser(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "377/",
- strlen("377/"),
- addr_internal,
- &addr_internal_len));
- WOLFSENTRY_EXIT_ON_FALSE(addr_internal_len == 8);
- WOLFSENTRY_EXIT_ON_FALSE(memcmp(addr_internal, "\377", 1) == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
- addr_text_len = (int)sizeof addr_text;
- WOLFSENTRY_EXIT_ON_FAILURE(
- my_addr_family_formatter(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- addr_internal,
- 8,
- addr_text,
- &addr_text_len));
- WOLFSENTRY_EXIT_ON_FALSE(addr_text_len == strlen("377/"));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(addr_text, "377/") == 0);
- }
+ /* raising the limit, or lowering it to exactly the current count, purges nothing. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_routes_set(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, 5));
+ WOLFSENTRY_EXIT_ON_FALSE(main_routes->purge_list.len == 3);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_routes_set(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, 3));
+ WOLFSENTRY_EXIT_ON_FALSE(main_routes->purge_list.len == 3);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_routes_set(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, 10));
+ WOLFSENTRY_EXIT_ON_FALSE(main_routes->purge_list.len == 3);
-#ifdef WOLFSENTRY_PROTOCOL_NAMES
+ /* lowering the limit below the current count purges immediately. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_routes_set(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, 2));
+ WOLFSENTRY_EXIT_ON_FALSE(main_routes->purge_list.len <= 2);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_routes_get(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &max_purgeable_routes));
+ WOLFSENTRY_EXIT_ON_FALSE(max_purgeable_routes == 2);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_handler_remove_byname(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "my_AF",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_routes_set(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+ return 0;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_handler_install(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET,
- "my_AF",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- my_addr_family_parser,
- my_addr_family_formatter,
- 24 /* max_addr_bits */));
+static int test_static_routes_guards(void) {
+ struct wolfsentry_context *wolfsentry;
+ static const struct wolfsentry_eventconfig config = { 0 };
- {
- wolfsentry_addr_family_t family_number;
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_pton(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "my_AF",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &family_number));
- WOLFSENTRY_EXIT_ON_FALSE(family_number == WOLFSENTRY_AF_USER_OFFSET);
-
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
- ITEM_NOT_FOUND,
- wolfsentry_addr_family_pton(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "no_such_AF",
- WOLFSENTRY_LENGTH_NULL_TERMINATED, &family_number));
- }
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- {
- struct wolfsentry_addr_family_bynumber *addr_family = NULL;
- const char *family_name;
+ WOLFSENTRY_EXIT_ON_FALSE(test_route_insert_requires_direction(WOLFSENTRY_CONTEXT_ARGS_OUT) == 0);
- WOLFSENTRY_EXIT_ON_FAILURE(
- (wolfsentry_addr_family_ntop(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET,
- &addr_family,
- &family_name)));
- WOLFSENTRY_EXIT_ON_FALSE((family_name != NULL) &&
- (! strcmp(family_name,"my_AF")));
+ WOLFSENTRY_EXIT_ON_FALSE(test_route_insert_by_exports_requires_direction(WOLFSENTRY_CONTEXT_ARGS_OUT) == 0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, addr_family, &action_results));
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
- }
+ WOLFSENTRY_EXIT_ON_FALSE(test_route_flags_sensical_validation(WOLFSENTRY_CONTEXT_ARGS_OUT) == 0);
-#endif /* WOLFSENTRY_PROTOCOL_NAMES */
+ WOLFSENTRY_EXIT_ON_FALSE(test_route_update_flags_rejects_penaltybox_and_greenlist(WOLFSENTRY_CONTEXT_ARGS_OUT) == 0);
- {
- wolfsentry_addr_family_parser_t parser;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_get_parser(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET,
- &parser));
- WOLFSENTRY_EXIT_ON_FALSE(parser == my_addr_family_parser);
- }
- {
- wolfsentry_addr_family_formatter_t formatter;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_get_formatter(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET,
- &formatter));
- WOLFSENTRY_EXIT_ON_FALSE(formatter == my_addr_family_formatter);
- }
+ WOLFSENTRY_EXIT_ON_FALSE(test_route_update_flags_protects_immutable_flags(WOLFSENTRY_CONTEXT_ARGS_OUT) == 0);
-#ifdef WOLFSENTRY_PROTOCOL_NAMES
- {
- wolfsentry_addr_family_t family_number;
- wolfsentry_addr_bits_t bits;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_pton(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "inet",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &family_number));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_max_addr_bits(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- family_number,
- &bits));
- WOLFSENTRY_EXIT_ON_FALSE(bits == 32);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_pton(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "inet6",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &family_number));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_max_addr_bits(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- family_number,
- &bits));
- WOLFSENTRY_EXIT_ON_FALSE(bits == 128);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_pton(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "link",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &family_number));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_max_addr_bits(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- family_number,
- &bits));
- WOLFSENTRY_EXIT_ON_FALSE(bits == 48);
+ WOLFSENTRY_EXIT_ON_FALSE(test_route_update_flags_protects_internal_flags(WOLFSENTRY_CONTEXT_ARGS_OUT) == 0);
- /* LINK64 must roundtrip through pton → ntop → verify label. */
- {
- struct wolfsentry_addr_family_bynumber *addr_family = NULL;
- const char *family_name = NULL;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_pton(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- "LINK64",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &family_number));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_ntop(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- family_number,
- &addr_family,
- &family_name));
- WOLFSENTRY_EXIT_ON_FALSE((family_name != NULL) && (! strcmp(family_name, "LINK64")));
- if (addr_family)
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, addr_family, &action_results));
- }
- }
-#endif /* WOLFSENTRY_PROTOCOL_NAMES */
+ WOLFSENTRY_EXIT_ON_FALSE(test_route_table_max_purgeable_routes_shrink(WOLFSENTRY_CONTEXT_ARGS_OUT) == 0);
WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
@@ -4231,24 +5263,58 @@ static int test_user_addr_families(void) {
WOLFSENTRY_RETURN_OK;
}
-#endif /* TEST_USER_ADDR_FAMILIES */
+#endif /* TEST_STATIC_ROUTES */
-#ifdef TEST_JSON
+#ifdef TEST_DYNAMIC_RULES
-#include "wolfsentry/wolfsentry_json.h"
-#ifdef WOLFSENTRY_HAVE_JSON_DOM
-#include
-#endif
+static wolfsentry_errcode_t wolfsentry_action_dummy_callback(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_context,
+ void *caller_arg,
+ const struct wolfsentry_event *event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)action;
+ (void)handler_context;
+ (void)caller_arg;
+ (void)event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+
+static int test_dynamic_rules(void) {
-static int test_json(const char *fname, const char *extra_fname) {
- wolfsentry_errcode_t ret;
struct wolfsentry_context *wolfsentry;
+#if 0
+ wolfsentry_action_res_t action_results;
+ int n_deleted;
+ int ret;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } src, dst;
+#endif
+
wolfsentry_ent_id_t id;
#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
- struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+ static const struct wolfsentry_eventconfig config2 = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 15 };
#else
- struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const struct wolfsentry_eventconfig config2 = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
#endif
WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
@@ -4262,1200 +5328,9243 @@ static int test_json(const char *fname, const char *extra_fname) {
WOLFSENTRY_INIT_FLAG_NONE));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_handler_install(
+ wolfsentry_event_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET,
- "my_AF",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- my_addr_family_parser,
- my_addr_family_formatter,
- 24 /* max_addr_bits */));
+ "connect",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_addr_family_handler_install(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_AF_USER_OFFSET + 1,
- "my_AF2",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- my_addr_family_parser,
- my_addr_family_formatter,
- 24 /* max_addr_bits */));
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_BUILTIN_LABEL_PREFIX "connect",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id),
+ NOT_PERMITTED));
- WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NO_ROUTES_OR_EVENTS, 1));
+ /* track port scanning */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "connection_refused",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "disconnect",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "authentication_succeeded",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "negotiation_abandoned",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
{
- static const char test_string[] = "hello";
- const char *value = NULL;
- int value_len = -1;
- struct wolfsentry_kv_pair_internal *kv_ref;
+ wolfsentry_ent_id_t auth_failed_id;
+ struct wolfsentry_event *auth_failed_event;
+ const struct wolfsentry_event *insertion_side_effect_demo_event;
+ const char *insertion_side_effect_demo_label;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_string(
+ wolfsentry_event_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "user-string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value,
- &value_len,
- &kv_ref));
+ "authentication_failed",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
- WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(test_string));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, test_string) == 0);
+ auth_failed_id = id;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_release_record(
+ wolfsentry_event_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- &kv_ref));
- }
-
- {
- static const char user_cert_string[] =
- "-----BEGIN CERTIFICATE-----\n"
- "MIIDnzCCAyWgAwIBAgICEAEwCgYIKoZIzj0EAwMwgZcxCzAJBgNVBAYTAlVTMRMw\n"
- "EQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAd3\n"
- "b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEYMBYGA1UEAwwPd3d3LndvbGZz\n"
- "c2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMCAXDTIyMDIx\n"
- "NTEyNTAyNFoYDzIwNTIwMjA4MTI1MDI0WjCBlTELMAkGA1UEBhMCVVMxEzARBgNV\n"
- "BAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxEDAOBgNVBAoMB0VsaXB0\n"
- "aWMxEjAQBgNVBAsMCUVDQzM4NFNydjEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29t\n"
- "MR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMHYwEAYHKoZIzj0CAQYF\n"
- "K4EEACIDYgAE6s+TTywJuzkUD1Zkw0C03w5jruVxSwDMBJf/4ek4lrtfkbJqzLU5\n"
- "X49wWfEB9lorAWxoC89VJa9tmEgKqHTJqRegDMP70yNo/gQ8Y1CIO7lPfGc09zup\n"
- "c+cbw1FeIhjso4IBQDCCATwwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAw\n"
- "HQYDVR0OBBYEFII78mUv87QAxrwG/XlCdUtl0c68MIHXBgNVHSMEgc8wgcyAFKvg\n"
- "wyZMGNRyu9KEjJwKBZKAElNSoYGdpIGaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UE\n"
- "CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEQMA4GA1UECgwHd29sZlNT\n"
- "TDEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNv\n"
- "bTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbYIUaiYbTQIaM//CRxT5\n"
- "51VgWi5/ESkwDgYDVR0PAQH/BAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAoG\n"
- "CCqGSM49BAMDA2gAMGUCMQCEPZBU/y/EetTYGOKzLbtCN0CmHwmD3rwEeoLcVRdC\n"
- "XBeqB0LcyPZQzRS3Bhk5HyQCMBNiS5/JoIzSac8WToa9nik4ROlKOmOgZjiV4n3j\n"
- "F+yUIbg9aV7K5ISc2mF9G1G/0Q==\n"
- "-----END CERTIFICATE-----\n";
- const char *value = NULL;
- int value_len = -1;
- struct wolfsentry_kv_pair_internal *kv_ref;
+ "insertion_side_effect_demo",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_string(
+ wolfsentry_event_set_aux_event(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "user-cert-string",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value,
- &value_len,
- &kv_ref));
-
- WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(user_cert_string));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, user_cert_string) == 0);
+ "authentication_failed",
+ -1,
+ "insertion_side_effect_demo",
+ -1));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_release_record(
+ wolfsentry_event_get_reference(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- &kv_ref));
- }
+ "authentication_failed",
+ -1,
+ &auth_failed_event));
- WOLFSENTRY_EXIT_ON_FAILURE(load_test_action_handlers(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE(auth_failed_id == wolfsentry_get_object_id(auth_failed_event));
- WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_DRY_RUN, 1));
+ WOLFSENTRY_EXIT_ON_FALSE(
+ insertion_side_effect_demo_event = wolfsentry_event_get_aux_event(auth_failed_event)
+ );
- WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT, 1));
+ WOLFSENTRY_EXIT_ON_FALSE(
+ insertion_side_effect_demo_label = wolfsentry_event_get_label(insertion_side_effect_demo_event)
+ );
- WOLFSENTRY_EXIT_ON_SUCCESS(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT, 0));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(insertion_side_effect_demo_label, "insertion_side_effect_demo") == 0);
- {
- static const char *bad_json[] = {
- "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"user-string\" : \"should collide\" } }",
- "{ \"wolfsentry-config-version\" : 2, \"user-values\" : { \"ok-user-string\" : \"shouldn't collide\" } }",
- "{ \"wolfsentry-config-version\" : 1, \"not-user-values\" : { \"ok-user-string\" : \"shouldn't collide\" } }",
- "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"too-long-user-string-123456789-abcdefghi\" : \"x\" } }",
- };
- const char **bad_json_i;
- for (bad_json_i = bad_json; bad_json_i < &bad_json[length_of_array(bad_json)]; ++bad_json_i) {
- WOLFSENTRY_EXIT_ON_SUCCESS(
- wolfsentry_config_json_oneshot(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- (const unsigned char *)*bad_json_i,
- strlen(*bad_json_i),
- WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT,
- NULL,
- 0));
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_drop_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ auth_failed_event,
+ NULL /* action_results */));
}
- {
- struct wolfsentry_route_table *main_routes, main_routes_copy;
- const char *value = NULL;
- int value_len = -1;
- struct wolfsentry_kv_pair_internal *kv_ref;
- struct {
- struct wolfsentry_sockaddr sa;
- byte addr_buf[4];
- } remote, local;
- wolfsentry_ent_id_t route_id;
- wolfsentry_route_flags_t inexact_matches;
- wolfsentry_action_res_t action_results;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "match_side_effect_demo",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "deletion_side_effect_demo",
+ -1 /* label_len */,
+ 10,
+ &config2,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
- main_routes_copy = *main_routes;
+ {
+ struct wolfsentry_eventconfig eventconfig;
- if (extra_fname) {
- remote.sa.sa_family = local.sa.sa_family = AF_INET;
- remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
- remote.sa.sa_port = 12345;
- local.sa.sa_port = 13579;
- remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
- remote.sa.interface = local.sa.interface = 1;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "deletion_side_effect_demo",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &eventconfig));
- memcpy(remote.sa.addr,"\12\24\36\50",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\62\74\106\120",sizeof local.addr_buf);
+ WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == config2.route_private_data_size) &&
+ (eventconfig.route_private_data_alignment == config2.route_private_data_alignment) &&
+ (eventconfig.max_connection_count == config2.max_connection_count));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- NULL /* event_label */,
- 0 /* event_label_len */,
- NULL /* caller_arg */,
- &route_id,
- &inexact_matches,
- &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(route_id == WOLFSENTRY_ENT_ID_NONE);
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_FALLTHROUGH));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "deletion_side_effect_demo",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config));
- WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, extra_fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT, 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "deletion_side_effect_demo",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &eventconfig));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- NULL /* event_label */,
- 0 /* event_label_len */,
- NULL /* caller_arg */,
- &route_id,
- &inexact_matches,
- &action_results));
- WOLFSENTRY_EXIT_ON_TRUE(route_id == WOLFSENTRY_ENT_ID_NONE);
- WOLFSENTRY_EXIT_ON_FALSE(action_results == WOLFSENTRY_ACTION_RES_ACCEPT);
-
- memcpy(remote.sa.addr,"\13\24\36\50",sizeof remote.addr_buf);
-
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- NULL /* event_label */,
- 0 /* event_label_len */,
- NULL /* caller_arg */,
- &route_id,
- &inexact_matches,
- &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(route_id == WOLFSENTRY_ENT_ID_NONE);
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_FALLTHROUGH));
- } else {
- static const char *trivial_test_json = "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"extra-user-string\" : \"extra hello\" } }";
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_config_json_oneshot(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- (const unsigned char *)trivial_test_json,
- strlen(trivial_test_json),
- WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT,
- NULL,
- 0));
- }
-
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
-
- WOLFSENTRY_EXIT_ON_FALSE((main_routes->max_purgeable_routes == main_routes_copy.max_purgeable_routes) &&
- (main_routes->default_policy == main_routes_copy.default_policy));
-
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == config.route_private_data_size) &&
+ (eventconfig.route_private_data_alignment == config.route_private_data_alignment) &&
+ (eventconfig.max_connection_count == config.max_connection_count));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_string(
+ wolfsentry_event_delete(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "user-string",
+ "deletion_side_effect_demo",
WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value,
- &value_len,
- &kv_ref));
-
- WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen("hello"));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, "hello") == 0);
-
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_release_record(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &kv_ref));
-
+ NULL /* action_results */));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_get_string(
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ wolfsentry_event_delete(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- "extra-user-string",
+ "deletion_side_effect_demo",
WOLFSENTRY_LENGTH_NULL_TERMINATED,
- &value,
- &value_len,
- &kv_ref));
+ NULL /* action_results */));
+ }
- WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen("extra hello"));
- WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, "extra hello") == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "insert_always",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_user_value_release_record(
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_action_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- &kv_ref));
- }
+ WOLFSENTRY_BUILTIN_LABEL_PREFIX "insert_always",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id),
+ NOT_PERMITTED));
- WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "insert_alway",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
{
- struct wolfsentry_route_table *main_routes, main_routes_copy;
- struct wolfsentry_cursor *cursor;
- WOLFSENTRY_BYTE_STREAM_DECLARE_STACK(json_out, 16384);
- WOLFSENTRY_BYTE_STREAM_DECLARE_HEAP(json_out2, 16384);
- wolfsentry_hitcount_t n_seen = 0;
- char err_buf[512];
-
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_BYTE_STREAM_INIT_HEAP(json_out2) != NULL);
+ static char too_long_label[WOLFSENTRY_MAX_LABEL_BYTES + 2];
+ memset(too_long_label, 'x', sizeof too_long_label - 1);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ too_long_label[sizeof too_long_label - 1] = 0;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ STRING_ARG_TOO_LONG,
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ too_long_label,
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
- WOLFSENTRY_BYTE_STREAM_RESET(json_out);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ STRING_ARG_TOO_LONG,
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ too_long_label,
+ sizeof too_long_label - 1,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_table_dump_json_start(
+ wolfsentry_action_insert(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- &cursor,
- WOLFSENTRY_BYTE_STREAM_PTR(json_out),
- WOLFSENTRY_BYTE_STREAM_SPC(json_out),
- WOLFSENTRY_FORMAT_FLAG_NONE));
+ too_long_label,
+ sizeof too_long_label - 2,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
- for (;;) {
- ret = wolfsentry_route_table_dump_json_next(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- cursor,
- WOLFSENTRY_BYTE_STREAM_PTR(json_out),
- WOLFSENTRY_BYTE_STREAM_SPC(json_out),
- WOLFSENTRY_FORMAT_FLAG_NONE);
- if (ret < 0) {
- if (! WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND))
- WOLFSENTRY_EXIT_ON_FAILURE(ret);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_table_dump_json_end(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- &cursor,
- WOLFSENTRY_BYTE_STREAM_PTR(json_out),
- WOLFSENTRY_BYTE_STREAM_SPC(json_out),
- WOLFSENTRY_FORMAT_FLAG_NONE));
- } else
- ++n_seen;
- if (ret < 0)
- break;
- }
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ STRING_ARG_TOO_LONG,
+ wolfsentry_action_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ too_long_label,
+ sizeof too_long_label - 1,
+ NULL /* action_results */));
- WOLFSENTRY_EXIT_ON_FALSE(n_seen == wolfsentry->routes->header.n_ents);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ STRING_ARG_TOO_LONG,
+ wolfsentry_action_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ too_long_label,
+ -1,
+ NULL /* action_results */));
- main_routes_copy = *main_routes;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_action_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL,
+ -1,
+ NULL /* action_results */));
- ret = wolfsentry_config_json_oneshot(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_BYTE_STREAM_HEAD(json_out),
- WOLFSENTRY_BYTE_STREAM_LEN(json_out),
- WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT | WOLFSENTRY_CONFIG_LOAD_FLAG_FLUSH_ONLY_ROUTES,
- err_buf,
- sizeof err_buf);
- if (ret < 0) {
- (void)fprintf(stderr, "%.*s\n", (int)sizeof err_buf, err_buf);
- WOLFSENTRY_EXIT_ON_FAILURE(ret);
- }
+ too_long_label[sizeof too_long_label - 2] = 0;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_ALREADY_PRESENT,
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ too_long_label,
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
- WOLFSENTRY_EXIT_ON_FALSE((main_routes->max_purgeable_routes == main_routes_copy.max_purgeable_routes) &&
- (main_routes->default_policy == main_routes_copy.default_policy));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL,
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
- WOLFSENTRY_BYTE_STREAM_RESET(json_out2);
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ too_long_label,
+ 0 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_table_dump_json_start(
+ wolfsentry_action_delete(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- &cursor,
+ too_long_label,
+ -1 /* label_len */,
+ NULL /* action_results */));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "set_connect_wildcards",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "set_connectionreset_wildcards",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "increment_derogatory",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "increment_commendable",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "check_counts",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ {
+ struct wolfsentry_action *action;
+ wolfsentry_action_flags_t flags;
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_NOT_FOUND,
+ wolfsentry_action_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "checXXXounts",
+ -1 /* label_len */,
+ &action));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_action_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "checXXXounts",
+ 0 /* label_len */,
+ &action));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ STRING_ARG_TOO_LONG,
+ wolfsentry_action_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "checXXXounts",
+ WOLFSENTRY_MAX_LABEL_BYTES + 1 /* label_len */,
+ &action));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "check_counts",
+ -1 /* label_len */,
+ &action));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_get_flags(
+ action,
+ &flags));
+ WOLFSENTRY_EXIT_ON_FALSE(flags == WOLFSENTRY_ACTION_FLAG_NONE);
+
+ {
+ wolfsentry_action_flags_t flags_before, flags_after;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_update_flags(
+ action,
+ WOLFSENTRY_ACTION_FLAG_DISABLED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ &flags_before,
+ &flags_after));
+ WOLFSENTRY_EXIT_ON_FALSE(flags_before == WOLFSENTRY_ACTION_FLAG_NONE);
+ WOLFSENTRY_EXIT_ON_FALSE(flags_after == WOLFSENTRY_ACTION_FLAG_DISABLED);
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_get_flags(
+ action,
+ &flags));
+ WOLFSENTRY_EXIT_ON_FALSE(flags == WOLFSENTRY_ACTION_FLAG_DISABLED);
+
+ {
+ wolfsentry_action_flags_t flags_before, flags_after;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_update_flags(
+ action,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ WOLFSENTRY_ACTION_FLAG_DISABLED,
+ &flags_before,
+ &flags_after));
+ WOLFSENTRY_EXIT_ON_FALSE(flags_before == WOLFSENTRY_ACTION_FLAG_DISABLED);
+ WOLFSENTRY_EXIT_ON_FALSE(flags_after == WOLFSENTRY_ACTION_FLAG_NONE);
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_get_flags(
+ action,
+ &flags));
+ WOLFSENTRY_EXIT_ON_FALSE(flags == WOLFSENTRY_ACTION_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, action, NULL));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "add_to_greenlist",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "del_from_greenlist",
+ -1 /* label_len */,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "match_side_effect_demo",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "del_from_greenlist",
+ -1));
+
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ wolfsentry_event_action_prepend(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "match_side_effect_demo",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "del_from_greenlist",
+ -1));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_prepend(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "match_side_effect_demo",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "add_to_greenlist",
+ -1));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_insert_after(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "match_side_effect_demo",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "check_counts",
+ -1,
+ "add_to_greenlist",
+ -1));
+
+ {
+ struct wolfsentry_action_list_ent *cursor;
+ const char *action_label;
+ int action_label_len;
+ static const char *labels[] = { "add_to_greenlist", "check_counts", "del_from_greenlist" };
+ int label_i = 0;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_list_start(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "match_side_effect_demo",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ &cursor));
+
+ while (wolfsentry_event_action_list_next(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &cursor,
+ &action_label,
+ &action_label_len) >= 0) {
+ WOLFSENTRY_EXIT_ON_TRUE((size_t)label_i >= sizeof labels / sizeof labels[0]);
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(action_label, labels[label_i++]) == 0);
+ }
+
+ WOLFSENTRY_EXIT_ON_FALSE(label_i == sizeof labels / sizeof labels[0]);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_list_done(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &cursor));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "match_side_effect_demo",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "del_from_greenlist",
+ -1));
+
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t action_dispatch_counter_handler(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_arg,
+ void *caller_arg,
+ const struct wolfsentry_event *trigger_event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)action;
+ (void)caller_arg;
+ (void)trigger_event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+ if (handler_arg != NULL)
+ ++*(int *)handler_arg;
+ WOLFSENTRY_RETURN_OK;
+}
+
+static void dynamic_rules_fill_sockaddrs(
+ struct wolfsentry_sockaddr *remote,
+ struct wolfsentry_sockaddr *local,
+ size_t addr_bytes)
+{
+ remote->sa_family = local->sa_family = AF_INET;
+ remote->sa_proto = local->sa_proto = IPPROTO_TCP;
+ remote->sa_port = 12345;
+ local->sa_port = 443;
+ remote->addr_len = local->addr_len = (wolfsentry_addr_bits_t)(addr_bytes * BITS_PER_BYTE);
+ remote->interface = local->interface = 1;
+ memcpy(remote->addr, "\1\2\3\4", addr_bytes);
+ memcpy(local->addr, "\4\3\2\1", addr_bytes);
+}
+
+static int test_dispatch_inhibited_insert_actions(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ struct wolfsentry_route *route = NULL;
+ wolfsentry_route_flags_t route_flags;
+ int insert_calls = 0;
+ int delete_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test-insert-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ action_dispatch_counter_handler,
+ &insert_calls,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ev-insert",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ev-insert",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_INSERT,
+ "test-insert-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test-delete-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ action_dispatch_counter_handler,
+ &delete_calls,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ev-insert",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_DELETE,
+ "test-delete-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_inhibit_actions(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert_and_check_out(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "ev-insert",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &route,
+ &action_results));
+
+ /* actions were inhibited at insert time, so the handler must not have run,
+ * and the route must not be marked as having run its insert actions.
+ */
+ WOLFSENTRY_EXIT_ON_FALSE(insert_calls == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(route, &route_flags));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_INSERT_ACTIONS_CALLED));
+
+ /* re-enable actions and run the deferred insert actions; the handler must
+ * now run exactly once for the previously inhibited route.
+ */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_enable_actions(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_bulk_insert_actions(WOLFSENTRY_CONTEXT_ARGS_OUT, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(insert_calls == 1);
+
+ /* with actions inhibited again, deleting the route must not run its delete
+ * handler nor mark the route as having run its delete actions.
+ */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_inhibit_actions(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, wolfsentry_get_object_id(route),
+ NULL /* trigger_label */, 0 /* trigger_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(delete_calls == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(route, &route_flags));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_DELETE_ACTIONS_CALLED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route, NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+struct reentrant_delete_ctx {
+ const char *event_label;
+ const char *action_label;
+ wolfsentry_action_type_t which_action_list;
+ int call_count;
+ wolfsentry_errcode_t delete_ret;
+};
+
+static wolfsentry_errcode_t action_reentrant_delete_handler(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_arg,
+ void *caller_arg,
+ const struct wolfsentry_event *trigger_event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ struct reentrant_delete_ctx *c = (struct reentrant_delete_ctx *)handler_arg;
+
+ (void)action;
+ (void)caller_arg;
+ (void)trigger_event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+
+ if (c != NULL) {
+ ++c->call_count;
+ c->delete_ret = wolfsentry_event_action_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ c->event_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ c->which_action_list,
+ c->action_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED);
+ }
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dispatch_reentrant_action_delete(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id;
+ struct reentrant_delete_ctx sdctx;
+ int after_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ memset(&sdctx, 0, sizeof sdctx);
+ sdctx.event_label = "ev-match";
+ sdctx.action_label = "self-del-action";
+ sdctx.which_action_list = WOLFSENTRY_ACTION_TYPE_MATCH;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "self-del-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ action_reentrant_delete_handler,
+ &sdctx,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "after-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ action_dispatch_counter_handler,
+ &after_calls,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ev-match",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ /* a self-deleting action followed by another action in the same list: the
+ * dispatch loop must survive the first handler deleting its own entry and
+ * still reach the second action.
+ */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ev-match",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "self-del-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ev-match",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "after-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "ev-match",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id,
+ &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id,
+ &inexact_matches,
+ &action_results));
+
+ /* a handler's attempt to delete its own live entry is refused, and
+ * iteration still reaches the following action.
+ */
+ WOLFSENTRY_EXIT_ON_FALSE(sdctx.call_count == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(sdctx.delete_ret, BUSY));
+ WOLFSENTRY_EXIT_ON_FALSE(after_calls == 1);
+
+ /* now the first handler targets the SUCCESSOR entry: that delete must also
+ * be refused, and the successor must still run.
+ */
+ sdctx.call_count = 0;
+ sdctx.action_label = "after-action";
+ after_calls = 0;
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(sdctx.call_count == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(sdctx.delete_ret, BUSY));
+ WOLFSENTRY_EXIT_ON_FALSE(after_calls == 1);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+struct event_del_ctx {
+ const char *event_label;
+ int call_count;
+ wolfsentry_errcode_t del_ret;
+};
+
+static wolfsentry_errcode_t action_delete_own_event_handler(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_arg,
+ void *caller_arg,
+ const struct wolfsentry_event *trigger_event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ struct event_del_ctx *c = (struct event_del_ctx *)handler_arg;
+
+ (void)action;
+ (void)caller_arg;
+ (void)trigger_event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+
+ if (c != NULL) {
+ ++c->call_count;
+ c->del_ret = wolfsentry_event_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ c->event_label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL /* action_results */);
+ }
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t action_error_handler(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_arg,
+ void *caller_arg,
+ const struct wolfsentry_event *trigger_event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)action;
+ (void)caller_arg;
+ (void)trigger_event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+ if (handler_arg != NULL)
+ ++*(int *)handler_arg;
+ WOLFSENTRY_ERROR_RETURN(INVALID_ARG);
+}
+
+static int test_dispatch_handler_deletes_own_event(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id;
+ struct event_del_ctx edctx;
+ struct wolfsentry_event *gone_ref = NULL;
+ int after_calls = 0;
+ int probe_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ memset(&edctx, 0, sizeof edctx);
+ edctx.event_label = "ev-selfev";
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings, WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */, &wolfsentry, WOLFSENTRY_INIT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "evdel-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_delete_own_event_handler, &edctx, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "after-ev-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &after_calls, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "probe-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &probe_calls, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-selfev", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-selfev", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "evdel-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-selfev", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "after-ev-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ /* a separate event used only to prove the dispatch guard is released. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-probe", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-probe", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "probe-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa,
+ flags, "ev-selfev", WOLFSENTRY_LENGTH_NULL_TERMINATED, &id, &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+
+ /* the handler deleted its own event mid-dispatch; the delete succeeded and
+ * the event is gone from the table, yet the held event reference kept the
+ * list valid so iteration reached the following action.
+ */
+ WOLFSENTRY_EXIT_ON_FALSE(edctx.call_count == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_IS_SUCCESS(edctx.del_ret));
+ WOLFSENTRY_EXIT_ON_FALSE(after_calls == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-selfev",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED, &gone_ref),
+ ITEM_NOT_FOUND));
+
+ /* the dispatch released its guard, so an unrelated list is now mutable. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-probe", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "probe-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dispatch_handler_error(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id;
+ int err_calls = 0;
+ int after_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings, WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */, &wolfsentry, WOLFSENTRY_INIT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "err-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_error_handler, &err_calls, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "after-err-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &after_calls, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-err", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-err", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "err-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-err", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "after-err-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa,
+ flags, "ev-err", WOLFSENTRY_LENGTH_NULL_TERMINATED, &id, &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+
+ /* the first handler returned an error, taking the loop's error-cleanup
+ * exit, so the following action did not run.
+ */
+ WOLFSENTRY_EXIT_ON_FALSE(err_calls == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(after_calls == 0);
+
+ /* the error exit still released the dispatch guard, so the list is mutable. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-err", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "err-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+struct ctx_exchange_ctx {
+ struct wolfsentry_context *peer;
+ int call_count;
+ wolfsentry_errcode_t exchange_ret;
+};
+
+static wolfsentry_errcode_t action_context_exchange_handler(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_arg,
+ void *caller_arg,
+ const struct wolfsentry_event *trigger_event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ struct ctx_exchange_ctx *c = (struct ctx_exchange_ctx *)handler_arg;
+
+ (void)action;
+ (void)caller_arg;
+ (void)trigger_event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+
+ if (c != NULL) {
+ ++c->call_count;
+ c->exchange_ret = wolfsentry_context_exchange(WOLFSENTRY_CONTEXT_ARGS_OUT, c->peer);
+ }
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dispatch_context_exchange_during_dispatch(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_context *peer = NULL;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id;
+ struct ctx_exchange_ctx cectx;
+ int after_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ memset(&cectx, 0, sizeof cectx);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings, WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */, &wolfsentry, WOLFSENTRY_INIT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "exch-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_context_exchange_handler, &cectx, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "after-exch-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &after_calls, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-exch", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-exch", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "exch-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-exch", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "after-exch-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa,
+ flags, "ev-exch", WOLFSENTRY_LENGTH_NULL_TERMINATED, &id, &action_results));
+
+ /* a peer context to attempt to swap in from the handler. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &peer, WOLFSENTRY_CLONE_FLAG_NONE));
+ cectx.peer = peer;
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+
+ /* a context exchange attempted mid-dispatch must be refused, and the
+ * following action must still run.
+ */
+ WOLFSENTRY_EXIT_ON_FALSE(cectx.call_count == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(cectx.exchange_ret, BUSY));
+ WOLFSENTRY_EXIT_ON_FALSE(after_calls == 1);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&peer)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dispatch_inhibit_actions_flag(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id;
+ int match_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "inh-match-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ action_dispatch_counter_handler,
+ &match_calls,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ev-inh",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ev-inh",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "inh-match-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "ev-inh",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id,
+ &action_results));
+
+ /* with actions inhibited, the matched route's handler must not fire. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_inhibit_actions(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(match_calls == 0);
+
+ /* with actions re-enabled, the same dispatch must fire the handler. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_enable_actions(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(match_calls == 1);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_action_drop_reference_clears_results(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ struct wolfsentry_action *action = NULL;
+ wolfsentry_action_res_t action_results;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "drop-ref-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ action_dispatch_counter_handler,
+ NULL,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "drop-ref-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &action));
+
+ /* with a reference still held, a non-final drop must clear stale caller
+ * bits rather than leaving them, matching the event and route drop APIs.
+ */
+ action_results = WOLFSENTRY_ACTION_RES_DEALLOCATED | WOLFSENTRY_ACTION_RES_REJECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, action, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == WOLFSENTRY_ACTION_RES_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t action_set_stop_handler(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_arg,
+ void *caller_arg,
+ const struct wolfsentry_event *trigger_event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)action;
+ (void)caller_arg;
+ (void)trigger_event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ if (handler_arg != NULL)
+ ++*(int *)handler_arg;
+ WOLFSENTRY_SET_BITS(*action_results, WOLFSENTRY_ACTION_RES_STOP);
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dispatch_action_stop(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id;
+ int stop_calls = 0;
+ int after_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "stop-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_set_stop_handler, &stop_calls, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "after-stop-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &after_calls, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-stop", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-stop", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "stop-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-stop", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "after-stop-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa,
+ flags, "ev-stop", WOLFSENTRY_LENGTH_NULL_TERMINATED, &id, &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+
+ /* the first match action signals stop, so the second must be skipped. */
+ WOLFSENTRY_EXIT_ON_FALSE(stop_calls == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(after_calls == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dispatch_disabled_action(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id;
+ struct wolfsentry_action *disabled_action = NULL;
+ wolfsentry_action_flags_t flags_before, flags_after;
+ int disabled_calls = 0;
+ int enabled_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "disabled-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &disabled_calls, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "enabled-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &enabled_calls, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-dis", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-dis", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "disabled-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-dis", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "enabled-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "disabled-action", WOLFSENTRY_LENGTH_NULL_TERMINATED, &disabled_action));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_update_flags(
+ disabled_action, WOLFSENTRY_ACTION_FLAG_DISABLED, WOLFSENTRY_ACTION_FLAG_NONE, &flags_before, &flags_after));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa,
+ flags, "ev-dis", WOLFSENTRY_LENGTH_NULL_TERMINATED, &id, &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+
+ /* the disabled action must be skipped while the enabled one fires. */
+ WOLFSENTRY_EXIT_ON_FALSE(disabled_calls == 0);
+ WOLFSENTRY_EXIT_ON_FALSE(enabled_calls == 1);
+
+ /* clearing the disabled flag must let the action run again. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_update_flags(
+ disabled_action, WOLFSENTRY_ACTION_FLAG_NONE, WOLFSENTRY_ACTION_FLAG_DISABLED, &flags_before, &flags_after));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(disabled_calls == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(enabled_calls == 2);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, disabled_action, NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dispatch_dont_count_hits(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id;
+ struct wolfsentry_action *hc_action = NULL;
+ int hc_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t counted_flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ wolfsentry_route_flags_t uncounted_flags = counted_flags | WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_HITS;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "hc-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &hc_calls, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-hc", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-hc", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH, "hc-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "hc-action", WOLFSENTRY_LENGTH_NULL_TERMINATED, &hc_action));
+
+ /* a route with DONT_COUNT_HITS: its matched action's handler runs but its
+ * hitcount must not advance.
+ */
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa,
+ uncounted_flags, "ev-hc", WOLFSENTRY_LENGTH_NULL_TERMINATED, &id, &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, uncounted_flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(hc_calls == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(hc_action->header.hitcount == 0);
+
+ /* a route without the flag advances the hitcount on dispatch. */
+ memcpy(remote.sa.addr, "\5\6\7\10", sizeof remote.addr_buf);
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa,
+ counted_flags, "ev-hc", WOLFSENTRY_LENGTH_NULL_TERMINATED, &id, &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &remote.sa, &local.sa, counted_flags,
+ NULL, 0, NULL, &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(hc_calls == 2);
+ WOLFSENTRY_EXIT_ON_FALSE(hc_action->header.hitcount == 1);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, hc_action, NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dispatch_insert_delete_already_guard(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ struct wolfsentry_route *route = NULL;
+ wolfsentry_route_flags_t route_flags;
+ int ins_calls = 0;
+ int del_calls = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ins-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &ins_calls, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "del-action", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE, action_dispatch_counter_handler, &del_calls, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-ad", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-ad", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_INSERT, "ins-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "ev-ad", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_DELETE, "del-action", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ dynamic_rules_fill_sockaddrs(&remote.sa, &local.sa, sizeof remote.addr_buf);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert_and_check_out(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &remote.sa, &local.sa,
+ flags, "ev-ad", WOLFSENTRY_LENGTH_NULL_TERMINATED, &route, &action_results));
+
+ /* insert actions run once and the route is marked accordingly. */
+ WOLFSENTRY_EXIT_ON_FALSE(ins_calls == 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(route, &route_flags));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_INSERT_ACTIONS_CALLED));
+
+ /* a repeated insert pass must not re-run them while the flag is set. */
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_bulk_insert_actions(WOLFSENTRY_CONTEXT_ARGS_OUT, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(ins_calls == 1);
+
+ /* clearing the status reopens the insert actions for that route. */
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_bulk_clear_insert_action_status(WOLFSENTRY_CONTEXT_ARGS_OUT, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(route, &route_flags));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_INSERT_ACTIONS_CALLED));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_bulk_insert_actions(WOLFSENTRY_CONTEXT_ARGS_OUT, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(ins_calls == 2);
+
+ /* deleting the route runs its delete actions exactly once and marks the
+ * route so the delete-side guard would reject a repeat dispatch.
+ */
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, wolfsentry_get_object_id(route),
+ NULL /* trigger_label */, 0 /* trigger_label_len */, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(del_calls == 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(route, &route_flags));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_DELETE_ACTIONS_CALLED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, route, NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+static wolfsentry_errcode_t test_regression_bitmask_match_specificity(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_ent_id_t route_id, broad_id, narrow_id;
+ struct wolfsentry_route_exports broad_route, narrow_route;
+ byte broad_addr[8], narrow_addr[8];
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ static const wolfsentry_route_flags_t common_flags =
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_ADDR_BITMASK
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(&broad_route, 0, sizeof broad_route);
+ memset(&narrow_route, 0, sizeof narrow_route);
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+
+ /* broad bitmask route 10.0.0.0/255.0.0.0, greenlisted (accept). */
+ memcpy(broad_addr, "\x0A\x00\x00\x00\xFF\x00\x00\x00", 8); /* NOLINT(bugprone-not-null-terminated-result) */
+ broad_route.sa_family = AF_INET;
+ broad_route.sa_proto = IPPROTO_TCP;
+ broad_route.remote_address = broad_addr;
+ broad_route.remote.addr_len = 64;
+ broad_route.flags = common_flags | WOLFSENTRY_ROUTE_FLAG_GREENLISTED;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &broad_route,
+ &broad_id, &action_results));
+
+ /* narrow bitmask route 10.1.2.0/255.255.255.0, penaltyboxed (reject). */
+ memcpy(narrow_addr, "\x0A\x01\x02\x00\xFF\xFF\xFF\x00", 8); /* NOLINT(bugprone-not-null-terminated-result) */
+ narrow_route.sa_family = AF_INET;
+ narrow_route.sa_proto = IPPROTO_TCP;
+ narrow_route.remote_address = narrow_addr;
+ narrow_route.remote.addr_len = 64;
+ narrow_route.flags = common_flags | WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &narrow_route,
+ &narrow_id, &action_results));
+
+ (void)broad_id;
+
+ /* 10.1.2.3 matches both; the more specific /24 route must win, so the
+ * verdict is reject, not accept.
+ */
+ remote.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.addr_len = 32;
+ memcpy(remote.sa.addr, "\x0A\x01\x02\x03", 4); /* NOLINT(bugprone-not-null-terminated-result) */
+ local.sa.sa_family = AF_INET;
+ local.sa.sa_proto = IPPROTO_TCP;
+ local.sa.addr_len = 0;
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == narrow_id);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ /* non-nested masks: 192.0.0.1 inexactly matches a two-bit mask whose
+ * address is closer to it and a three-bit mask whose address is not; the
+ * three-bit mask constrains more bits, so it must win.
+ */
+ memcpy(broad_addr, "\xC0\x00\x00\x00\xC0\x00\x00\x00", 8); /* NOLINT(bugprone-not-null-terminated-result) */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &broad_route,
+ &broad_id, &action_results));
+ memcpy(narrow_addr, "\x00\x00\x00\x00\x38\x00\x00\x00", 8); /* NOLINT(bugprone-not-null-terminated-result) */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &narrow_route,
+ &narrow_id, &action_results));
+
+ memcpy(remote.sa.addr, "\xC0\x00\x00\x01", 4); /* NOLINT(bugprone-not-null-terminated-result) */
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == narrow_id);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+#endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */
+
+/* a stale purge either purged something or had nothing due */
+static int regression_purge_ok(wolfsentry_errcode_t ret) {
+ return WOLFSENTRY_IS_SUCCESS(ret) || WOLFSENTRY_ERROR_CODE_IS(ret, ALREADY);
+}
+
+static wolfsentry_errcode_t test_regression_stale_purge_connected_idle(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *table;
+ struct wolfsentry_route *route;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches, route_flags;
+ wolfsentry_ent_id_t id, id_b, route_id;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, remote_b, local;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.addr_len = 32;
+ memcpy(remote.sa.addr, "\x0A\x09\x08\x07", 4); /* NOLINT(bugprone-not-null-terminated-result) */
+ local.sa.sa_family = AF_INET;
+ local.sa.sa_proto = IPPROTO_TCP;
+ local.sa.addr_len = 0;
+
+ WOLFSENTRY_CLEAR_ALL_BITS(route_flags);
+ WOLFSENTRY_SET_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS
+ | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, route_flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
+
+ /* a second route with no connection, less stale than the first; it sits
+ * ahead of the protected route in the purge list and must still be purged. */
+ memcpy(&remote_b, &remote, sizeof remote_b);
+ memcpy(remote_b.sa.addr, "\x0A\x09\x08\x06", 4); /* NOLINT(bugprone-not-null-terminated-result) */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote_b.sa, &local.sa, route_flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id_b, &action_results));
+
+ /* idle-time limit far larger than this test's wall-clock runtime, so a
+ * connected route is within the limit and must be preserved by a purge. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &table));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_max_purgeable_idle_time_set(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, table, (wolfsentry_time_t)1 << 30));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ /* open a connection on the route (connection_count -> 1). */
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_SET_BITS(action_results, WOLFSENTRY_ACTION_RES_CONNECT);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, route_flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id, &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == id);
+
+ /* mark the protected route as the most stale entry (purge_after 1), and the
+ * unconnected route as less stale (purge_after 2); both are in the past. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_purge_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, route, 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id_b, (struct wolfsentry_table_ent_header **)&route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_purge_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, route, 2));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FALSE(regression_purge_ok(wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ /* the route's live connection is within the idle limit, so it must survive. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&route));
+ /* the unconnected route behind it must not be stranded by the purge. */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_NOT_FOUND,
+ wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id_b, (struct wolfsentry_table_ent_header **)&route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_dispatch_error_not_masked(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches, flags;
+ wolfsentry_ent_id_t id;
+ wolfsentry_errcode_t ret;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = AF_INET;
+ remote.sa.addr_len = 0;
+ local.sa.sa_family = AF_INET;
+ local.sa.addr_len = 0;
+
+ /* a family wildcard without the remote/local/proto wildcards it requires is
+ * rejected while the ephemeral target route is built, before any rule is
+ * found; the dispatcher must surface that error, not report a successful
+ * default-policy fallthrough.
+ */
+ WOLFSENTRY_CLEAR_ALL_BITS(flags);
+ WOLFSENTRY_SET_BITS(flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_SA_FAMILY_WILDCARD);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ ret = wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id, &inexact_matches, &action_results);
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_IS_FAILURE(ret));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ERROR));
+ /* the default policy is unset (NONE) here, so the failure must fail closed
+ * with an explicit reject rather than leaving the packet to be allowed. */
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_copy_metadata_purge_list(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_context *ctx_clone = NULL;
+ struct wolfsentry_route *route;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t route_flags;
+ wolfsentry_ent_id_t id;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.addr_len = 32;
+ memcpy(remote.sa.addr, "\x0A\x05\x06\x07", 4); /* NOLINT(bugprone-not-null-terminated-result) */
+ local.sa.sa_family = AF_INET;
+ local.sa.sa_proto = IPPROTO_TCP;
+ local.sa.addr_len = 0;
+
+ WOLFSENTRY_CLEAR_ALL_BITS(route_flags);
+ WOLFSENTRY_SET_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS
+ | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, route_flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
+
+ /* clone before the route has a purge time, so the clone's copy of the route
+ * is not in the clone's purge list. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NONE));
+
+ /* give the live route a (stale) purge time, so copy_metadata will propagate
+ * a nonzero purge_after into the clone's not-yet-listed route. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_purge_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, route, 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ /* swap the clone in, copying the live metadata across. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_exchange(WOLFSENTRY_CONTEXT_ARGS_OUT, ctx_clone));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone)));
+
+ /* the swapped-in route now has a stale purge_after, so it must be reachable
+ * from the purge list and get purged. */
+ WOLFSENTRY_EXIT_ON_FALSE(regression_purge_ok(wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_NOT_FOUND,
+ wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_route_api_invariants(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *table;
+ struct wolfsentry_route *route;
+ const struct wolfsentry_route *fallthrough_route;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t inexact_matches, route_flags;
+ wolfsentry_ent_id_t id;
+ char default_event_label[WOLFSENTRY_MAX_LABEL_BYTES + 1];
+ int default_event_label_len;
+ int new_count;
+ char addr_fmt_buf[32];
+ int addr_fmt_len;
+ byte link_addr[2];
+ size_t i;
+ static const wolfsentry_action_res_t forbidden_inited[] = {
+ WOLFSENTRY_ACTION_RES_ACCEPT,
+ WOLFSENTRY_ACTION_RES_REJECT,
+ WOLFSENTRY_ACTION_RES_STOP,
+ WOLFSENTRY_ACTION_RES_DEALLOCATED,
+ WOLFSENTRY_ACTION_RES_ERROR
+ };
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.addr_len = 32;
+ memcpy(remote.sa.addr, "\x0A\x02\x03\x04", 4); /* NOLINT(bugprone-not-null-terminated-result) */
+ local.sa.sa_family = AF_INET;
+ local.sa.sa_proto = IPPROTO_TCP;
+ local.sa.addr_len = 0;
+
+ /* internal-only result bits must not be accepted as caller-initialized
+ * input to the dispatch-with-inited-result entry points. */
+ action_results = WOLFSENTRY_ACTION_RES_FALLTHROUGH;
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id, &inexact_matches, &action_results),
+ INVALID_ARG));
+
+ action_results = WOLFSENTRY_ACTION_RES_INSERTED;
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id, &inexact_matches, &action_results),
+ INVALID_ARG));
+
+ /* a default policy may be accept or reject, never both at once. */
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_route_default_policy_set(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_REJECT),
+ INVALID_ARG));
+
+ /* reading back the default event must report the written label length, not
+ * leave the caller's in/out length untouched. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "default_evt", -1 /* label_len */, 10 /* priority */,
+ NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &table));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_set_default_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, table, "default_evt", -1));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ default_event_label_len = (int)sizeof default_event_label;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_get_default_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, table, default_event_label, &default_event_label_len));
+ WOLFSENTRY_EXIT_ON_FALSE(default_event_label_len == (int)strlen("default_evt"));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(default_event_label, "default_evt") == 0);
+
+ /* incrementing a derogatory count by zero is a no-op that must succeed even
+ * when the count is saturated, not be mistaken for an averted overflow. */
+ WOLFSENTRY_CLEAR_ALL_BITS(route_flags);
+ WOLFSENTRY_SET_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS
+ | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa, route_flags,
+ NULL /* event_label */, 0 /* event_label_len */,
+ &id, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&route));
+ new_count = -1;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_increment_derogatory_count(WOLFSENTRY_CONTEXT_ARGS_OUT, route, 65535, &new_count));
+ WOLFSENTRY_EXIT_ON_FALSE(new_count == 65535);
+ new_count = -1;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_increment_derogatory_count(WOLFSENTRY_CONTEXT_ARGS_OUT, route, 0, &new_count));
+ WOLFSENTRY_EXIT_ON_FALSE(new_count == 65535);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ /* the fallthrough route belongs to no table, so setting a purge time on it
+ * must be refused rather than dereferencing a null parent table. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_fallthrough_route_get(WOLFSENTRY_CONTEXT_ARGS_OUT, table, &fallthrough_route));
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_route_purge_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, (struct wolfsentry_route *)fallthrough_route, 1),
+ NOT_PERMITTED));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, (struct wolfsentry_route *)fallthrough_route, NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ /* a link address whose length is not a whole number of bytes must render
+ * its final partial byte, not silently drop it. */
+ link_addr[0] = 0xAB;
+ link_addr[1] = 0xCD;
+ addr_fmt_len = (int)sizeof addr_fmt_buf;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_format_address(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_AF_LINK, link_addr, 12 /* addr_bits */,
+ addr_fmt_buf, &addr_fmt_len));
+ WOLFSENTRY_EXIT_ON_FALSE(addr_fmt_len == 5);
+ WOLFSENTRY_EXIT_ON_FALSE(addr_fmt_buf[2] == ':');
+
+ /* a null action_results must be rejected up front, not dereferenced while
+ * clearing it. */
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id, &inexact_matches, NULL /* action_results */),
+ INVALID_ARG));
+
+ /* a "reset" default policy (reject plus port reset) is accepted and carried
+ * into unmatched verdicts; a port reset cannot qualify an accept. */
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_route_default_policy_set(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_PORT_RESET),
+ INVALID_ARG));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_default_policy_set(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_PORT_RESET));
+ memcpy(remote.sa.addr, "\x0A\x63\x63\x63", 4); /* NOLINT(bugprone-not-null-terminated-result) */
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_SUCCESS_CODE_IS(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id, &inexact_matches, &action_results),
+ USED_FALLBACK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_PORT_RESET));
+
+ /* a caller may not pre-decide the verdict or pre-set internal status bits. */
+ for (i = 0; i < sizeof forbidden_inited / sizeof forbidden_inited[0]; ++i) {
+ action_results = forbidden_inited[i];
+ WOLFSENTRY_EXIT_ON_FALSE(
+ WOLFSENTRY_ERROR_CODE_IS(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id, &inexact_matches, &action_results),
+ INVALID_ARG));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+/* insert an inbound TCP/IPv4 route with wildcarded local side and ports; a NULL
+ * remote_addr leaves the remote address empty (pair with the address wildcard). */
+static wolfsentry_errcode_t regression_insert(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const char *remote_addr,
+ unsigned int remote_prefix_bits,
+ const char *event_label,
+ wolfsentry_route_flags_t extra_flags,
+ wolfsentry_ent_id_t *id)
+{
+ wolfsentry_action_res_t action_results;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ if (remote_addr != NULL) {
+ remote.sa.addr_len = (wolfsentry_addr_bits_t)remote_prefix_bits;
+ memcpy(remote.sa.addr, remote_addr, sizeof remote.addr_buf);
+ }
+
+ return wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa, &local.sa,
+ extra_flags
+ | WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS
+ | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD,
+ event_label, event_label ? -1 : 0,
+ id, &action_results);
+}
+
+/* dispatch an inbound TCP/IPv4 packet from a 4-byte remote address, with the
+ * caller-initialized result bits in inited_results. */
+static wolfsentry_errcode_t regression_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const char *remote_addr,
+ wolfsentry_action_res_t inited_results,
+ wolfsentry_ent_id_t *id,
+ wolfsentry_action_res_t *action_results)
+{
+ wolfsentry_route_flags_t inexact_matches;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.addr_len = 32;
+ memcpy(remote.sa.addr, remote_addr, sizeof remote.addr_buf);
+ *action_results = inited_results;
+
+ return wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa, &local.sa, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */, 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ id, &inexact_matches, action_results);
+}
+
+static wolfsentry_time_t regression_fake_now;
+
+static wolfsentry_errcode_t regression_fake_get_time(void *context, wolfsentry_time_t *now) {
+ (void)context;
+ *now = regression_fake_now;
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t regression_route_meta(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ wolfsentry_ent_id_t id,
+ struct wolfsentry_route_metadata_exports *meta)
+{
+ struct wolfsentry_route *route;
+ wolfsentry_errcode_t ret, unlock_ret;
+
+ ret = wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ if (ret < 0)
+ return ret;
+ ret = wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&route);
+ if (ret >= 0)
+ ret = wolfsentry_route_get_metadata(route, meta);
+ unlock_ret = wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ if (ret >= 0)
+ ret = unlock_ret;
+ return ret;
+}
+
+static wolfsentry_errcode_t regression_set_purge_time(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ wolfsentry_ent_id_t id,
+ wolfsentry_time_t purge_after)
+{
+ struct wolfsentry_route *route;
+ wolfsentry_errcode_t ret, unlock_ret;
+
+ ret = wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ if (ret < 0)
+ return ret;
+ ret = wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&route);
+ if (ret >= 0)
+ ret = wolfsentry_route_purge_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, route, purge_after);
+ unlock_ret = wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ if (ret >= 0)
+ ret = unlock_ret;
+ return ret;
+}
+
+static wolfsentry_errcode_t regression_insert_pbox_event(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const char *label,
+ wolfsentry_hitcount_t derog_threshold,
+ wolfsentry_time_t penaltybox_duration,
+ wolfsentry_eventconfig_flags_t flags)
+{
+ struct wolfsentry_eventconfig event_config;
+
+ memset(&event_config, 0, sizeof event_config);
+ event_config.route_private_data_size = PRIVATE_DATA_SIZE;
+ event_config.route_private_data_alignment = PRIVATE_DATA_ALIGNMENT;
+ event_config.derogatory_threshold_for_penaltybox = derog_threshold;
+ event_config.penaltybox_duration = penaltybox_duration;
+ event_config.flags = flags;
+ return wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, label, -1, 10, &event_config, WOLFSENTRY_EVENT_FLAG_NONE, NULL /* id */);
+}
+
+static wolfsentry_errcode_t test_regression_route_timing(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *table;
+ struct wolfsentry_route_metadata_exports meta;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id, matched_id;
+ unsigned int i;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ /* drive the context clock by hand so time boundaries are exact. */
+ regression_fake_now = 1000000;
+ wolfsentry_get_timecbs(wolfsentry)->get_time = regression_fake_get_time;
+
+ /* a route expiring one tick from now survives a purge, and is purged once
+ * the clock reaches its purge time. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x01", 32, NULL, WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_set_purge_time(WOLFSENTRY_CONTEXT_ARGS_OUT, id, regression_fake_now + 1));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ALREADY, wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ ++regression_fake_now;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ITEM_NOT_FOUND, regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+
+ /* an expired route with a live connection idle for exactly the limit is
+ * kept; one tick longer and it is purged. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &table));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_idle_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, table, 100));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x02", 32, NULL, WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x02", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_set_purge_time(WOLFSENTRY_CONTEXT_ARGS_OUT, id, regression_fake_now));
+ regression_fake_now += 100;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ALREADY, wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 1);
+ ++regression_fake_now;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ITEM_NOT_FOUND, regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+
+ /* a route penalty-boxed by its second derogatory event stays boxed at
+ * exactly the box duration, and is released one tick later. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert_pbox_event(WOLFSENTRY_CONTEXT_ARGS_OUT, "pbox", 2, 50, WOLFSENTRY_EVENTCONFIG_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x03", 32, "pbox", WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x03", WOLFSENTRY_ACTION_RES_DEROGATORY, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x03", WOLFSENTRY_ACTION_RES_DEROGATORY, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ regression_fake_now += 50;
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x03", WOLFSENTRY_ACTION_RES_NONE, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ ++regression_fake_now;
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x03", WOLFSENTRY_ACTION_RES_NONE, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ /* a zero derogatory threshold disables penalty boxing entirely. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert_pbox_event(WOLFSENTRY_CONTEXT_ARGS_OUT, "nothresh", 0, 50, WOLFSENTRY_EVENTCONFIG_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x04", 32, "nothresh", WOLFSENTRY_ROUTE_FLAG_GREENLISTED, &id));
+ for (i = 0; i < 3; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x04", WOLFSENTRY_ACTION_RES_DEROGATORY, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ }
+
+ /* a zero box duration is permanent, and a route boxed at insert (no box
+ * time recorded) is never released by the timeout. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert_pbox_event(WOLFSENTRY_CONTEXT_ARGS_OUT, "pboxperm", 2, 0, WOLFSENTRY_EVENTCONFIG_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x05", 32, "pboxperm", WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x06", 32, "pbox", WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED, &id));
+ for (i = 0; i < 2; ++i)
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x05", WOLFSENTRY_ACTION_RES_DEROGATORY, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ regression_fake_now += 1000000;
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x05", WOLFSENTRY_ACTION_RES_NONE, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x06", WOLFSENTRY_ACTION_RES_NONE, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ /* with commendable events ignored, reaching the derogatory threshold boxes
+ * the route even though commendable events would otherwise offset it. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert_pbox_event(WOLFSENTRY_CONTEXT_ARGS_OUT, "ignorecommend", 2, 0, WOLFSENTRY_EVENTCONFIG_FLAG_DEROGATORY_THRESHOLD_IGNORE_COMMENDABLE));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x07", 32, "ignorecommend", WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+ for (i = 0; i < 2; ++i)
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x07", WOLFSENTRY_ACTION_RES_COMMENDABLE, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x07", WOLFSENTRY_ACTION_RES_DEROGATORY, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x05\x05\x07", WOLFSENTRY_ACTION_RES_DEROGATORY, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_connection_limits(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_metadata_exports meta;
+ struct wolfsentry_eventconfig limit_config;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id, matched_id;
+ unsigned int i;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(&limit_config, 0, sizeof limit_config);
+ limit_config.route_private_data_size = PRIVATE_DATA_SIZE;
+ limit_config.route_private_data_alignment = PRIVATE_DATA_ALIGNMENT;
+ limit_config.max_connection_count = 2;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "limit2", -1, 10, &limit_config, WOLFSENTRY_EVENT_FLAG_NONE, &matched_id));
+
+ /* a limit the 16-bit per-route counter cannot enforce is refused rather
+ * than silently capped, and the counter maximum is enforced exactly. */
+ limit_config.max_connection_count = 0x10000U;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(NUMERIC_ARG_TOO_BIG, wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "limitmax", -1, 10, &limit_config, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ limit_config.max_connection_count = 0xffffU;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "limitmax", -1, 10, &limit_config, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x03", 32, "limitmax", WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+ for (i = 0; i < 0xffffU; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x03", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x03", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 0xffffU);
+ limit_config.max_connection_count = 2;
+
+ /* a route that opts out of connection counting is never limited. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x01", 32, "limit2", WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS, &id));
+ for (i = 0; i < 3; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x01", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 0);
+
+ /* a counted route admits exactly the limit, rejects the next connection
+ * without counting it, and reopens a slot after a disconnect. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x02", 32, "limit2", WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+ for (i = 0; i < 2; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x02", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x02", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 2);
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x02", WOLFSENTRY_ACTION_RES_DISCONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x08\x02", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 2);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_connection_count_zero_limit(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_metadata_exports meta;
+ struct wolfsentry_eventconfig limit_config;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id, matched_id;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(&limit_config, 0, sizeof limit_config);
+ limit_config.route_private_data_size = PRIVATE_DATA_SIZE;
+ limit_config.route_private_data_alignment = PRIVATE_DATA_ALIGNMENT;
+ limit_config.max_connection_count = 2;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "zerolimit", -1, 10, &limit_config, WOLFSENTRY_EVENT_FLAG_NONE, &matched_id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x09\x01", 32, "zerolimit", WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+
+ /* a connection opened under a limit stays counted after the limit is
+ * removed, and connections opened afterward are counted too. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x09\x01", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ limit_config.max_connection_count = 0;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_update_config(WOLFSENTRY_CONTEXT_ARGS_OUT, "zerolimit", -1, &limit_config));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x09\x01", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x09\x01", WOLFSENTRY_ACTION_RES_DISCONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x09\x01", WOLFSENTRY_ACTION_RES_DISCONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_purge_list_reinsert(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *table;
+ struct wolfsentry_route_metadata_exports meta;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t ids[3], matched_id;
+ static const char * const addrs[3] = { "\x0A\x08\x0A\x01", "\x0A\x08\x0A\x02", "\x0A\x08\x0A\x03" };
+ int i;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &table));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_max_purgeable_idle_time_set(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, table, (wolfsentry_time_t)1 << 30));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ /* three expired routes, each protected by a live connection */
+ for (i = 0; i < 3; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, addrs[i], 32, NULL, WOLFSENTRY_ROUTE_FLAG_NONE, &ids[i]));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, addrs[i], WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_set_purge_time(WOLFSENTRY_CONTEXT_ARGS_OUT, ids[i], (wolfsentry_time_t)(10 * (i + 1))));
+ }
+ /* moving the stalest entry to the head must not leave a stale back link,
+ * so a scan that visits every protected entry still terminates. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_set_purge_time(WOLFSENTRY_CONTEXT_ARGS_OUT, ids[0], 40));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ALREADY, wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+ for (i = 0; i < 3; ++i)
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, ids[i], &meta));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+/* intervals in milliseconds, unlike the microsecond absolute clock. */
+static wolfsentry_errcode_t regression_ms_interval_from_seconds(time_t howlong_secs, long howlong_nsecs, wolfsentry_time_t *howlong) {
+ *howlong = ((wolfsentry_time_t)howlong_secs * 1000) + (howlong_nsecs / 1000000);
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_bounded_purge_progress(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *table;
+ struct wolfsentry_route_metadata_exports meta;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t ids[9], matched_id;
+ byte addr[4] = { 0x0A, 0x08, 0x0B, 0x00 };
+ int i;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ regression_fake_now = 1000000;
+ wolfsentry_get_timecbs(wolfsentry)->get_time = regression_fake_get_time;
+ wolfsentry_get_timecbs(wolfsentry)->interval_from_seconds = regression_ms_interval_from_seconds;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &table));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_max_purgeable_idle_time_set(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, table, (wolfsentry_time_t)1 << 30));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ /* eight expired connected routes at the stale end, and one expired
+ * unconnected route queued behind them. */
+ for (i = 0; i < 9; ++i) {
+ addr[3] = (byte)(i + 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, (const char *)addr, 32, NULL, WOLFSENTRY_ROUTE_FLAG_NONE, &ids[i]));
+ if (i < 8)
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, (const char *)addr, WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ }
+ for (i = 0; i < 9; ++i)
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_set_purge_time(WOLFSENTRY_CONTEXT_ARGS_OUT, ids[i], (wolfsentry_time_t)(10 * (i + 1))));
+
+ for (i = 0; i < 3; ++i)
+ WOLFSENTRY_EXIT_ON_FALSE(regression_purge_ok(wolfsentry_route_stale_purge_one_opportunistically(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL)));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ITEM_NOT_FOUND, regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, ids[8], &meta));
+ /* the connected routes are requeued one purge margin out, as an interval */
+ for (i = 0; i < 8; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, ids[i], &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(meta.purge_after == regression_fake_now + (WOLFSENTRY_ROUTE_PURGE_MARGIN_SECONDS * 1000));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_purge_idle_edges(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *table;
+ struct wolfsentry_route_metadata_exports meta;
+ struct wolfsentry_route_exports exports;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id, matched_id;
+ byte addr[4] = { 0x0A, 0x08, 0x0C, 0x01 };
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ regression_fake_now = 1000000;
+ wolfsentry_get_timecbs(wolfsentry)->get_time = regression_fake_get_time;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &table));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_idle_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, table, 100));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ /* an imported route carrying a live connection but never hit is idle
+ * since its insertion, not since the epoch. */
+ memset(&exports, 0, sizeof exports);
+ exports.sa_family = AF_INET;
+ exports.sa_proto = IPPROTO_TCP;
+ exports.remote_address = addr;
+ exports.remote.addr_len = 32;
+ exports.flags = WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN
+ | WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD
+ | WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD;
+ exports.meta.connection_count = 1;
+ exports.meta.purge_after = regression_fake_now + 1;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_insert_by_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, &exports, &id, &action_results));
+ regression_fake_now += 2;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ALREADY, wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_delete_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL /* caller_arg */, id, NULL /* trigger_label */, 0, &action_results));
+
+ /* a negative idle limit, like zero, never purges a connected route. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_max_purgeable_idle_time_set(WOLFSENTRY_CONTEXT_ARGS_OUT, table, -1));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x0C\x02", 32, NULL, WOLFSENTRY_ROUTE_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\x0A\x08\x0C\x02", WOLFSENTRY_ACTION_RES_CONNECT, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_set_purge_time(WOLFSENTRY_CONTEXT_ARGS_OUT, id, regression_fake_now));
+ ++regression_fake_now;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ALREADY, wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_route_meta(WOLFSENTRY_CONTEXT_ARGS_OUT, id, &meta));
+ WOLFSENTRY_EXIT_ON_FALSE(meta.connection_count == 1);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_regression_prefix_specificity(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t broad_id, narrow_id, matched_id;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ /* equal-priority overlapping prefixes: the longer match must win inside
+ * the /25, and the /24 must still match outside it. */
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\xC0\xA8\x01\x00", 24, NULL, WOLFSENTRY_ROUTE_FLAG_GREENLISTED, &broad_id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "\xC0\xA8\x01\x80", 25, NULL, WOLFSENTRY_ROUTE_FLAG_PENALTYBOXED, &narrow_id));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\xC0\xA8\x01\xC8", WOLFSENTRY_ACTION_RES_NONE, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(matched_id == narrow_id);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FAILURE(regression_dispatch(WOLFSENTRY_CONTEXT_ARGS_OUT, "\xC0\xA8\x01\x0A", WOLFSENTRY_ACTION_RES_NONE, &matched_id, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(matched_id == broad_id);
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_dynamic_rules_regressions(void) {
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_bitmask_match_specificity(), OK));
+#endif
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_stale_purge_connected_idle(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_dispatch_error_not_masked(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_copy_metadata_purge_list(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_route_api_invariants(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_route_timing(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_connection_limits(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_connection_count_zero_limit(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_purge_list_reinsert(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_bounded_purge_progress(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_purge_idle_edges(), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_regression_prefix_specificity(), OK));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static wolfsentry_errcode_t test_dynamic_count_main_routes(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ wolfsentry_hitcount_t *n_routes)
+{
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_route_table *main_routes;
+ struct wolfsentry_cursor *cursor;
+ struct wolfsentry_route *route;
+
+ *n_routes = 0;
+
+ ret = wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+
+ ret = wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes);
+ if (ret >= 0) {
+ ret = wolfsentry_route_table_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor);
+ if (ret >= 0) {
+ for (ret = wolfsentry_route_table_iterate_current(main_routes, cursor, &route);
+ ret >= 0;
+ ret = wolfsentry_route_table_iterate_next(main_routes, cursor, &route))
+ {
+ ++*n_routes;
+ }
+ ret = wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor);
+ }
+ }
+
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_ERROR_RERETURN(ret);
+}
+
+static wolfsentry_errcode_t test_track_peer_sentinel_action(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_context,
+ void *caller_arg,
+ const struct wolfsentry_event *event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)action;
+ (void)caller_arg;
+ (void)event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+
+ ++*(int *)handler_context;
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_track_peer_ephemeral_route_bound(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_eventconfig aux_config;
+ int sentinel_hits = 0;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t rule_flags = WOLFSENTRY_ROUTE_FLAG_NONE;
+ wolfsentry_route_flags_t dispatch_flags = WOLFSENTRY_ROUTE_FLAG_NONE;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id;
+ wolfsentry_hitcount_t n_routes;
+ int i;
+ byte addr[4];
+
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_generator",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ /* an aux event with no purge time would make every tracked peer a permanent route. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_unpurgeable",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &aux_config));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 60, 0, &aux_config.route_idle_time_for_purge));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_purgeable",
+ -1 /* label_len */,
+ 10,
+ &aux_config,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_generator",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ WOLFSENTRY_BUILTIN_LABEL_PREFIX "track-peer-v1",
+ -1));
+
+ /* a refused insert must not stop the rest of the match chain. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_sentinel",
+ -1,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_track_peer_sentinel_action,
+ &sentinel_hits,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_generator",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "track_peer_sentinel",
+ -1));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_generator",
+ -1,
+ "track_peer_unpurgeable",
+ -1));
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 0;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memset(remote.sa.addr, 0, sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf);
+
+ WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD);
+ WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD);
+ WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ rule_flags,
+ "track_peer_generator",
+ -1,
+ &id,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_count_main_routes(WOLFSENTRY_CONTEXT_ARGS_OUT, &n_routes));
+ WOLFSENTRY_EXIT_ON_FALSE(n_routes == 1);
+
+ WOLFSENTRY_SET_BITS(dispatch_flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+
+ for (i = 1; i <= 3; ++i) {
+ remote.sa.sa_port = (wolfsentry_port_t)(1000 + i);
+ addr[0] = 10;
+ addr[1] = 0;
+ addr[2] = 0;
+ addr[3] = (byte)i;
+ memcpy(remote.sa.addr, addr, sizeof addr);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_count_main_routes(WOLFSENTRY_CONTEXT_ARGS_OUT, &n_routes));
+ WOLFSENTRY_EXIT_ON_FALSE(n_routes == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(sentinel_hits == 3);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_generator",
+ -1,
+ "track_peer_purgeable",
+ -1));
+
+ for (i = 1; i <= 3; ++i) {
+ remote.sa.sa_port = (wolfsentry_port_t)(2000 + i);
+ addr[0] = 10;
+ addr[1] = 0;
+ addr[2] = 1;
+ addr[3] = (byte)i;
+ memcpy(remote.sa.addr, addr, sizeof addr);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_count_main_routes(WOLFSENTRY_CONTEXT_ARGS_OUT, &n_routes));
+ WOLFSENTRY_EXIT_ON_FALSE(n_routes == 4);
+ WOLFSENTRY_EXIT_ON_FALSE(sentinel_hits == 6);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+/* find the non-wildcard route for the peer whose remote address ends in remote_last_byte. */
+static wolfsentry_errcode_t test_dynamic_ephemeral_route_exports(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ byte remote_last_byte,
+ struct wolfsentry_route_exports *exports_out)
+{
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_route_table *main_routes;
+ struct wolfsentry_cursor *cursor;
+ struct wolfsentry_route *route;
+ struct wolfsentry_route_exports exports;
+ int found = 0;
+
+ ret = wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+
+ ret = wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes);
+ if (ret >= 0) {
+ ret = wolfsentry_route_table_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor);
+ if (ret >= 0) {
+ for (ret = wolfsentry_route_table_iterate_current(main_routes, cursor, &route);
+ ret >= 0;
+ ret = wolfsentry_route_table_iterate_next(main_routes, cursor, &route))
+ {
+ ret = wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, route, &exports);
+ if (ret < 0)
+ break;
+ if ((! WOLFSENTRY_CHECK_BITS(exports.flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD)) &&
+ (exports.remote.addr_len == 4 * BITS_PER_BYTE) &&
+ (exports.remote_address[3] == remote_last_byte))
+ {
+ *exports_out = exports;
+ found = 1;
+ break;
+ }
+ }
+ if (ret >= 0 || WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND))
+ ret = wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor);
+ else
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor));
+ }
+ }
+
+ WOLFSENTRY_WARN_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ if ((ret >= 0) && (! found))
+ ret = WOLFSENTRY_ERROR_ENCODE(ITEM_NOT_FOUND);
+
+ WOLFSENTRY_ERROR_RERETURN(ret);
+}
+
+static int test_track_peer_connect_count(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_eventconfig limit_config, nocount_config, nolimit_config;
+ struct wolfsentry_route_exports ephemeral_exports;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t rule_flags = WOLFSENTRY_ROUTE_FLAG_NONE;
+ wolfsentry_route_flags_t dispatch_flags = WOLFSENTRY_ROUTE_FLAG_NONE;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_ent_id_t id;
+
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_set(WOLFSENTRY_CONTEXT_ARGS_OUT, WOLFSENTRY_ACTION_RES_ACCEPT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_cc_generator",
+ -1 /* label_len */,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &limit_config));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 60, 0, &limit_config.route_idle_time_for_purge));
+ limit_config.max_connection_count = 1;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_cc_limit",
+ -1 /* label_len */,
+ 10,
+ &limit_config,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &nocount_config));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 60, 0, &nocount_config.route_idle_time_for_purge));
+ nocount_config.max_connection_count = 1;
+ nocount_config.route_flags_to_add_on_insert = WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_cc_nocount",
+ -1 /* label_len */,
+ 10,
+ &nocount_config,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_cc_generator",
+ -1,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ WOLFSENTRY_BUILTIN_LABEL_PREFIX "track-peer-v1",
+ -1));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_cc_generator",
+ -1,
+ "track_peer_cc_limit",
+ -1));
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 0;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memset(remote.sa.addr, 0, sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf);
+
+ WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+ WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_ADDR_WILDCARD);
+ WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD);
+ WOLFSENTRY_SET_BITS(rule_flags, WOLFSENTRY_ROUTE_FLAG_GREENLISTED);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ rule_flags,
+ "track_peer_cc_generator",
+ -1,
+ &id,
+ &action_results));
+
+ WOLFSENTRY_SET_BITS(dispatch_flags, WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN);
+
+ remote.sa.sa_port = 1234;
+ memcpy(remote.sa.addr, "\12\0\2\1", sizeof remote.addr_buf);
+
+ /* the CONNECT that spawns the tracking route must be counted on that route. */
+ action_results = WOLFSENTRY_ACTION_RES_CONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 1, &ephemeral_exports));
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 1);
+
+ /* with a per-peer limit of 1, a second concurrent CONNECT from the same peer is rejected. */
+ action_results = WOLFSENTRY_ACTION_RES_CONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 1, &ephemeral_exports));
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 1);
+
+ /* the matching DISCONNECT brings the count back to zero. */
+ action_results = WOLFSENTRY_ACTION_RES_DISCONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 1, &ephemeral_exports));
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 0);
+
+ /* and the peer can connect again. */
+ action_results = WOLFSENTRY_ACTION_RES_CONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 1, &ephemeral_exports));
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 1);
+
+ /* a tracking route flagged dont-count-current-connections stays at zero. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_cc_generator",
+ -1,
+ "track_peer_cc_nocount",
+ -1));
+
+ remote.sa.sa_port = 2345;
+ memcpy(remote.sa.addr, "\12\0\3\2", sizeof remote.addr_buf);
+
+ action_results = WOLFSENTRY_ACTION_RES_CONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 2, &ephemeral_exports));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(ephemeral_exports.flags, WOLFSENTRY_ROUTE_FLAG_DONT_COUNT_CURRENT_CONNECTIONS));
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 0);
+
+ /* an unlimited route still counts the spawning CONNECT, so a later DISCONNECT balances it. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_eventconfig_init(wolfsentry, &nolimit_config));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_interval_from_seconds(wolfsentry, 60, 0, &nolimit_config.route_idle_time_for_purge));
+ nolimit_config.max_connection_count = 0;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_cc_nolimit",
+ -1 /* label_len */,
+ 10,
+ &nolimit_config,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "track_peer_cc_generator",
+ -1,
+ "track_peer_cc_nolimit",
+ -1));
+
+ remote.sa.sa_port = 3456;
+ memcpy(remote.sa.addr, "\12\0\4\3", sizeof remote.addr_buf);
+
+ action_results = WOLFSENTRY_ACTION_RES_CONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_INSERTED));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 3, &ephemeral_exports));
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 1);
+
+ action_results = WOLFSENTRY_ACTION_RES_DISCONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ dispatch_flags,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(test_dynamic_ephemeral_route_exports(WOLFSENTRY_CONTEXT_ARGS_OUT, 3, &ephemeral_exports));
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_exports.meta.connection_count == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_max_length_label(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ struct wolfsentry_event *event;
+ struct wolfsentry_eventconfig eventconfig;
+ char label[WOLFSENTRY_MAX_LABEL_BYTES + 1];
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(label, 'm', WOLFSENTRY_MAX_LABEL_BYTES);
+ label[WOLFSENTRY_MAX_LABEL_BYTES] = 0;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &event));
+
+ WOLFSENTRY_EXIT_ON_FALSE(id == wolfsentry_get_object_id(event));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_drop_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ event,
+ NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_MAX_LABEL_BYTES,
+ &eventconfig));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_NOT_FOUND,
+ wolfsentry_event_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &event));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_max_length_label_lookups(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ struct wolfsentry_action_list_ent *cursor;
+ const char *action_label;
+ int action_label_len;
+ struct wolfsentry_eventconfig eventconfig;
+ int n_actions = 0;
+ char label[WOLFSENTRY_MAX_LABEL_BYTES + 1];
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(label, 'n', WOLFSENTRY_MAX_LABEL_BYTES);
+ label[WOLFSENTRY_MAX_LABEL_BYTES] = 0;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "max-label-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &eventconfig));
+
+ WOLFSENTRY_EXIT_ON_FALSE(eventconfig.route_private_data_size == config.route_private_data_size);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "max-label-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_list_start(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ &cursor));
+
+ while (wolfsentry_event_action_list_next(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &cursor,
+ &action_label,
+ &action_label_len) >= 0) {
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(action_label, "max-label-action") == 0);
+ ++n_actions;
+ }
+
+ WOLFSENTRY_EXIT_ON_FALSE(n_actions == 1);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_list_done(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &cursor));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_MATCH,
+ "max-label-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_insert_failure_leaves_id_untouched(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id, id_before;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "id_out_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ id_before = id;
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_ALREADY_PRESENT,
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "id_out_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FALSE(id == id_before);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_update_config_null(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ struct wolfsentry_eventconfig global_eventconfig, eventconfig;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "null_config_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "null_config_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &global_eventconfig));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "null_config_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL /* config */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "null_config_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &eventconfig));
+
+ WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == global_eventconfig.route_private_data_size) &&
+ (eventconfig.route_private_data_alignment == global_eventconfig.route_private_data_alignment) &&
+ (eventconfig.max_connection_count == global_eventconfig.max_connection_count));
+ WOLFSENTRY_EXIT_ON_FALSE(eventconfig.max_connection_count == config.max_connection_count);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_update_config_layout_with_routes(void) {
+ struct wolfsentry_context *wolfsentry, *ctx_clone;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ struct wolfsentry_eventconfig eventconfig;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+ static const struct wolfsentry_eventconfig config2 = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 15 };
+ static const struct wolfsentry_eventconfig config2_more_connections = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 20 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const struct wolfsentry_eventconfig config2 = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const struct wolfsentry_eventconfig config2_more_connections = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr, "\0\1\2\3", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ &config2,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes_inherited",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_no_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ &config2,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "owns_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id,
+ &action_results));
+
+ memcpy(remote.sa.addr, "\4\5\6\7", sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "owns_routes_inherited",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id,
+ &action_results));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &eventconfig));
+
+ WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == config2.route_private_data_size) &&
+ (eventconfig.route_private_data_alignment == config2.route_private_data_alignment) &&
+ (eventconfig.max_connection_count == config2.max_connection_count));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config2_more_connections));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &eventconfig));
+
+ WOLFSENTRY_EXIT_ON_FALSE(eventconfig.max_connection_count == config2_more_connections.max_connection_count);
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes_inherited",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config2));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes_inherited",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout-insert-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ wolfsentry_action_dummy_callback,
+ NULL /* handler_context */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes_with_insert_action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ &config2,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes_with_insert_action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_INSERT,
+ "layout-insert-action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ memcpy(remote.sa.addr, "\10\11\12\13", sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "owns_routes_with_insert_action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id,
+ &action_results));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_routes_with_insert_action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_no_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owns_no_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &eventconfig));
+
+ WOLFSENTRY_EXIT_ON_FALSE((eventconfig.route_private_data_size == config.route_private_data_size) &&
+ (eventconfig.route_private_data_alignment == config.route_private_data_alignment));
+
+ /* a clone without routes has no route using the old layout */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NO_ROUTES));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone),
+ "owns_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone),
+ "owns_routes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_aux_event_self_reference(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ struct wolfsentry_event *event;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "self_aux_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "self_aux_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "self_aux_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "self_aux_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &event));
+
+ WOLFSENTRY_EXIT_ON_TRUE(wolfsentry_event_get_aux_event(event) != NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(wolfsentry_event_get_flags(event), WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_drop_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ event,
+ NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "self_aux_event",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_aux_event_reuse_after_clear(void) {
+ struct wolfsentry_context *wolfsentry, *ctx_clone;
+ struct wolfsentry_event *deleted_owner;
+ struct wolfsentry_event *held_aux;
+ wolfsentry_ent_id_t id;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_a",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_d",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ /* a -> b, then clear: b is free to own an aux event of its own */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_a",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "aux_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_a",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "aux_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ /* c is now an aux event, so it cannot own one */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "aux_d",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ /* replacing a's aux event releases the old one */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_a",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "aux_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL,
+ 0));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "aux_d",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_a",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "aux_d",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_one",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_two",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "shared_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "other_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_one",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "shared_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_two",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "shared_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ /* owner_one is deleted from the table but stays alive, and still owns shared_aux */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_one",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &deleted_owner));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_one",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL /* action_results */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_two",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL,
+ 0));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "shared_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "other_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_drop_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ deleted_owner,
+ NULL /* action_results */));
+
+ /* freeing the deleted owner released shared_aux, which can now own an aux event */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "shared_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "other_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "held_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "held_target",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ /* an ordinary reference on the aux event is not ownership */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "held_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &held_aux));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_two",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "held_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "held_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "held_target",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "owner_two",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "held_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "held_target",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_drop_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ held_aux,
+ NULL /* action_results */));
+
+ /* a clone rebuilds the aux ownership graph, independently of the source */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone),
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "owner_two",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone),
+ "aux_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone),
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "owner_two",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "aux_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "owner_two",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_aux_mark_cleared_on_owner_free(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_event *aux;
+ wolfsentry_ent_id_t id;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_owner", WOLFSENTRY_LENGTH_NULL_TERMINATED, 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_insert(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_aux", WOLFSENTRY_LENGTH_NULL_TERMINATED, 10, NULL /* config */, WOLFSENTRY_EVENT_FLAG_NONE, &id));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_set_aux_event(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_owner", WOLFSENTRY_LENGTH_NULL_TERMINATED, "mark_aux", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_get_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_aux", WOLFSENTRY_LENGTH_NULL_TERMINATED, &aux));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(wolfsentry_event_get_flags(aux), WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT));
+
+ /* freeing the only owner leaves the aux event with no owners */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, "mark_owner", WOLFSENTRY_LENGTH_NULL_TERMINATED, NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(wolfsentry_event_get_flags(aux), WOLFSENTRY_EVENT_FLAG_IS_SUBEVENT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_event_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, aux, NULL /* action_results */));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_aux_event_rejects_subevent_owner(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "subevent_a",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "subevent_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "subevent_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "subevent_a",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "subevent_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "subevent_b",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "subevent_c",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_aux_event_rejects_parent_event(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "route_parent",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "route_owner",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr, "\0\1\2\3", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "route_parent",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id,
+ &action_results));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "route_owner",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "route_parent",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_former_aux_becomes_route_parent(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ static const char * const labels[3] = { "former_aux", "first_owner", "later_owner" };
+ unsigned int n;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ for (n = 0; n < 3; ++n) {
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ labels[n],
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "first_owner",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "former_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ /* freeing the owner drops the aux owner count without clearing the stored subevent mark */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "first_owner",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr, "\0\1\2\3", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "former_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &id,
+ &action_results));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "later_owner",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "former_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#ifndef WOLFSENTRY_NO_JSON
+
+static int event_priority_check_winner(const char *priority_clause, int expect_accept) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_errcode_t ret;
+ wolfsentry_ent_id_t route_id;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+ char err_buf[512];
+ char json[2048];
+ int json_len;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ json_len = snprintf(
+ json, sizeof json,
+ "{ \"wolfsentry-config-version\" : 1,"
+ " \"events-insert\" : ["
+ " { \"label\" : \"first-event\" %s },"
+ " { \"label\" : \"second-event\", \"priority\" : 1 } ],"
+ " \"static-routes-insert\" : ["
+ " { \"parent-event\" : \"first-event\", \"direction-in\" : true,"
+ " \"green-listed\" : true, \"penalty-boxed\" : false, \"family\" : %u,"
+ " \"remote\" : { \"address\" : \"10.0.0.0\", \"prefix-bits\" : 8 },"
+ " \"local\" : { \"address\" : \"10.0.0.0\", \"prefix-bits\" : 8 } },"
+ " { \"parent-event\" : \"second-event\", \"direction-in\" : true,"
+ " \"green-listed\" : false, \"penalty-boxed\" : true, \"family\" : %u,"
+ " \"remote\" : { \"address\" : \"10.1.2.3\" },"
+ " \"local\" : { \"address\" : \"10.0.0.0\", \"prefix-bits\" : 8 } } ] }",
+ priority_clause, (unsigned int)AF_INET, (unsigned int)AF_INET);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+
+ ret = wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (unsigned char *)json,
+ (size_t)json_len,
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE,
+ err_buf,
+ sizeof err_buf);
+ if (ret < 0)
+ (void)fprintf(stderr, "%s\n", err_buf);
+ WOLFSENTRY_EXIT_ON_FAILURE(ret);
+
+ memset(&remote, 0, sizeof remote);
+ memset(&local, 0, sizeof local);
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr, "\12\1\2\3", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\12\11\11\11", sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id,
+ &inexact_matches,
+ &action_results));
+
+ if (expect_accept) {
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ } else {
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_default_priority(void) {
+ /* an omitted priority behaves as priority 0, the highest */
+ WOLFSENTRY_EXIT_ON_FAILURE(event_priority_check_winner("", 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(event_priority_check_winner(", \"priority\" : 0", 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(event_priority_check_winner(", \"priority\" : 2", 0));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_json_event_max_length_label(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_eventconfig config;
+ char label[WOLFSENTRY_MAX_LABEL_BYTES + 1];
+ char err_buf[512];
+ char json[WOLFSENTRY_MAX_LABEL_BYTES + 128];
+ int json_len;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ memset(label, 'L', sizeof label);
+
+ json_len = snprintf(
+ json, sizeof json,
+ "{ \"wolfsentry-config-version\" : 1, \"events-insert\" : [ { \"label\" : \"%.*s\" } ] }",
+ (int)WOLFSENTRY_MAX_LABEL_BYTES, label);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+ ret = wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (unsigned char *)json,
+ (size_t)json_len,
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE,
+ err_buf,
+ sizeof err_buf);
+ if (ret < 0)
+ (void)fprintf(stderr, "%s\n", err_buf);
+ WOLFSENTRY_EXIT_ON_FAILURE(ret);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_get_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ label,
+ (int)WOLFSENTRY_MAX_LABEL_BYTES,
+ &config));
+
+ json_len = snprintf(
+ json, sizeof json,
+ "{ \"wolfsentry-config-version\" : 1, \"events-insert\" : [ { \"label\" : \"%.*s\" } ] }",
+ (int)WOLFSENTRY_MAX_LABEL_BYTES + 1, label);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ STRING_ARG_TOO_LONG,
+ wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (unsigned char *)json,
+ (size_t)json_len,
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE,
+ err_buf,
+ sizeof err_buf));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#endif /* !WOLFSENTRY_NO_JSON */
+
+static wolfsentry_errcode_t test_action_insert_noop(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const struct wolfsentry_action *action,
+ void *handler_arg,
+ void *caller_arg,
+ const struct wolfsentry_event *trigger_event,
+ wolfsentry_action_type_t action_type,
+ const struct wolfsentry_route *target_route,
+ struct wolfsentry_route_table *route_table,
+ struct wolfsentry_route *rule_route,
+ wolfsentry_action_res_t *action_results)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)action;
+ (void)handler_arg;
+ (void)caller_arg;
+ (void)trigger_event;
+ (void)action_type;
+ (void)target_route;
+ (void)route_table;
+ (void)rule_route;
+ (void)action_results;
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_action_insert_failure_leaves_id_untouched(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id, id_before;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* default_config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "id_out_action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action_insert_noop,
+ NULL /* handler_arg */,
+ &id));
+
+ id_before = id;
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_ALREADY_PRESENT,
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "id_out_action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action_insert_noop,
+ NULL /* handler_arg */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FALSE(id == id_before);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_aux_event_layout_pinned(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+ static const struct wolfsentry_eventconfig config2 = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 15 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const struct wolfsentry_eventconfig config2 = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_aux_owner",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ NULL /* config */,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ &config,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_aux_owner",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "layout_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config2));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_set_aux_event(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_aux_owner",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ NULL /* aux_event_label */,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_aux",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config2));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_event_layout_guard_held_deleted_route(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+ wolfsentry_action_res_t action_results;
+ struct wolfsentry_route *route = NULL;
+ wolfsentry_route_flags_t flags = WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS | WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ static const struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT, .max_connection_count = 10 };
+ static const struct wolfsentry_eventconfig config2 = { .route_private_data_size = PRIVATE_DATA_SIZE * 2, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT * 2, .max_connection_count = 15 };
+#else
+ static const struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const struct wolfsentry_eventconfig config2 = { PRIVATE_DATA_SIZE * 2, PRIVATE_DATA_ALIGNMENT * 2, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_action_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_insert_action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_FLAG_NONE,
+ test_action_insert_noop,
+ NULL /* handler_arg */,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_insert(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_held_parent",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 10,
+ &config,
+ WOLFSENTRY_EVENT_FLAG_NONE,
+ &id));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_event_action_append(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_held_parent",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_ACTION_TYPE_INSERT,
+ "layout_insert_action",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr, "\0\1\2\3", sizeof remote.addr_buf);
+ memcpy(local.sa.addr, "\377\376\375\374", sizeof local.addr_buf);
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_insert_and_check_out(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &remote.sa,
+ &local.sa,
+ flags,
+ "layout_held_parent",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &route,
+ &action_results));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ route->header.id,
+ NULL /* trigger_label */,
+ 0,
+ &action_results));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INCOMPATIBLE_STATE,
+ wolfsentry_event_update_config(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "layout_held_parent",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &config2));
+
+ WOLFSENTRY_CLEAR_ALL_BITS(action_results);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_drop_reference(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ route,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#endif /* TEST_DYNAMIC_RULES */
+
+#ifdef TEST_USER_VALUES
+
+#include
+
+static wolfsentry_errcode_t test_kv_validator(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ struct wolfsentry_kv_pair *kv)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ switch (WOLFSENTRY_KV_TYPE(kv)) {
+ case WOLFSENTRY_KV_NONE:
+ case WOLFSENTRY_KV_NULL:
+ case WOLFSENTRY_KV_TRUE:
+ case WOLFSENTRY_KV_FALSE:
+ WOLFSENTRY_RETURN_OK;
+ case WOLFSENTRY_KV_UINT:
+ if (WOLFSENTRY_KV_V_UINT(kv) == 12345678UL)
+ WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
+ else
+ WOLFSENTRY_RETURN_OK;
+ case WOLFSENTRY_KV_SINT:
+ if (WOLFSENTRY_KV_V_SINT(kv) == -12345678L)
+ WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
+ else
+ WOLFSENTRY_RETURN_OK;
+ case WOLFSENTRY_KV_FLOAT:
+ if (WOLFSENTRY_KV_V_FLOAT(kv) > 100.0)
+ WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
+ else
+ WOLFSENTRY_RETURN_OK;
+ case WOLFSENTRY_KV_STRING:
+ if (WOLFSENTRY_KV_V_STRING_LEN(kv) != 8)
+ WOLFSENTRY_RETURN_OK;
+ if (strncmp(WOLFSENTRY_KV_V_STRING(kv), "deadbeef", WOLFSENTRY_KV_V_STRING_LEN(kv)) == 0)
+ WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
+ else
+ WOLFSENTRY_RETURN_OK;
+ case WOLFSENTRY_KV_BYTES:
+ if (WOLFSENTRY_KV_V_BYTES_LEN(kv) != 10)
+ WOLFSENTRY_RETURN_OK;
+ if (memcmp(WOLFSENTRY_KV_V_STRING(kv), "abcdefghij", WOLFSENTRY_KV_V_BYTES_LEN(kv)) == 0)
+ WOLFSENTRY_ERROR_RETURN(BAD_VALUE);
+ else
+ WOLFSENTRY_RETURN_OK;
+ default:
+ break;
+ }
+ WOLFSENTRY_ERROR_RETURN(WRONG_TYPE);
+}
+
+static int test_user_values(void) {
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+
+ wolfsentry_kv_type_t kv_type;
+ struct wolfsentry_kv_pair_internal *kv_ref;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ action_results = WOLFSENTRY_ACTION_RES_NONE;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_set_validator(WOLFSENTRY_CONTEXT_ARGS_OUT, test_kv_validator, &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == WOLFSENTRY_ACTION_RES_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_null(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_null",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_bool(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_KV_TRUE,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_bool(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &kv_type));
+
+ WOLFSENTRY_EXIT_ON_FALSE(kv_type == WOLFSENTRY_KV_TRUE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_bool(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_KV_FALSE,
+ 1));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_bool(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &kv_type));
+
+ WOLFSENTRY_EXIT_ON_FALSE(kv_type == WOLFSENTRY_KV_FALSE);
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ WRONG_TYPE,
+ wolfsentry_user_value_store_bool(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ WOLFSENTRY_KV_NONE,
+ 1));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_type(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &kv_type));
+
+ WOLFSENTRY_EXIT_ON_FALSE(kv_type == WOLFSENTRY_KV_FALSE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_type(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ strlen("test_bool"),
+ &kv_type));
+
+ WOLFSENTRY_EXIT_ON_FALSE(kv_type == WOLFSENTRY_KV_FALSE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_NOT_FOUND,
+ wolfsentry_user_value_get_type(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bool",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &kv_type));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_uint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_uint",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 123UL,
+ 0));
+
+ {
+ uint64_t value = 0;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_uint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_uint",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value == 123UL);
+ }
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ BAD_VALUE,
+ wolfsentry_user_value_store_uint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "bad_uint",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 12345678UL,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_sint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_sint",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ -123L,
+ 0));
+
+ {
+ int64_t value = 0;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_sint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_sint",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value == -123L);
+ }
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ BAD_VALUE,
+ wolfsentry_user_value_store_sint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "bad_sint",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ -12345678L,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_double(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_float",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 1.234,
+ 0));
+
+ {
+ double value = 0.0;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_float(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_float",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value));
+
+ WOLFSENTRY_EXIT_ON_FALSE(fabs(value - 1.234) < 0.000001);
+ }
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ BAD_VALUE,
+ wolfsentry_user_value_store_double(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "bad_float",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 123.45678,
+ 0));
+
+ {
+ static const char test_string[] = "abc123";
+ const char *value = NULL;
+ int value_len = -1;
+ int mutable = -1;
+ wolfsentry_errcode_t ret;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ test_string,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_mutability(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &mutable));
+
+ WOLFSENTRY_EXIT_ON_FALSE(mutable == 1);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_set_mutability(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_mutability(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &mutable));
+
+ WOLFSENTRY_EXIT_ON_FALSE(mutable == 0);
+
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ ret = wolfsentry_user_value_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(NOT_PERMITTED, ret);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value,
+ &value_len,
+ &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(test_string));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, test_string) == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_release_record(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &kv_ref));
+
+ }
+
+#ifdef WOLFSENTRY_HAVE_JSON_DOM
+ /* strings rendered as JSON must escape quotes and backslashes. */
+ {
+ static const char raw[] = "a\"b\\c";
+ const struct wolfsentry_kv_pair *kv_exports;
+ char render_buf[64];
+ int render_buf_space = (int)sizeof render_buf;
+
+ memset(render_buf, 0, sizeof render_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "json_escape_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ raw,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+ {
+ const char *value = NULL;
+ int value_len = -1;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "json_escape_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value, &value_len, &kv_ref));
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_pair_export(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_ref, &kv_exports));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_render_value(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_exports, render_buf, &render_buf_space));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE(strstr(render_buf, "\\\"") != NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(strstr(render_buf, "\\\\") != NULL);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_release_record(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_ref));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "json_escape_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ }
+#endif /* WOLFSENTRY_HAVE_JSON_DOM */
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ BAD_VALUE,
+ wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "bad_string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "deadbeef",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+
+ {
+ static const byte test_bytes[] = { 0, 1, 2, 3, 4 };
+ const byte *value = NULL;
+ int value_len = -1;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_bytes(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bytes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ test_bytes,
+ sizeof test_bytes,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_bytes(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "test_bytes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value,
+ &value_len,
+ &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)sizeof test_bytes);
+ WOLFSENTRY_EXIT_ON_FALSE(memcmp(value, test_bytes, (size_t)value_len) == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_release_record(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &kv_ref));
+ }
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ BAD_VALUE,
+ wolfsentry_user_value_store_bytes(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "bad_bytes",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (const byte *)"abcdefghij",
+ 10,
+ 0));
+
+#ifndef WOLFSENTRY_NO_STDIO
+ {
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_cursor *cursor;
+ const struct wolfsentry_kv_pair *kv_exports;
+ const char *val_type;
+ char val_buf[256];
+ int val_buf_space;
+ wolfsentry_hitcount_t n_seen = 0;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
+ for (ret = wolfsentry_user_values_iterate_current(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref);
+ ret >= 0;
+ ret = wolfsentry_user_values_iterate_next(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref)) {
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_pair_export(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_ref, &kv_exports));
+ val_buf_space = sizeof val_buf;
+ if (wolfsentry_kv_type_to_string(WOLFSENTRY_KV_TYPE(kv_exports), &val_type) < 0)
+ val_type = "?";
+ if (wolfsentry_kv_render_value(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_exports, val_buf, &val_buf_space) < 0)
+ strcpy(val_buf,"?");
+ printf("{ \"%.*s\" : { \"%s\" : %s } }\n",
+ (int)WOLFSENTRY_KV_KEY_LEN(kv_exports),
+ WOLFSENTRY_KV_KEY(kv_exports),
+ val_type,
+ val_buf);
+ ++n_seen;
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE(n_seen == wolfsentry->user_values->header.n_ents);
+ WOLFSENTRY_EXIT_ON_FALSE(n_seen == 6);
+ }
+#endif
+
+ {
+ static const struct {
+ const char *q;
+ const char *a;
+ } base64_qna[] = {
+ { "", "" },
+ { "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu", "Many hands make light work." },
+ { "bGlnaHQgd29yay4=", "light work." },
+ { "bGlnaHQgd29yay4", "light work." },
+ { "bGlnaHQgd29yaw==", "light work" },
+ { "bGlnaHQgd29yaw", "light work" },
+ { "bGlnaHQgd29y", "light wor" },
+ { "bGlnaHQgd28=", "light wo" },
+ { "bGlnaHQgdw==", "light w" },
+ { "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV"
+ "1hZWjAxMjM0NTY3ODkhQCMkJV4mKigpXy0rPXxcYH5bXXt9OzonIiw8Lj4vPw==",
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ "!@#$%^&*()_-+=|\\`~[]{};:'\",<.>/?" },
+ { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+",
+ "\x69\xb7\x1d\x79\xf8\x21\x8a\x39\x25\x9a\x7a\x29\xaa\xbb\x2d\xba"
+ "\xfc\x31\xcb\x30\x01\x08\x31\x05\x18\x72\x09\x28\xb3\x0d\x38\xf4"
+ "\x11\x49\x35\x15\x59\x76\x19\xd3\x5d\xb7\xe3\x9e\xbb\xf3\xdf\xfe"
+ }
+ };
+
+ int i;
+ byte outbuf[256];
+ size_t outbuf_spc;
+
+ for (i=0; i < (int)(sizeof base64_qna / sizeof base64_qna[0]); ++i) {
+ outbuf_spc = sizeof outbuf;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_base64_decode(base64_qna[i].q, strlen(base64_qna[i].q), outbuf, &outbuf_spc, 0 /* ignore_junk_p */));
+ WOLFSENTRY_EXIT_ON_FALSE((outbuf_spc == strlen(base64_qna[i].a)) && (memcmp(outbuf, base64_qna[i].a, outbuf_spc) == 0));
+ }
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_kv_reference_lifetime(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_kv_pair_internal *kv_ref = NULL;
+ const char *value = NULL;
+ int value_len = -1;
+ static const char key[] = "lifetime_key";
+ static const char str[] = "lifetime_value";
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ str, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value, &value_len, &kv_ref));
+
+ /* delete the key while the reference returned by get_string is still held;
+ * the held reference must keep the entry alive and readable.
+ */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(str));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, str) == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_release_record(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value, &value_len, &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_kv_get2_reference_balance(void) {
+ struct wolfsentry_context *wolfsentry;
+ static const char key[] = "balance_key";
+ uint64_t uint_value = 0;
+ int mutable = -1;
+ int i;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_uint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 42, 0));
+
+ /* the non-reference getters take and release an internal reference each
+ * call; repeated calls must leave the refcount balanced so the entry can
+ * still be deleted afterwards.
+ */
+ for (i = 0; i < 100; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_uint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &uint_value));
+ WOLFSENTRY_EXIT_ON_FALSE(uint_value == 42);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_mutability(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &mutable));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ wolfsentry_user_value_get_uint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &uint_value));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_kv_get_bad_key_errcode(void) {
+ struct wolfsentry_context *wolfsentry;
+ const char *value = NULL;
+ int value_len = -1;
+ struct wolfsentry_kv_pair_internal *kv_ref = NULL;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ /* a rejected key must surface the validation error itself, not an error
+ * from an unlock attempted without a held lock.
+ */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "unused_key", 0 /* key_len */,
+ &value, &value_len, &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int render_only_user_value(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ char *out,
+ int out_space)
+{
+ struct wolfsentry_cursor *cursor;
+ struct wolfsentry_kv_pair_internal *kv_ref;
+ const struct wolfsentry_kv_pair *kv_exports;
+ int render_space = out_space;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_current(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_pair_export(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_ref, &kv_exports));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_render_value(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_exports, out, &render_space));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_kv_render_large_int(void) {
+ struct wolfsentry_context *wolfsentry;
+ char buf[64];
+ /* 0x1234567890ABCDEF and -(2^33), built without long-long literals so the
+ * test stays legal under WOLFSENTRY_C89; compared against the exact decimal
+ * rendering to avoid strtoull()/strtoll().
+ */
+ uint64_t stored_uint = ((uint64_t)0x12345678u << 32) | (uint64_t)0x90abcdefu;
+ int64_t stored_sint = -((int64_t)1 << 33);
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_uint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "big_uint", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ stored_uint, 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(buf, "1311768467294899695") == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "big_uint", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_sint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "big_sint", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ stored_sint, 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(buf, "-8589934592") == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_kv_render_float_roundtrip(void) {
+ struct wolfsentry_context *wolfsentry;
+ char buf[64];
+ double stored = 3.141592653589793;
+#ifndef WOLFSENTRY_NO_JSON
+ char json[128];
+ int json_len;
+ double reloaded;
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_double(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "pi", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ stored, 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf));
+
+ /* the rendered form must reparse to the exact same double. */
+ WOLFSENTRY_EXIT_ON_FALSE(strtod(buf, NULL) == stored);
+
+#ifndef WOLFSENTRY_NO_JSON
+ /* the longest %.17g form, 24 characters, must reload through the JSON loader */
+ stored = -2.2250738585072014e-308;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, "pi", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_store_double(WOLFSENTRY_CONTEXT_ARGS_OUT, "tiny", WOLFSENTRY_LENGTH_NULL_TERMINATED, stored, 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf));
+ WOLFSENTRY_EXIT_ON_FALSE(strlen(buf) == 24);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_delete(WOLFSENTRY_CONTEXT_ARGS_OUT, "tiny", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+ json_len = snprintf(json, sizeof json, "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"tiny\" : %s } }", buf);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_config_json_oneshot(WOLFSENTRY_CONTEXT_ARGS_OUT, (const unsigned char *)json, (size_t)json_len, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, NULL, 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_value_get_float(WOLFSENTRY_CONTEXT_ARGS_OUT, "tiny", WOLFSENTRY_LENGTH_NULL_TERMINATED, &reloaded));
+ WOLFSENTRY_EXIT_ON_FALSE(reloaded == stored);
+#endif
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#ifndef WOLFSENTRY_C89
+static int test_kv_render_nonfinite_float(void) {
+ struct wolfsentry_context *wolfsentry;
+ char buf[64];
+ double reparsed;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_double(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "nan_val", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ strtod("nan", NULL), 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf));
+ /* a bare nan/inf is not valid JSON; the rendered value must be quoted and
+ * must reparse to the same non-finite value.
+ */
+ WOLFSENTRY_EXIT_ON_FALSE(buf[0] == '"');
+ reparsed = strtod(buf + 1, NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(isnan(reparsed));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "nan_val", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_double(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "inf_val", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ strtod("inf", NULL), 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf));
+ WOLFSENTRY_EXIT_ON_FALSE(buf[0] == '"');
+ reparsed = strtod(buf + 1, NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(isinf(reparsed) && (reparsed > 0.0));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_delete(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "inf_val", WOLFSENTRY_LENGTH_NULL_TERMINATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_double(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "ninf_val", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ -strtod("inf", NULL), 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf));
+ WOLFSENTRY_EXIT_ON_FALSE(buf[0] == '"');
+ reparsed = strtod(buf + 1, NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(isinf(reparsed) && (reparsed < 0.0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+#endif /* !WOLFSENTRY_C89 */
+
+static wolfsentry_errcode_t failing_mk_id_cb(void *context, wolfsentry_ent_id_t *id) {
+ (void)context;
+ (void)id;
+ WOLFSENTRY_ERROR_RETURN(SYS_RESOURCE_FAILED);
+}
+
+static int test_kv_replace_keeps_value_on_id_failure(void) {
+ struct wolfsentry_context *wolfsentry;
+ int64_t value = 0;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_sint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "replaced", WOLFSENTRY_LENGTH_NULL_TERMINATED, 1, 1 /* overwrite_p */));
+
+ wolfsentry->mk_id_cb = failing_mk_id_cb;
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ wolfsentry_user_value_store_sint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "replaced", WOLFSENTRY_LENGTH_NULL_TERMINATED, 2, 1 /* overwrite_p */));
+ wolfsentry->mk_id_cb = NULL;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_sint(
+ WOLFSENTRY_CONTEXT_ARGS_OUT, "replaced", WOLFSENTRY_LENGTH_NULL_TERMINATED, &value));
+ WOLFSENTRY_EXIT_ON_FALSE(value == 1);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_kv_empty_key_rejected(void) {
+ struct wolfsentry_context *wolfsentry;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ /* an empty key must be rejected consistently, whether supplied as an
+ * explicit zero length or as a null-terminated empty string.
+ */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "", 0 /* key_len */,
+ "v", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ INVALID_ARG,
+ wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "v", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_kv_readonly_overwrite_rejected(void) {
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_kv_pair_internal *kv_ref = NULL;
+ const char *value = NULL;
+ int value_len = -1;
+ static const char key[] = "ro_key";
+ static const char orig[] = "orig";
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ orig, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_set_mutability(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 0 /* not mutable */));
+
+ /* overwriting a read-only value must be rejected. */
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ NOT_PERMITTED,
+ wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ "different", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ 1 /* overwrite_p */));
+
+ /* and the value must be unchanged. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ key, WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value, &value_len, &kv_ref));
+ WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(orig));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, orig) == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_release_record(WOLFSENTRY_CONTEXT_ARGS_OUT, &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#ifndef WOLFSENTRY_HAVE_JSON_DOM
+static int test_kv_render_string_escape(void) {
+ struct wolfsentry_context *wolfsentry;
+ char buf[64];
+ static const char raw[] = "a\"b\\c\nd";
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_store_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "esc", WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ raw, (int)(sizeof raw - 1),
+ 0));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(render_only_user_value(WOLFSENTRY_CONTEXT_ARGS_OUT, buf, (int)sizeof buf));
+
+ /* the rendered JSON string must not contain a raw control byte, and the
+ * quote, backslash and newline must all appear escaped.
+ */
+ WOLFSENTRY_EXIT_ON_FALSE(strchr(buf + 1, '\n') == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(strstr(buf, "\\\"") != NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(strstr(buf, "\\\\") != NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(strstr(buf, "\\n") != NULL);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+#endif /* !WOLFSENTRY_HAVE_JSON_DOM */
+
+#endif /* TEST_USER_VALUES */
+
+#ifdef TEST_USER_ADDR_FAMILIES
+
+static int test_user_addr_families(void) {
+
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t action_results;
+#ifdef WOLFSENTRY_PROTOCOL_NAMES
+ char overlong_family_name[WOLFSENTRY_MAX_LABEL_BYTES + 2];
+ wolfsentry_addr_family_t unspec_family_number;
+ const char *unspec_family_name;
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_install(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET,
+ "my_AF",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ my_addr_family_parser,
+ my_addr_family_formatter,
+ 24 /* max_addr_bits */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_remove_bynumber(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_install(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET,
+ "my_AF",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ my_addr_family_parser,
+ my_addr_family_formatter,
+ 24 /* max_addr_bits */));
+
+ action_results = 0;
+
+ /* exercise the plugins to disambiguate failures in the plugins from
+ * JSON-specific failures.
+ */
+ {
+ byte addr_internal[3];
+ wolfsentry_addr_bits_t addr_internal_len;
+ char addr_text[13];
+ int addr_text_len;
+
+ addr_internal_len = (wolfsentry_addr_bits_t)(sizeof addr_internal * 8);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ my_addr_family_parser(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "377/377/377",
+ strlen("377/377/377"),
+ addr_internal,
+ &addr_internal_len));
+ WOLFSENTRY_EXIT_ON_FALSE(addr_internal_len == (wolfsentry_addr_bits_t)(sizeof addr_internal * 8));
+ WOLFSENTRY_EXIT_ON_FALSE(memcmp(addr_internal, "\377\377\377", sizeof addr_internal) == 0);
+
+ addr_text_len = (int)sizeof addr_text;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ my_addr_family_formatter(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ addr_internal,
+ (int)(sizeof addr_internal * 8),
+ addr_text,
+ &addr_text_len));
+ WOLFSENTRY_EXIT_ON_FALSE(addr_text_len == strlen("377/377/377"));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(addr_text, "377/377/377") == 0);
+
+ addr_internal_len = (wolfsentry_addr_bits_t)(sizeof addr_internal * 8);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ my_addr_family_parser(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "0/0/0",
+ strlen("0/0/0"),
+ addr_internal,
+ &addr_internal_len));
+ WOLFSENTRY_EXIT_ON_FALSE(addr_internal_len == (wolfsentry_addr_bits_t)(sizeof addr_internal * 8));
+ WOLFSENTRY_EXIT_ON_FALSE(memcmp(addr_internal, "\0\0\0", sizeof addr_internal) == 0);
+
+ addr_internal_len = (wolfsentry_addr_bits_t)(sizeof addr_internal * 8);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ my_addr_family_parser(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "377/377/",
+ strlen("377/377/"),
+ addr_internal,
+ &addr_internal_len));
+ WOLFSENTRY_EXIT_ON_FALSE(addr_internal_len == 16);
+ WOLFSENTRY_EXIT_ON_FALSE(memcmp(addr_internal, "\377\377", 2) == 0);
+
+ addr_text_len = (int)sizeof addr_text;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ my_addr_family_formatter(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ addr_internal,
+ 16,
+ addr_text,
+ &addr_text_len));
+ WOLFSENTRY_EXIT_ON_FALSE(addr_text_len == strlen("377/377/"));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(addr_text, "377/377/") == 0);
+
+ addr_internal_len = (wolfsentry_addr_bits_t)(sizeof addr_internal * 8);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ my_addr_family_parser(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "377/",
+ strlen("377/"),
+ addr_internal,
+ &addr_internal_len));
+ WOLFSENTRY_EXIT_ON_FALSE(addr_internal_len == 8);
+ WOLFSENTRY_EXIT_ON_FALSE(memcmp(addr_internal, "\377", 1) == 0);
+
+ addr_text_len = (int)sizeof addr_text;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ my_addr_family_formatter(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ addr_internal,
+ 8,
+ addr_text,
+ &addr_text_len));
+ WOLFSENTRY_EXIT_ON_FALSE(addr_text_len == strlen("377/"));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(addr_text, "377/") == 0);
+ }
+
+#ifdef WOLFSENTRY_PROTOCOL_NAMES
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_remove_byname(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "my_AF",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_install(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET,
+ "my_AF",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ my_addr_family_parser,
+ my_addr_family_formatter,
+ 24 /* max_addr_bits */));
+
+ {
+ wolfsentry_addr_family_t family_number;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_pton(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "my_AF",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &family_number));
+ WOLFSENTRY_EXIT_ON_FALSE(family_number == WOLFSENTRY_AF_USER_OFFSET);
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ ITEM_NOT_FOUND,
+ wolfsentry_addr_family_pton(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "no_such_AF",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED, &family_number));
+ }
+
+ {
+ struct wolfsentry_addr_family_bynumber *addr_family = NULL;
+ const char *family_name;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ (wolfsentry_addr_family_ntop(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET,
+ &addr_family,
+ &family_name)));
+ WOLFSENTRY_EXIT_ON_FALSE((family_name != NULL) &&
+ (! strcmp(family_name,"my_AF")));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, addr_family, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_DEALLOCATED));
+ }
+
+#endif /* WOLFSENTRY_PROTOCOL_NAMES */
+
+ {
+ wolfsentry_addr_family_parser_t parser;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_get_parser(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET,
+ &parser));
+ WOLFSENTRY_EXIT_ON_FALSE(parser == my_addr_family_parser);
+ }
+ {
+ wolfsentry_addr_family_formatter_t formatter;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_get_formatter(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET,
+ &formatter));
+ WOLFSENTRY_EXIT_ON_FALSE(formatter == my_addr_family_formatter);
+ }
+
+#ifdef WOLFSENTRY_PROTOCOL_NAMES
+ {
+ wolfsentry_addr_family_t family_number;
+ wolfsentry_addr_bits_t bits;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_pton(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "inet",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &family_number));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_max_addr_bits(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ family_number,
+ &bits));
+ WOLFSENTRY_EXIT_ON_FALSE(bits == 32);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_pton(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "inet6",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &family_number));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_max_addr_bits(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ family_number,
+ &bits));
+ WOLFSENTRY_EXIT_ON_FALSE(bits == 128);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_pton(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "link",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &family_number));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_max_addr_bits(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ family_number,
+ &bits));
+ WOLFSENTRY_EXIT_ON_FALSE(bits == 48);
+
+ /* LINK64 must roundtrip through pton → ntop → verify label. */
+ {
+ struct wolfsentry_addr_family_bynumber *addr_family = NULL;
+ const char *family_name = NULL;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_pton(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "LINK64",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &family_number));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_ntop(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ family_number,
+ &addr_family,
+ &family_name));
+ WOLFSENTRY_EXIT_ON_FALSE((family_name != NULL) && (! strcmp(family_name, "LINK64")));
+ if (addr_family)
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_addr_family_drop_reference(WOLFSENTRY_CONTEXT_ARGS_OUT, addr_family, &action_results));
+ }
+ }
+#endif /* WOLFSENTRY_PROTOCOL_NAMES */
+
+#ifdef WOLFSENTRY_PROTOCOL_NAMES
+ /* A family name longer than the maximum label length must propagate
+ * STRING_ARG_TOO_LONG rather than a lock-management error from an error
+ * path that returns before the context lock is acquired.
+ */
+ memset(overlong_family_name, 'a', sizeof overlong_family_name - 1);
+ overlong_family_name[sizeof overlong_family_name - 1] = 0;
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ STRING_ARG_TOO_LONG,
+ wolfsentry_addr_family_handler_install(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET + 1,
+ overlong_family_name,
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ my_addr_family_parser,
+ my_addr_family_formatter,
+ 24 /* max_addr_bits */));
+#endif /* WOLFSENTRY_PROTOCOL_NAMES */
+
+#ifdef WOLFSENTRY_PROTOCOL_NAMES
+ /* WOLFSENTRY_AF_UNSPEC formats to "UNSPEC" through ntop and must parse back
+ * to the same number through pton so the encode/decode roundtrip holds.
+ */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_ntop(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_UNSPEC,
+ NULL,
+ &unspec_family_name));
+ WOLFSENTRY_EXIT_ON_FALSE((unspec_family_name != NULL) &&
+ (! strcmp(unspec_family_name, "UNSPEC")));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_pton(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "UNSPEC",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &unspec_family_number));
+ WOLFSENTRY_EXIT_ON_FALSE(unspec_family_number == WOLFSENTRY_AF_UNSPEC);
+#endif /* WOLFSENTRY_PROTOCOL_NAMES */
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
+
+#endif /* TEST_USER_ADDR_FAMILIES */
+
+#ifdef TEST_JSON
+
+#include "wolfsentry/wolfsentry_json.h"
+#ifdef WOLFSENTRY_HAVE_JSON_DOM
+#include
+
+static int test_dom_allocator_marker;
+static int test_dom_allocator_context_ok;
+static int test_dom_allocator_malloc_calls;
+
+static void *test_dom_allocator_malloc(
+ WOLFSENTRY_CONTEXT_ARGS_IN_EX(void *context), size_t size)
+{
+ WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
+ ++test_dom_allocator_malloc_calls;
+ if (context != (void *)&test_dom_allocator_marker)
+ test_dom_allocator_context_ok = 0;
+ return malloc(size);
+}
+
+static void test_dom_allocator_free(
+ WOLFSENTRY_CONTEXT_ARGS_IN_EX(void *context), void *ptr)
+{
+ WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
+ if (context != (void *)&test_dom_allocator_marker)
+ test_dom_allocator_context_ok = 0;
+ free(ptr);
+}
+
+static void *test_dom_allocator_realloc(
+ WOLFSENTRY_CONTEXT_ARGS_IN_EX(void *context), void *ptr, size_t size)
+{
+ WOLFSENTRY_CONTEXT_ARGS_THREAD_NOT_USED;
+ if (context != (void *)&test_dom_allocator_marker)
+ test_dom_allocator_context_ok = 0;
+ return realloc(ptr, size);
+}
+#endif
+
+static int test_json(const char *fname, const char *extra_fname) {
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_ent_id_t id;
+
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_install(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET,
+ "my_AF",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ my_addr_family_parser,
+ my_addr_family_formatter,
+ 24 /* max_addr_bits */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_install(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_USER_OFFSET + 1,
+ "my_AF2",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ my_addr_family_parser,
+ my_addr_family_formatter,
+ 24 /* max_addr_bits */));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NO_ROUTES_OR_EVENTS, 1));
+
+ {
+ static const char test_string[] = "hello";
+ const char *value = NULL;
+ int value_len = -1;
+ struct wolfsentry_kv_pair_internal *kv_ref;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "user-string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value,
+ &value_len,
+ &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(test_string));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, test_string) == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_release_record(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &kv_ref));
+ }
+
+ {
+ static const char user_cert_string[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDnzCCAyWgAwIBAgICEAEwCgYIKoZIzj0EAwMwgZcxCzAJBgNVBAYTAlVTMRMw\n"
+ "EQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAd3\n"
+ "b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEYMBYGA1UEAwwPd3d3LndvbGZz\n"
+ "c2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMCAXDTIyMDIx\n"
+ "NTEyNTAyNFoYDzIwNTIwMjA4MTI1MDI0WjCBlTELMAkGA1UEBhMCVVMxEzARBgNV\n"
+ "BAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxEDAOBgNVBAoMB0VsaXB0\n"
+ "aWMxEjAQBgNVBAsMCUVDQzM4NFNydjEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29t\n"
+ "MR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMHYwEAYHKoZIzj0CAQYF\n"
+ "K4EEACIDYgAE6s+TTywJuzkUD1Zkw0C03w5jruVxSwDMBJf/4ek4lrtfkbJqzLU5\n"
+ "X49wWfEB9lorAWxoC89VJa9tmEgKqHTJqRegDMP70yNo/gQ8Y1CIO7lPfGc09zup\n"
+ "c+cbw1FeIhjso4IBQDCCATwwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAw\n"
+ "HQYDVR0OBBYEFII78mUv87QAxrwG/XlCdUtl0c68MIHXBgNVHSMEgc8wgcyAFKvg\n"
+ "wyZMGNRyu9KEjJwKBZKAElNSoYGdpIGaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UE\n"
+ "CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEQMA4GA1UECgwHd29sZlNT\n"
+ "TDEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNv\n"
+ "bTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbYIUaiYbTQIaM//CRxT5\n"
+ "51VgWi5/ESkwDgYDVR0PAQH/BAQDAgOoMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAoG\n"
+ "CCqGSM49BAMDA2gAMGUCMQCEPZBU/y/EetTYGOKzLbtCN0CmHwmD3rwEeoLcVRdC\n"
+ "XBeqB0LcyPZQzRS3Bhk5HyQCMBNiS5/JoIzSac8WToa9nik4ROlKOmOgZjiV4n3j\n"
+ "F+yUIbg9aV7K5ISc2mF9G1G/0Q==\n"
+ "-----END CERTIFICATE-----\n";
+ const char *value = NULL;
+ int value_len = -1;
+ struct wolfsentry_kv_pair_internal *kv_ref;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "user-cert-string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value,
+ &value_len,
+ &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen(user_cert_string));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, user_cert_string) == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_release_record(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &kv_ref));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(load_test_action_handlers(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_DRY_RUN, 1));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT, 1));
+
+ WOLFSENTRY_EXIT_ON_SUCCESS(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT, 0));
+
+ {
+ static const char *bad_json[] = {
+ "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"user-string\" : \"should collide\" } }",
+ "{ \"wolfsentry-config-version\" : 2, \"user-values\" : { \"ok-user-string\" : \"shouldn't collide\" } }",
+ "{ \"wolfsentry-config-version\" : 1, \"not-user-values\" : { \"ok-user-string\" : \"shouldn't collide\" } }",
+ "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"too-long-user-string-123456789-abcdefghi\" : \"x\" } }",
+ };
+ const char **bad_json_i;
+ for (bad_json_i = bad_json; bad_json_i < &bad_json[length_of_array(bad_json)]; ++bad_json_i) {
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (const unsigned char *)*bad_json_i,
+ strlen(*bad_json_i),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT,
+ NULL,
+ 0));
+ }
+ }
+
+ {
+ struct wolfsentry_route_table *main_routes, main_routes_copy;
+ const char *value = NULL;
+ int value_len = -1;
+ struct wolfsentry_kv_pair_internal *kv_ref;
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_ent_id_t route_id;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ main_routes_copy = *main_routes;
+
+ if (extra_fname) {
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 13579;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+
+ memcpy(remote.sa.addr,"\12\24\36\50",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\62\74\106\120",sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == WOLFSENTRY_ENT_ID_NONE);
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_FALLTHROUGH));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, extra_fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT, 1));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(route_id == WOLFSENTRY_ENT_ID_NONE);
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == WOLFSENTRY_ACTION_RES_ACCEPT);
+
+ memcpy(remote.sa.addr,"\13\24\36\50",sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ NULL /* event_label */,
+ 0 /* event_label_len */,
+ NULL /* caller_arg */,
+ &route_id,
+ &inexact_matches,
+ &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(route_id == WOLFSENTRY_ENT_ID_NONE);
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_FALLTHROUGH));
+ } else {
+ static const char *trivial_test_json = "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"extra-user-string\" : \"extra hello\" } }";
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (const unsigned char *)trivial_test_json,
+ strlen(trivial_test_json),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT,
+ NULL,
+ 0));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+
+ WOLFSENTRY_EXIT_ON_FALSE((main_routes->max_purgeable_routes == main_routes_copy.max_purgeable_routes) &&
+ (main_routes->default_policy == main_routes_copy.default_policy));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "user-string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value,
+ &value_len,
+ &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen("hello"));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, "hello") == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_release_record(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &kv_ref));
+
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_get_string(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ "extra-user-string",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ &value,
+ &value_len,
+ &kv_ref));
+
+ WOLFSENTRY_EXIT_ON_FALSE(value_len == (int)strlen("extra hello"));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(value, "extra hello") == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_user_value_release_record(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &kv_ref));
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, 1));
+
+ {
+ struct wolfsentry_route_table *main_routes, main_routes_copy;
+ struct wolfsentry_cursor *cursor;
+ WOLFSENTRY_BYTE_STREAM_DECLARE_STACK(json_out, 16384);
+ WOLFSENTRY_BYTE_STREAM_DECLARE_HEAP(json_out2, 16384);
+ wolfsentry_hitcount_t n_seen = 0;
+ char err_buf[512];
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_BYTE_STREAM_INIT_HEAP(json_out2) != NULL);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+
+ WOLFSENTRY_BYTE_STREAM_RESET(json_out);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_dump_json_start(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ &cursor,
+ WOLFSENTRY_BYTE_STREAM_PTR(json_out),
+ WOLFSENTRY_BYTE_STREAM_SPC(json_out),
+ WOLFSENTRY_FORMAT_FLAG_NONE));
+
+ for (;;) {
+ ret = wolfsentry_route_table_dump_json_next(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ cursor,
+ WOLFSENTRY_BYTE_STREAM_PTR(json_out),
+ WOLFSENTRY_BYTE_STREAM_SPC(json_out),
+ WOLFSENTRY_FORMAT_FLAG_NONE);
+ if (ret < 0) {
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND))
+ WOLFSENTRY_EXIT_ON_FAILURE(ret);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_dump_json_end(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ &cursor,
+ WOLFSENTRY_BYTE_STREAM_PTR(json_out),
+ WOLFSENTRY_BYTE_STREAM_SPC(json_out),
+ WOLFSENTRY_FORMAT_FLAG_NONE));
+ } else
+ ++n_seen;
+ if (ret < 0)
+ break;
+ }
+
+ WOLFSENTRY_EXIT_ON_FALSE(n_seen == wolfsentry->routes->header.n_ents);
+
+ main_routes_copy = *main_routes;
+
+ ret = wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_BYTE_STREAM_HEAD(json_out),
+ WOLFSENTRY_BYTE_STREAM_LEN(json_out),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_LOAD_THEN_COMMIT | WOLFSENTRY_CONFIG_LOAD_FLAG_FLUSH_ONLY_ROUTES,
+ err_buf,
+ sizeof err_buf);
+ if (ret < 0) {
+ (void)fprintf(stderr, "%.*s\n", (int)sizeof err_buf, err_buf);
+ WOLFSENTRY_EXIT_ON_FAILURE(ret);
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+
+ WOLFSENTRY_EXIT_ON_FALSE((main_routes->max_purgeable_routes == main_routes_copy.max_purgeable_routes) &&
+ (main_routes->default_policy == main_routes_copy.default_policy));
+
+ WOLFSENTRY_BYTE_STREAM_RESET(json_out2);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_dump_json_start(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ &cursor,
+ WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
+ WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
+ WOLFSENTRY_FORMAT_FLAG_NONE));
+
+ for (;;) {
+ ret = wolfsentry_route_table_dump_json_next(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ cursor,
+ WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
+ WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
+ WOLFSENTRY_FORMAT_FLAG_NONE);
+ if (ret < 0) {
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_dump_json_end(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ &cursor,
+ WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
+ WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
+ WOLFSENTRY_FORMAT_FLAG_NONE));
+ break;
+ }
+ }
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_BYTE_STREAM_LEN(json_out2) == WOLFSENTRY_BYTE_STREAM_LEN(json_out));
+ WOLFSENTRY_EXIT_ON_FALSE(memcmp(json_out, json_out2, WOLFSENTRY_BYTE_STREAM_LEN(json_out)) == 0);
+
+ ret = wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_BYTE_STREAM_HEAD(json_out),
+ WOLFSENTRY_BYTE_STREAM_LEN(json_out),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_FLUSH_ONLY_ROUTES,
+ err_buf,
+ sizeof err_buf);
+ if (ret < 0) {
+ (void)fprintf(stderr, "%.*s\n", (int)sizeof err_buf, err_buf);
+ WOLFSENTRY_EXIT_ON_FAILURE(ret);
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+
+ WOLFSENTRY_BYTE_STREAM_RESET(json_out2);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_dump_json_start(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ &cursor,
WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
WOLFSENTRY_FORMAT_FLAG_NONE));
- for (;;) {
- ret = wolfsentry_route_table_dump_json_next(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- cursor,
- WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
- WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
- WOLFSENTRY_FORMAT_FLAG_NONE);
- if (ret < 0) {
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND));
+ for (;;) {
+ ret = wolfsentry_route_table_dump_json_next(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ cursor,
+ WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
+ WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
+ WOLFSENTRY_FORMAT_FLAG_NONE);
+ if (ret < 0) {
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_table_dump_json_end(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ main_routes,
+ &cursor,
+ WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
+ WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
+ WOLFSENTRY_FORMAT_FLAG_NONE));
+ break;
+ }
+ }
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_BYTE_STREAM_LEN(json_out2) == WOLFSENTRY_BYTE_STREAM_LEN(json_out));
+ WOLFSENTRY_EXIT_ON_FALSE(memcmp(json_out, json_out2, WOLFSENTRY_BYTE_STREAM_LEN(json_out)) == 0);
+
+ WOLFSENTRY_EXIT_ON_FALSE(n_seen == wolfsentry->routes->header.n_ents);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_BYTE_STREAM_FREE_HEAP(json_out2);
+ }
+
+ WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, 1));
+
+ {
+ struct wolfsentry_context *ctx_clone;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_AS_AT_CREATION));
+ WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone), fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, 1));
+
+#ifdef WOLFSENTRY_HAVE_JSON_DOM
+ {
+ static const char *sequential_test_json = "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"user-json2\" : { \"json\" : { \"z\" : 26, \"y\" : 25, \"x\" : 24 } } } }";
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone),
+ (const unsigned char *)sequential_test_json,
+ strlen(sequential_test_json),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_JSON_DOM_MAINTAINDICTORDER,
+ NULL,
+ 0));
+ }
+#endif
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_exchange(WOLFSENTRY_CONTEXT_ARGS_OUT, ctx_clone));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone)));
+ }
+
+ {
+ struct wolfsentry_context *ctx_clone;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone)));
+ }
+
+ {
+ struct wolfsentry_cursor *cursor;
+ struct wolfsentry_route *route;
+ struct wolfsentry_route_exports route_exports;
+ struct wolfsentry_route_table *main_routes;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor));
+ for (ret = wolfsentry_route_table_iterate_current(main_routes, cursor, &route);
+ ret >= 0;
+ ret = wolfsentry_route_table_iterate_next(main_routes, cursor, &route)) {
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, route, &route_exports));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_exports_render(WOLFSENTRY_CONTEXT_ARGS_OUT, &route_exports, stdout));
+ putc('\n', stdout);
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ }
+
+#ifndef WOLFSENTRY_NO_STDIO
+ {
+ struct wolfsentry_kv_pair_internal *kv_ref;
+ struct wolfsentry_cursor *cursor;
+ const struct wolfsentry_kv_pair *kv_exports;
+ const char *val_type;
+ char val_buf[2048];
+ int val_buf_space;
+ wolfsentry_hitcount_t n_seen = 0;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
+ for (ret = wolfsentry_user_values_iterate_current(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref);
+ ret >= 0;
+ ret = wolfsentry_user_values_iterate_next(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref)) {
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_pair_export(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_ref, &kv_exports));
+ val_buf_space = sizeof val_buf;
+ if (wolfsentry_kv_type_to_string(WOLFSENTRY_KV_TYPE(kv_exports), &val_type) < 0)
+ val_type = "?";
+ if (wolfsentry_kv_render_value(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_exports, val_buf, &val_buf_space) < 0) {
+ if (WOLFSENTRY_KV_TYPE(kv_exports) == WOLFSENTRY_KV_BYTES)
+ (void)snprintf(val_buf, sizeof val_buf, "<%.*s>", (int)WOLFSENTRY_KV_V_BYTES_LEN(kv_exports), WOLFSENTRY_KV_V_BYTES(kv_exports));
+ else
+ strcpy(val_buf,"?");
+ }
+ switch (WOLFSENTRY_KV_TYPE(kv_exports)) {
+ case WOLFSENTRY_KV_TRUE:
+ case WOLFSENTRY_KV_FALSE:
+ case WOLFSENTRY_KV_NULL:
+ printf("{ \"%.*s\" : %s }\n",
+ (int)WOLFSENTRY_KV_KEY_LEN(kv_exports),
+ WOLFSENTRY_KV_KEY(kv_exports),
+ val_buf);
+ break;
+ default:
+ printf("{ \"%.*s\" : { \"%s\" : %s } }\n",
+ (int)WOLFSENTRY_KV_KEY_LEN(kv_exports),
+ WOLFSENTRY_KV_KEY(kv_exports),
+ val_type,
+ val_buf);
+ }
+ ++n_seen;
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE(n_seen == wolfsentry->user_values->header.n_ents);
+ }
+#endif
+
+ {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr,"\177\0\0\1",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\177\0\0\1",sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
+ }
+
+ {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+
+ remote.sa.sa_family = local.sa.sa_family = WOLFSENTRY_AF_CHAOS;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 12345;
+ local.sa.sa_port = 443;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 1;
+ memcpy(remote.sa.addr,"\177\0\0\1",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\177\0\0\1",sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_FALLTHROUGH));
+ }
+
+ {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 0;
+ local.sa.sa_port = 13579;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 0;
+ memcpy(remote.sa.addr,"\1\2\3\4",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\0\0\0\0",sizeof local.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET));
+ }
+
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+/* test bitmap-based matching of CAN addresses. */
+ {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+
+ remote.sa.sa_family = local.sa.sa_family = WOLFSENTRY_AF_CAN;
+ remote.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;;
+ local.sa.addr_len = 0;
+ remote.sa.interface = local.sa.interface = 0;
+ memcpy(remote.sa.addr,"\x1f\xff\xff\xed",sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
+ WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER0));
+
+ memcpy(remote.sa.addr,"\x1f\xff\xff\xec",sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
+ WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER0));
+
+ memcpy(remote.sa.addr,"\x00\x00\x07\xcb",sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
+ WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER1));
+
+ memcpy(remote.sa.addr,"\x1f\xff\xff\xff",sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
+ WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER2));
+
+ memcpy(remote.sa.addr,"\x1f\xff\x01\x23",sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
+ WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER3));
+
+ /* one prefix-matched CAN address to try */
+ memcpy(remote.sa.addr,"\x15\x67\x01\x22",sizeof remote.addr_buf);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
+ WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches, &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER4));
+ }
+#endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */
+
+#ifdef WOLFSENTRY_HAVE_JSON_DOM
+ {
+ unsigned char *test_json_document = NULL;
+ int fd = -1;
+ JSON_VALUE p_root;
+ JSON_VALUE *v1 = NULL, *v2 = NULL, *v3 = NULL;
+ struct stat st;
+ static const JSON_CONFIG centijson_config = {
+ 65536, /* max_total_len */
+ 1000, /* max_total_values */
+ 20, /* max_number_len */
+ WOLFSENTRY_KV_MAX_VALUE_BYTES, /* max_string_len */
+ WOLFSENTRY_MAX_LABEL_BYTES, /* max_key_len */
+ 10, /* max_nesting_level */
+ JSON_NOSCALARROOT /* flags */
+ };
+ JSON_INPUT_POS json_pos;
+ const unsigned char *s;
+ size_t alen, i;
+
+ WOLFSENTRY_EXIT_ON_SYSFAILURE(fd = open(fname, O_RDONLY));
+ WOLFSENTRY_EXIT_ON_SYSFAILURE(fstat(fd, &st));
+ WOLFSENTRY_EXIT_ON_SYSFALSE((test_json_document = (unsigned char *)malloc((size_t)st.st_size)) != NULL);
+ WOLFSENTRY_EXIT_ON_SYSFALSE(read(fd, test_json_document, (size_t)st.st_size) == st.st_size);
+
+ if ((ret = json_dom_parse(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), test_json_document, (size_t)st.st_size, ¢ijson_config,
+ 0 /* dom_flags */, &p_root, &json_pos)) < 0) {
+ const void *p = memchr((const char *)(test_json_document + json_pos.offset), '\n', (size_t)st.st_size - json_pos.offset);
+ int linelen = p ? ((int)((unsigned char *)p - (test_json_document + json_pos.offset)) + (int)json_pos.column_number - 1) :
+ (((int)st.st_size - (int)json_pos.offset) + (int)json_pos.column_number - 1);
+ if (WOLFSENTRY_ERROR_DECODE_SOURCE_ID(ret) == WOLFSENTRY_SOURCE_ID_UNSET)
+ (void)fprintf(stderr, "json_dom_parse failed at offset " SIZET_FMT ", L%u, col %u, with centijson code %d: %s\n", json_pos.offset,json_pos.line_number, json_pos.column_number, ret, json_dom_error_str(ret));
+ else
+ (void)fprintf(stderr, "json_dom_parse failed at offset " SIZET_FMT ", L%u, col %u, with " WOLFSENTRY_ERROR_FMT "\n", json_pos.offset,json_pos.line_number, json_pos.column_number, WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ (void)fprintf(stderr,"%.*s\n", linelen, test_json_document + json_pos.offset - json_pos.column_number + 1);
+ exit(1);
+ }
+
+ WOLFSENTRY_EXIT_ON_TRUE((v1 = json_value_path(&p_root, "wolfsentry-config-version")) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_uint32(v1) == 1U);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v1)));
+
+ WOLFSENTRY_EXIT_ON_TRUE((v1 = json_value_path(&p_root, "default-policies")) == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE((v2 = json_value_path(v1, "default-policy")) == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE((s = json_value_string(v2)) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp((const char *)s, "reject") == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v2)));
+
+ WOLFSENTRY_EXIT_ON_TRUE((v2 = json_value_path(v1, "default-event")) == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE((s = json_value_string(v2)) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp((const char *)s, "static-route-parent") == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v2)));
+ v2 = NULL;
+
+ WOLFSENTRY_EXIT_ON_TRUE((v1 = json_value_path(&p_root, "static-routes-insert")) == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE((alen = json_value_array_size(v1)) == 0);
+ for (i = 0; i < alen; ++i) {
+ WOLFSENTRY_EXIT_ON_TRUE((v2 = json_value_array_get(v1, i)) == NULL);
+ v3 = json_value_path(v2, "family");
+ if (v3) {
+ WOLFSENTRY_EXIT_ON_TRUE((json_value_string(v3) == NULL) && (json_value_int32(v3) <= 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v3)));
+ v3 = NULL;
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v2)));
+ v2 = NULL;
+ }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v1)));
+
+ WOLFSENTRY_EXIT_ON_TRUE((v1 = json_value_path(&p_root, "user-values/user-null")) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_type(v1) == JSON_VALUE_NULL);
+
+ if (v3)
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v3)));
+ if (v2)
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v2)));
+ if (v1)
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v1)));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), &p_root)));
+ if (test_json_document != NULL)
+ free(test_json_document);
+ if (fd != -1)
+ (void)close(fd);
+ }
+#endif /* WOLFSENTRY_HAVE_JSON_DOM */
+
+#ifdef WOLFSENTRY_HAVE_JSON_DOM
+ /* A duplicate key whose value is a container, parsed with USEFIRST, must
+ * leave the DOM nesting intact: the first value wins and the discarded
+ * container must not leak its children into the enclosing object. */
+ {
+ static const char dupkey_doc[] =
+ "{ \"k\" : 1, \"k\" : { \"x\" : 2 }, \"after\" : 3 }";
+ static const JSON_CONFIG dupkey_config = {
+ 65536, 1000, 20, WOLFSENTRY_KV_MAX_VALUE_BYTES,
+ WOLFSENTRY_MAX_LABEL_BYTES, 10, JSON_NOSCALARROOT
+ };
+ JSON_VALUE dupkey_root;
+ const JSON_VALUE *dupkey_v;
+ JSON_INPUT_POS dupkey_pos;
+
+ WOLFSENTRY_EXIT_ON_TRUE(
+ json_dom_parse(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)),
+ (const unsigned char *)dupkey_doc, strlen(dupkey_doc), &dupkey_config,
+ JSON_DOM_DUPKEY_USEFIRST, &dupkey_root, &dupkey_pos) < 0);
+
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_type(&dupkey_root) == JSON_VALUE_DICT);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_dict_size(&dupkey_root) == 2);
+ WOLFSENTRY_EXIT_ON_TRUE((dupkey_v = json_value_path(&dupkey_root, "k")) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(dupkey_v) == 1);
+ WOLFSENTRY_EXIT_ON_TRUE((dupkey_v = json_value_path(&dupkey_root, "after")) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(dupkey_v) == 3);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&dupkey_root, "x") == NULL);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(
+ json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), &dupkey_root)));
+ }
+#endif /* WOLFSENTRY_HAVE_JSON_DOM */
+
+#ifdef WOLFSENTRY_HAVE_JSON_DOM
+ /* USEFIRST must also drop duplicate containers whose value is a nested
+ * object or an array: the first value wins, the duplicate (and anything
+ * nested in it) is dropped whole, and the surrounding keys stay intact. */
+ {
+ static const char dupnest_doc[] =
+ "{ \"k\" : { \"a\" : 1 },"
+ " \"k\" : { \"b\" : { \"c\" : 2 } },"
+ " \"arr\" : [ 10, 20 ],"
+ " \"arr\" : [ 30, 40, 50 ],"
+ " \"after\" : 7 }";
+ static const JSON_CONFIG dupnest_config = {
+ 65536, 1000, 20, WOLFSENTRY_KV_MAX_VALUE_BYTES,
+ WOLFSENTRY_MAX_LABEL_BYTES, 10, JSON_NOSCALARROOT
+ };
+ JSON_VALUE dupnest_root;
+ const JSON_VALUE *dupnest_v;
+ JSON_INPUT_POS dupnest_pos;
+
+ WOLFSENTRY_EXIT_ON_TRUE(
+ json_dom_parse(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)),
+ (const unsigned char *)dupnest_doc, strlen(dupnest_doc), &dupnest_config,
+ JSON_DOM_DUPKEY_USEFIRST, &dupnest_root, &dupnest_pos) < 0);
+
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_type(&dupnest_root) == JSON_VALUE_DICT);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_dict_size(&dupnest_root) == 3);
+
+ /* first (object) value of "k" wins; the nested duplicate is dropped whole */
+ WOLFSENTRY_EXIT_ON_TRUE((dupnest_v = json_value_path(&dupnest_root, "k")) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_type(dupnest_v) == JSON_VALUE_DICT);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_dict_size(dupnest_v) == 1);
+ WOLFSENTRY_EXIT_ON_TRUE((dupnest_v = json_value_path(&dupnest_root, "k/a")) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(dupnest_v) == 1);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&dupnest_root, "k/b") == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&dupnest_root, "b") == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&dupnest_root, "c") == NULL);
+
+ /* first (array) value of "arr" wins; the longer duplicate is dropped */
+ WOLFSENTRY_EXIT_ON_TRUE((dupnest_v = json_value_path(&dupnest_root, "arr")) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_type(dupnest_v) == JSON_VALUE_ARRAY);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_array_size(dupnest_v) == 2);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_array_get(dupnest_v, 0) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(json_value_array_get(dupnest_v, 0)) == 10);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(json_value_array_get(dupnest_v, 1)) == 20);
+
+ WOLFSENTRY_EXIT_ON_TRUE((dupnest_v = json_value_path(&dupnest_root, "after")) == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(dupnest_v) == 7);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(
+ json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), &dupnest_root)));
+ }
+#endif /* WOLFSENTRY_HAVE_JSON_DOM */
+
+#ifdef WOLFSENTRY_HAVE_JSON_DOM
+ /* A custom allocator must receive its own context pointer, not the
+ * allocator object, as the first argument to its callbacks. */
+ {
+ static const char ctx_doc[] = "{ \"a\" : [ 1, 2, 3 ] }";
+ static const JSON_CONFIG ctx_config = {
+ 65536, 1000, 20, WOLFSENTRY_KV_MAX_VALUE_BYTES,
+ WOLFSENTRY_MAX_LABEL_BYTES, 10, JSON_NOSCALARROOT
+ };
+ struct wolfsentry_allocator ctx_allocator;
+ JSON_VALUE ctx_root;
+ JSON_INPUT_POS ctx_pos;
+
+ ctx_allocator.context = &test_dom_allocator_marker;
+ ctx_allocator.malloc = test_dom_allocator_malloc;
+ ctx_allocator.free = test_dom_allocator_free;
+ ctx_allocator.realloc = test_dom_allocator_realloc;
+ ctx_allocator.memalign = NULL;
+ ctx_allocator.free_aligned = NULL;
+
+ test_dom_allocator_context_ok = 1;
+ test_dom_allocator_malloc_calls = 0;
+
+ WOLFSENTRY_EXIT_ON_TRUE(
+ json_dom_parse(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_allocator),
+ (const unsigned char *)ctx_doc, strlen(ctx_doc), &ctx_config,
+ 0, &ctx_root, &ctx_pos) < 0);
+
+ WOLFSENTRY_EXIT_ON_FALSE(test_dom_allocator_malloc_calls > 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_dom_allocator_context_ok == 1);
+
+ WOLFSENTRY_EXIT_ON_TRUE(
+ json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_allocator), &ctx_root) < 0);
+ }
+#endif /* WOLFSENTRY_HAVE_JSON_DOM */
+
+
+ /* test aux event, flag bit filters & frobbing, and good/bad route insertion by
+ * wolfsentry_builtin_action_track_peer()
+ */
+ {
+ struct {
+ struct wolfsentry_sockaddr sa;
+ byte addr_buf[4];
+ } remote, local;
+ wolfsentry_route_flags_t inexact_matches;
+ wolfsentry_action_res_t action_results;
+ struct wolfsentry_route *ephemeral_route;
+ wolfsentry_route_flags_t ephemeral_route_flags;
+ int i;
+
+ /* first, trigger insertion of DNS dynamic pinhole. */
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_UDP;
+ remote.sa.sa_port = 53;
+ local.sa.sa_port = 65432;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 0;
+ memcpy(remote.sa.addr,"\1\2\3\4",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\0\0\0\0",sizeof local.addr_buf);
+
+ action_results = WOLFSENTRY_ACTION_RES_USER_BASE | (WOLFSENTRY_ACTION_RES_USER_BASE << 1U) | (WOLFSENTRY_ACTION_RES_USER_BASE << 3U);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_INSERTED | WOLFSENTRY_ACTION_RES_USER_BASE | (WOLFSENTRY_ACTION_RES_USER_BASE << 1U) | (WOLFSENTRY_ACTION_RES_USER_BASE << 3U)));
+
+ /* next, make sure the dynamic pinhole is live, by checking for an exact match. */
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_USER5));
+ WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD);
+
+ /* make sure the pinhole route has the expected flags on it. */
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(ephemeral_route, &ephemeral_route_flags));
+ /* make sure it's really ephemeral. */
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route->meta.purge_after != 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route_flags ==
+ (WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD |
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT |
+ WOLFSENTRY_ROUTE_FLAG_IN_TABLE |
+ WOLFSENTRY_ROUTE_FLAG_INSERT_ACTIONS_CALLED |
+ WOLFSENTRY_ROUTE_FLAG_GREENLISTED));
+
+ /* now trigger insertion of a tracking rule to catch a simulated port scan. */
+
+ remote.sa.sa_family = local.sa.sa_family = AF_INET;
+ remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
+ remote.sa.sa_port = 1234;
+ local.sa.sa_port = 65432;
+ remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
+ remote.sa.interface = local.sa.interface = 0;
+ memcpy(remote.sa.addr,"\1\2\3\4",sizeof remote.addr_buf);
+ memcpy(local.sa.addr,"\0\0\0\0",sizeof local.addr_buf);
+
+ action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_FALLTHROUGH | WOLFSENTRY_ACTION_RES_INSERTED | WOLFSENTRY_ACTION_RES_UNREACHABLE | (WOLFSENTRY_ACTION_RES_USER_BASE << 4U)));
+
+ /* iteratively increment the derog-thresh-for-penalty-boxing. */
+ for (i=0; i<3; ++i) {
+ action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_FALLTHROUGH | WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_USER5));
+ }
+
+ /* trigger the penalty boxing. */
+ action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_UPDATE | WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_USER5));
+
+ /* confirm the penalty boxing. */
+ action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_USER5));
+
+
+#ifdef WOLFSENTRY_UNITTEST_BENCHMARKS
+ {
+ struct timespec start_at, end_at;
+ uint64_t start_at_cycles, end_at_cycles;
+ double ns_per_call, cycles_per_call;
+
+ test_action_enabled = 0;
+
+ WOLFSENTRY_EXIT_ON_SYSFAILURE(clock_gettime(CLOCK_MONOTONIC, &start_at));
+ start_at_cycles = get_intel_cycles();
+ for (i=0; i<1000000; ++i) {
+ action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_table_dump_json_end(
+ wolfsentry_route_event_dispatch_with_inited_result(
WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- &cursor,
- WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
- WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
- WOLFSENTRY_FORMAT_FLAG_NONE));
- break;
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
}
+ end_at_cycles = get_intel_cycles();
+ WOLFSENTRY_EXIT_ON_SYSFAILURE(clock_gettime(CLOCK_MONOTONIC, &end_at));
+ ns_per_call = ((double)(end_at.tv_sec - start_at.tv_sec) * 1000000000.0 + (double)(end_at.tv_nsec - start_at.tv_nsec)) / (double)i;
+ cycles_per_call = (double)(end_at_cycles - start_at_cycles) / (double)i;
+ printf("benchmark wolfsentry_route_event_dispatch_with_inited_result() with JSON-loaded route table, matching penalty-boxed route: %.2f ns/call %.2f cycles/call\n", ns_per_call, cycles_per_call);
+
+#ifdef WOLFSENTRY_MAX_CYCLES_PER_CALL_JSON_LOADED
+ if (cycles_per_call > (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_JSON_LOADED) {
+ (void)fprintf(stderr, "benchmark wolfsentry_route_event_dispatch_with_inited_result() with JSON-loaded route table, matching penalty-boxed route: measured %.2f cycles/call exceeds max %.2f\n", cycles_per_call, (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_JSON_LOADED);
+ WOLFSENTRY_EXIT_ON_TRUE(cycles_per_call > (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_JSON_LOADED);
+ }
+#endif
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_USER5));
+
+ test_action_enabled = 1;
}
+#endif /* WOLFSENTRY_UNITTEST_BENCHMARKS */
+
+
+ /* force release from penalty box with explicit _RES_COMMENDABLE.
+ * note that the result will still be _REJECT because of the fallthrough policy in test-config.json.
+ * in practice, port scanning defenses are only needed on configurations that default open, in which
+ * case release from the penalty box would restore fallthrough to _ACCEPT.
+ * on the other hand, where port scanning is being tracked purely for purposes of IDS-type visibility,
+ * fallthrough to _REJECT is perfectly appropriate.
+ */
+ action_results = WOLFSENTRY_ACTION_RES_COMMENDABLE;
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_COMMENDABLE | WOLFSENTRY_ACTION_RES_UPDATE | WOLFSENTRY_ACTION_RES_FALLTHROUGH | WOLFSENTRY_ACTION_RES_USER5));
+
+ /* recheck to confirm no update on a second match with zero initial action_results. */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_FALLTHROUGH | WOLFSENTRY_ACTION_RES_USER5));
+
+ {
+ struct wolfsentry_route_exports ephemeral_route_exports;
+ wolfsentry_ent_id_t reinsert_id;
+ wolfsentry_time_t purge_after_after_deletion;
+
+ /* increment connection_count. */
+ action_results = WOLFSENTRY_ACTION_RES_CONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, ephemeral_route, &ephemeral_route_exports));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(
+ DEFERRED,
+ wolfsentry_route_delete_by_id(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ id,
+ NULL /* trigger_label */,
+ 0 /* trigger_label_len */,
+ &action_results));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_BYTE_STREAM_LEN(json_out2) == WOLFSENTRY_BYTE_STREAM_LEN(json_out));
- WOLFSENTRY_EXIT_ON_FALSE(memcmp(json_out, json_out2, WOLFSENTRY_BYTE_STREAM_LEN(json_out)) == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, ephemeral_route, &ephemeral_route_exports));
+ purge_after_after_deletion = ephemeral_route_exports.meta.purge_after;
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- ret = wolfsentry_config_json_oneshot(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- WOLFSENTRY_BYTE_STREAM_HEAD(json_out),
- WOLFSENTRY_BYTE_STREAM_LEN(json_out),
- WOLFSENTRY_CONFIG_LOAD_FLAG_FLUSH_ONLY_ROUTES,
- err_buf,
- sizeof err_buf);
- if (ret < 0) {
- (void)fprintf(stderr, "%.*s\n", (int)sizeof err_buf, err_buf);
- WOLFSENTRY_EXIT_ON_FAILURE(ret);
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_reset_metadata_exports(&ephemeral_route_exports));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(
+ ALREADY_OK,
+ wolfsentry_route_insert_by_exports(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ NULL /* caller_arg */,
+ &ephemeral_route_exports,
+ &reinsert_id,
+ &action_results));
- WOLFSENTRY_BYTE_STREAM_RESET(json_out2);
+ WOLFSENTRY_EXIT_ON_FALSE(reinsert_id == id);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_table_dump_json_start(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- &cursor,
- WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
- WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
- WOLFSENTRY_FORMAT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, ephemeral_route, &ephemeral_route_exports));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- for (;;) {
- ret = wolfsentry_route_table_dump_json_next(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- cursor,
- WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
- WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
- WOLFSENTRY_FORMAT_FLAG_NONE);
- if (ret < 0) {
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(ret, ITEM_NOT_FOUND));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_table_dump_json_end(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- main_routes,
- &cursor,
- WOLFSENTRY_BYTE_STREAM_PTR(json_out2),
- WOLFSENTRY_BYTE_STREAM_SPC(json_out2),
- WOLFSENTRY_FORMAT_FLAG_NONE));
- break;
- }
- }
+ WOLFSENTRY_EXIT_ON_TRUE(ephemeral_route_exports.meta.purge_after == purge_after_after_deletion);
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route_exports.meta.connection_count == 1);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_BYTE_STREAM_LEN(json_out2) == WOLFSENTRY_BYTE_STREAM_LEN(json_out));
- WOLFSENTRY_EXIT_ON_FALSE(memcmp(json_out, json_out2, WOLFSENTRY_BYTE_STREAM_LEN(json_out)) == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_purge_time_set(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ ephemeral_route,
+ 0));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FALSE(n_seen == wolfsentry->routes->header.n_ents);
+ /* decrement connection_count. */
+ action_results = WOLFSENTRY_ACTION_RES_DISCONNECT;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_event_dispatch_with_inited_result(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ &remote.sa,
+ &local.sa,
+ WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
+ "call-in-from-unit-test",
+ WOLFSENTRY_LENGTH_NULL_TERMINATED,
+ (void *)0x12345678 /* caller_arg */,
+ &id,
+ &inexact_matches,
+ &action_results));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, ephemeral_route, &ephemeral_route_exports));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_BYTE_STREAM_FREE_HEAP(json_out2);
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route_exports.meta.purge_after == 0);
+ WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route_exports.meta.connection_count == 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_route_purge_time_set(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ ephemeral_route,
+ 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ITEM_NOT_FOUND, wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ }
}
- WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT, fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, 1));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- {
- struct wolfsentry_context *ctx_clone;
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_AS_AT_CREATION));
- WOLFSENTRY_EXIT_ON_FAILURE(json_feed_file(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone), fname, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, 1));
+ WOLFSENTRY_RETURN_OK;
+}
#ifdef WOLFSENTRY_HAVE_JSON_DOM
- {
- static const char *sequential_test_json = "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"user-json2\" : { \"json\" : { \"z\" : 26, \"y\" : 25, \"x\" : 24 } } } }";
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_config_json_oneshot(
- WOLFSENTRY_CONTEXT_ARGS_OUT_EX(ctx_clone),
- (const unsigned char *)sequential_test_json,
- strlen(sequential_test_json),
- WOLFSENTRY_CONFIG_LOAD_FLAG_NO_FLUSH | WOLFSENTRY_CONFIG_LOAD_FLAG_JSON_DOM_MAINTAINDICTORDER,
- NULL,
- 0));
- }
-#endif
+/* Regression coverage for the centijson JSON_VALUE (DOM) public API. Uses a
+ * controllable test allocator so the error paths can be exercised through the
+ * public interface. */
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_exchange(WOLFSENTRY_CONTEXT_ARGS_OUT, ctx_clone));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone)));
+static size_t test_cjv_live = 0; /* count of live allocations */
+static size_t test_cjv_fail_big = 0; /* if nonzero, fail allocations >= this size */
+static int test_cjv_zero_null = 0; /* if set, zero-size requests return NULL */
+static long test_cjv_budget = -1; /* if >= 0, allow this many mallocs then fail */
+
+static void test_cjv_reset(void) {
+ test_cjv_live = 0;
+ test_cjv_fail_big = 0;
+ test_cjv_zero_null = 0;
+ test_cjv_budget = -1;
+}
+
+#ifdef WOLFSENTRY_THREADSAFE
+static void *test_cjv_malloc(void *context, struct wolfsentry_thread_context *thread, size_t size)
+#else
+static void *test_cjv_malloc(void *context, size_t size)
+#endif
+{
+ void *p;
+ (void)context;
+#ifdef WOLFSENTRY_THREADSAFE
+ (void)thread;
+#endif
+ if (test_cjv_zero_null && (size == 0))
+ return NULL;
+ if ((test_cjv_fail_big != 0) && (size >= test_cjv_fail_big))
+ return NULL;
+ if (test_cjv_budget >= 0) {
+ if (test_cjv_budget == 0)
+ return NULL;
+ --test_cjv_budget;
}
+ p = malloc(size);
+ if (p != NULL)
+ ++test_cjv_live;
+ return p;
+}
- {
- struct wolfsentry_context *ctx_clone;
+#ifdef WOLFSENTRY_THREADSAFE
+static void test_cjv_free(void *context, struct wolfsentry_thread_context *thread, void *ptr)
+#else
+static void test_cjv_free(void *context, void *ptr)
+#endif
+{
+ (void)context;
+#ifdef WOLFSENTRY_THREADSAFE
+ (void)thread;
+#endif
+ if (ptr != NULL)
+ --test_cjv_live;
+ free(ptr);
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_clone(WOLFSENTRY_CONTEXT_ARGS_OUT, &ctx_clone, WOLFSENTRY_CLONE_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_free(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&ctx_clone)));
+#ifdef WOLFSENTRY_THREADSAFE
+static void *test_cjv_realloc(void *context, struct wolfsentry_thread_context *thread, void *ptr, size_t size)
+#else
+static void *test_cjv_realloc(void *context, void *ptr, size_t size)
+#endif
+{
+ void *p;
+ int was_null;
+ (void)context;
+#ifdef WOLFSENTRY_THREADSAFE
+ (void)thread;
+#endif
+ if (test_cjv_zero_null && (size == 0)) {
+ if (ptr != NULL)
+ --test_cjv_live;
+ free(ptr);
+ return NULL;
+ }
+ if ((test_cjv_fail_big != 0) && (size >= test_cjv_fail_big))
+ return NULL;
+ was_null = (ptr == NULL);
+ p = realloc(ptr, size);
+ if (p != NULL) {
+ if (was_null)
+ ++test_cjv_live;
+ } else if ((size == 0) && (! was_null)) {
+ --test_cjv_live;
}
+ return p;
+}
- {
- struct wolfsentry_cursor *cursor;
- struct wolfsentry_route *route;
- struct wolfsentry_route_exports route_exports;
- struct wolfsentry_route_table *main_routes;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor));
- for (ret = wolfsentry_route_table_iterate_current(main_routes, cursor, &route);
- ret >= 0;
- ret = wolfsentry_route_table_iterate_next(main_routes, cursor, &route)) {
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, route, &route_exports));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_exports_render(WOLFSENTRY_CONTEXT_ARGS_OUT, &route_exports, stdout));
- putc('\n', stdout);
- }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+static int test_cjvalue_path(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE root;
+ JSON_VALUE *list;
+ JSON_VALUE *elem;
+ const JSON_VALUE *found;
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &root) < 0);
+
+ list = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &root, (const unsigned char *)"list", 4);
+ WOLFSENTRY_EXIT_ON_TRUE(list == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), list) < 0);
+
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), list);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, 10) < 0);
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), list);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, 20) < 0);
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), list);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, 30) < 0);
+
+ /* Attached array index (key followed immediately by a bracket). */
+ found = json_value_path(&root, "list[0]");
+ WOLFSENTRY_EXIT_ON_TRUE(found == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(found) == 10);
+
+ found = json_value_path(&root, "list[2]");
+ WOLFSENTRY_EXIT_ON_TRUE(found == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(found) == 30);
+
+ /* Slash-separated index is a documented equivalent of the attached form. */
+ found = json_value_path(&root, "list/[1]");
+ WOLFSENTRY_EXIT_ON_TRUE(found == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(found) == 20);
+
+ /* Negative indexes are relative to the array length. */
+ found = json_value_path(&root, "list[-1]");
+ WOLFSENTRY_EXIT_ON_TRUE(found == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(found) == 30);
+
+ found = json_value_path(&root, "list[-3]");
+ WOLFSENTRY_EXIT_ON_TRUE(found == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(found) == 10);
+
+ /* Out-of-range indexes resolve to NULL. */
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&root, "list[3]") == NULL);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&root, "list[-4]") == NULL);
+
+ /* Trailing bytes after a bracket index must be rejected. */
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&root, "list/[0]garbage]") == NULL);
+
+ /* An index that overflows size_t must be rejected, not wrapped. */
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&root, "list/[18446744073709551616]") == NULL);
+
+ /* An empty path returns the root. */
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_path(&root, "") == &root);
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &root) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
+
+ return 0;
+}
+
+static int test_cjvalue_array_remove_range(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE arr;
+ JSON_VALUE *elem;
+ int i;
+ int ret;
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr) < 0);
+ for (i = 0; i < 3; ++i) {
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, i) < 0);
}
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_array_size(&arr) == 3);
-#ifndef WOLFSENTRY_NO_STDIO
- {
- struct wolfsentry_kv_pair_internal *kv_ref;
- struct wolfsentry_cursor *cursor;
- const struct wolfsentry_kv_pair *kv_exports;
- const char *val_type;
- char val_buf[2048];
- int val_buf_space;
- wolfsentry_hitcount_t n_seen = 0;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
- for (ret = wolfsentry_user_values_iterate_current(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref);
- ret >= 0;
- ret = wolfsentry_user_values_iterate_next(WOLFSENTRY_CONTEXT_ARGS_OUT, cursor, &kv_ref)) {
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_kv_pair_export(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_ref, &kv_exports));
- val_buf_space = sizeof val_buf;
- if (wolfsentry_kv_type_to_string(WOLFSENTRY_KV_TYPE(kv_exports), &val_type) < 0)
- val_type = "?";
- if (wolfsentry_kv_render_value(WOLFSENTRY_CONTEXT_ARGS_OUT, kv_exports, val_buf, &val_buf_space) < 0) {
- if (WOLFSENTRY_KV_TYPE(kv_exports) == WOLFSENTRY_KV_BYTES)
- (void)snprintf(val_buf, sizeof val_buf, "<%.*s>", (int)WOLFSENTRY_KV_V_BYTES_LEN(kv_exports), WOLFSENTRY_KV_V_BYTES(kv_exports));
- else
- strcpy(val_buf,"?");
- }
- switch (WOLFSENTRY_KV_TYPE(kv_exports)) {
- case WOLFSENTRY_KV_TRUE:
- case WOLFSENTRY_KV_FALSE:
- case WOLFSENTRY_KV_NULL:
- printf("{ \"%.*s\" : %s }\n",
- (int)WOLFSENTRY_KV_KEY_LEN(kv_exports),
- WOLFSENTRY_KV_KEY(kv_exports),
- val_buf);
- break;
- default:
- printf("{ \"%.*s\" : { \"%s\" : %s } }\n",
- (int)WOLFSENTRY_KV_KEY_LEN(kv_exports),
- WOLFSENTRY_KV_KEY(kv_exports),
- val_type,
- val_buf);
- }
- ++n_seen;
- }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_user_values_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, &cursor));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FALSE(n_seen == wolfsentry->user_values->header.n_ents);
+ /* index + count must not wrap past the array size. */
+ ret = json_value_array_remove_range(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr, 3, SIZE_MAX);
+ WOLFSENTRY_EXIT_ON_FALSE(ret == -1);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_array_size(&arr) == 3);
+
+ /* a legitimate removal still works. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_array_remove_range(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr, 1, 1) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_array_size(&arr) == 2);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(json_value_array_get(&arr, 1)) == 2);
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
+ return 0;
+}
+
+static int test_cjvalue_clone_array_partial(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE src;
+ JSON_VALUE clone;
+ JSON_VALUE *elem;
+ JSON_VALUE *inner;
+ unsigned char small_str[50];
+ unsigned char big_str[256];
+ int i;
+ int ret;
+
+ memset(small_str, 'a', sizeof small_str);
+ memset(big_str, 'b', sizeof big_str);
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src) < 0);
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_string_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, small_str, sizeof small_str) < 0);
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_string_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, big_str, sizeof big_str) < 0);
+
+ json_value_init_null(&clone);
+
+ /* Force the clone of the second (large) element to fail partway. */
+ test_cjv_fail_big = 200;
+ ret = json_value_clone(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src, &clone);
+ test_cjv_fail_big = 0;
+ WOLFSENTRY_EXIT_ON_FALSE(ret < 0);
+
+ /* The failed clone must finalize the already-cloned prefix, leaking nothing. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
+
+ /* Also exercise failure on a partially-built nested aggregate element: a
+ * nested array whose element buffer allocation fails after its payload was
+ * allocated must not leak that payload. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src) < 0);
+ inner = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src);
+ WOLFSENTRY_EXIT_ON_TRUE(inner == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), inner) < 0);
+ for (i = 0; i < 16; ++i) {
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), inner);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, i) < 0);
+ inner = json_value_array_get(&src, 0);
+ WOLFSENTRY_EXIT_ON_TRUE(inner == NULL);
}
-#endif
- {
- struct {
- struct wolfsentry_sockaddr sa;
- byte addr_buf[4];
- } remote, local;
- wolfsentry_route_flags_t inexact_matches;
- wolfsentry_action_res_t action_results;
+ json_value_init_null(&clone);
+ /* The clone of the 16-slot inner array needs a 256-byte element buffer,
+ * which fails after the inner array payload has been allocated. */
+ test_cjv_fail_big = 200;
+ ret = json_value_clone(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src, &clone);
+ test_cjv_fail_big = 0;
+ WOLFSENTRY_EXIT_ON_FALSE(ret < 0);
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
+
+ return 0;
+}
+
+static int test_cjvalue_clone_empty_array(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE src;
+ JSON_VALUE clone;
+ int ret;
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src) < 0);
+ json_value_init_null(&clone);
- remote.sa.sa_family = local.sa.sa_family = AF_INET;
- remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
- remote.sa.sa_port = 12345;
- local.sa.sa_port = 443;
- remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
- remote.sa.interface = local.sa.interface = 1;
- memcpy(remote.sa.addr,"\177\0\0\1",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\177\0\0\1",sizeof local.addr_buf);
+ /* With an allocator that returns NULL for zero-size requests, cloning an
+ * empty array must still succeed (it needs no element storage). */
+ test_cjv_zero_null = 1;
+ ret = json_value_clone(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src, &clone);
+ test_cjv_zero_null = 0;
+ WOLFSENTRY_EXIT_ON_FALSE(ret >= 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_type(&clone) == JSON_VALUE_ARRAY);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_array_size(&clone) == 0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &clone) < 0);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &src) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_ACCEPT));
- }
+ return 0;
+}
- {
- struct {
- struct wolfsentry_sockaddr sa;
- byte addr_buf[4];
- } remote, local;
- wolfsentry_route_flags_t inexact_matches;
- wolfsentry_action_res_t action_results;
+static int test_cjvalue_compatible_lossy(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE v;
+
+ /* A finite double outside the float range is not float-compatible. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_double(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, 1e308) < 0);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_FLOAT));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* A small double is float-compatible. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_double(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, 2.0) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_is_compatible(&v, JSON_VALUE_FLOAT));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* 2^24 + 1 is not exactly representable in a float. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int64(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, (int64_t)16777217) < 0);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_FLOAT));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* 2^24 + 2 is exactly representable in a float and must stay compatible. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int64(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, (int64_t)16777218) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_is_compatible(&v, JSON_VALUE_FLOAT));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* A small integer is float-compatible. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, (int32_t)1000) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_is_compatible(&v, JSON_VALUE_FLOAT));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* 2^53 + 1 is not exactly representable in a double. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_uint64(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, (((uint64_t)1 << 53) + 1)) < 0);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_DOUBLE));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* 2^53 + 2 is exactly representable in a double and must stay compatible. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_uint64(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, (((uint64_t)1 << 53) + 2)) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_is_compatible(&v, JSON_VALUE_DOUBLE));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* A small integer is double-compatible. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, (int32_t)5) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_is_compatible(&v, JSON_VALUE_DOUBLE));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
- remote.sa.sa_family = local.sa.sa_family = WOLFSENTRY_AF_CHAOS;
- remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
- remote.sa.sa_port = 12345;
- local.sa.sa_port = 443;
- remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
- remote.sa.interface = local.sa.interface = 1;
- memcpy(remote.sa.addr,"\177\0\0\1",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\177\0\0\1",sizeof local.addr_buf);
+ return 0;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+static int test_cjvalue_compatible_float_to_int(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE v;
+
+ /* Out-of-range float/double values must not be treated as integer-compatible,
+ * and the predicate must not perform an out-of-range floating-to-integer cast. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_float(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, 1e30F) < 0);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_INT32));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_INT64));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_UINT64));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_double(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, 1e300) < 0);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_INT32));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_INT64));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_UINT64));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* An in-range integral float is integer-compatible. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_float(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, 42.0F) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_is_compatible(&v, JSON_VALUE_INT32));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
+
+ /* A non-integral double is not integer-compatible. */
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_double(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v, 1.5) < 0);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_is_compatible(&v, JSON_VALUE_INT32));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &v) < 0);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_FALLTHROUGH));
+ return 0;
+}
+
+static int test_cjvalue_dict_remove_orphan(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE outer;
+ JSON_VALUE *nested;
+ JSON_VALUE *slot;
+ char keybuf[8];
+ int i;
+ int ret;
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer) < 0);
+
+ nested = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)"a", 1);
+ WOLFSENTRY_EXIT_ON_TRUE(nested == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), nested) < 0);
+ for (i = 0; i < 4; ++i) {
+ (void)snprintf(keybuf, sizeof keybuf, "k%03d", i);
+ slot = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), nested, (const unsigned char *)keybuf, strlen(keybuf));
+ WOLFSENTRY_EXIT_ON_TRUE(slot == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), slot, (int32_t)i) < 0);
}
- {
- struct {
- struct wolfsentry_sockaddr sa;
- byte addr_buf[4];
- } remote, local;
- wolfsentry_route_flags_t inexact_matches;
- wolfsentry_action_res_t action_results;
+ slot = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)"b", 1);
+ WOLFSENTRY_EXIT_ON_TRUE(slot == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), slot, (int32_t)1) < 0);
- remote.sa.sa_family = local.sa.sa_family = AF_INET;
- remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
- remote.sa.sa_port = 0;
- local.sa.sa_port = 13579;
- remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
- remote.sa.interface = local.sa.interface = 0;
- memcpy(remote.sa.addr,"\1\2\3\4",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\0\0\0\0",sizeof local.addr_buf);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_dict_size(&outer) == 2);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+ /* Removing a nested dictionary needs no allocation, so it succeeds and frees the subtree with none available. */
+ test_cjv_budget = 0;
+ ret = json_value_dict_remove_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)"a", 1);
+ test_cjv_budget = -1;
+ WOLFSENTRY_EXIT_ON_FALSE(ret == 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_dict_size(&outer) == 1);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT));
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_PORT_RESET));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
+
+ return 0;
+}
+
+static int test_cjvalue_dict_clean_retry_later_node(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE outer;
+ JSON_VALUE *nested;
+ JSON_VALUE *slot;
+ char keybuf[8];
+ int i;
+ int ret;
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer) < 0);
+ for (i = 0; i < 3; ++i) {
+ (void)snprintf(keybuf, sizeof keybuf, "k%03d", i);
+ slot = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)keybuf, strlen(keybuf));
+ WOLFSENTRY_EXIT_ON_TRUE(slot == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), slot, (int32_t)i) < 0);
}
+ nested = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)"zz", 2);
+ WOLFSENTRY_EXIT_ON_TRUE(nested == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), nested) < 0);
+ slot = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), nested, (const unsigned char *)"in", 2);
+ WOLFSENTRY_EXIT_ON_TRUE(slot == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), slot, 7) < 0);
+
+ /* cleanup allocates nothing, so the earlier keys and the nested dictionary are all freed with no budget */
+ test_cjv_budget = 0;
+ ret = json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer);
+ test_cjv_budget = -1;
+ WOLFSENTRY_EXIT_ON_FALSE(ret == 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
-#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
-/* test bitmap-based matching of CAN addresses. */
- {
- struct {
- struct wolfsentry_sockaddr sa;
- byte addr_buf[4];
- } remote, local;
- wolfsentry_route_flags_t inexact_matches;
- wolfsentry_action_res_t action_results;
+ return 0;
+}
- remote.sa.sa_family = local.sa.sa_family = WOLFSENTRY_AF_CAN;
- remote.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;;
- local.sa.addr_len = 0;
- remote.sa.interface = local.sa.interface = 0;
- memcpy(remote.sa.addr,"\x1f\xff\xff\xed",sizeof remote.addr_buf);
+static int test_cjvalue_dict_clean_retry(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE outer;
+ JSON_VALUE *nested;
+ JSON_VALUE *slot;
+ char keybuf[8];
+ int i;
+ int ret;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
- WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer) < 0);
+ nested = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)"a", 1);
+ WOLFSENTRY_EXIT_ON_TRUE(nested == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), nested) < 0);
+ for (i = 0; i < 4; ++i) {
+ (void)snprintf(keybuf, sizeof keybuf, "k%03d", i);
+ slot = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), nested, (const unsigned char *)keybuf, strlen(keybuf));
+ WOLFSENTRY_EXIT_ON_TRUE(slot == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), slot, (int32_t)i) < 0);
+ }
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER0));
+ /* Cleanup allocates nothing, so it must succeed and free every node even
+ * with no allocation budget left. */
+ test_cjv_budget = 0;
+ ret = json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer);
+ test_cjv_budget = -1;
+ WOLFSENTRY_EXIT_ON_FALSE(ret == 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
- memcpy(remote.sa.addr,"\x1f\xff\xff\xec",sizeof remote.addr_buf);
+ return 0;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
- WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+static int test_cjvalue_nested_cleanup_no_leak(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE outer;
+ JSON_VALUE clone;
+ JSON_VALUE *nested;
+ JSON_VALUE *arr;
+ JSON_VALUE *slot;
+ JSON_VALUE *member;
+ char keybuf[8];
+ size_t live_before;
+ long budget;
+ int i;
+ int ret;
- WOLFSENTRY_EXIT_ON_TRUE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER0));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer) < 0);
+ nested = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)"a", 1);
+ WOLFSENTRY_EXIT_ON_TRUE(nested == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), nested) < 0);
+ arr = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)"b", 1);
+ WOLFSENTRY_EXIT_ON_TRUE(arr == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), arr) < 0);
+ slot = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), arr);
+ WOLFSENTRY_EXIT_ON_TRUE(slot == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), slot) < 0);
+ for (i = 0; i < 4; ++i) {
+ (void)snprintf(keybuf, sizeof keybuf, "k%03d", i);
+ member = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), nested, (const unsigned char *)keybuf, strlen(keybuf));
+ WOLFSENTRY_EXIT_ON_TRUE(member == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), member, (int32_t)i) < 0);
+ member = json_value_dict_add_(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), slot, (const unsigned char *)keybuf, strlen(keybuf));
+ WOLFSENTRY_EXIT_ON_TRUE(member == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), member, (int32_t)i) < 0);
+ }
- memcpy(remote.sa.addr,"\x00\x00\x07\xcb",sizeof remote.addr_buf);
+ /* Cloning the array must release everything it built at every possible allocation failure. */
+ live_before = test_cjv_live;
+ for (budget = 0; ; ++budget) {
+ json_value_init_null(&clone);
+ test_cjv_budget = budget;
+ ret = json_value_clone(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), arr, &clone);
+ test_cjv_budget = -1;
+ if (ret == 0) {
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &clone) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == live_before);
+ break;
+ }
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == live_before);
+ WOLFSENTRY_EXIT_ON_TRUE(budget > 64);
+ }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
- WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+ /* Removing a nested dictionary must free it even with no allocation budget. */
+ test_cjv_budget = 0;
+ ret = json_value_dict_remove(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer, (const unsigned char *)"a");
+ test_cjv_budget = -1;
+ WOLFSENTRY_EXIT_ON_FALSE(ret == 0);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER1));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &outer) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
- memcpy(remote.sa.addr,"\x1f\xff\xff\xff",sizeof remote.addr_buf);
+ return 0;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
- WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+static int test_cjvalue_empty_dict(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE d;
+ JSON_VALUE clone;
+ int ret;
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER2));
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &d) < 0);
+ json_value_init_null(&clone);
- memcpy(remote.sa.addr,"\x1f\xff\x01\x23",sizeof remote.addr_buf);
+ /* Cloning an empty dictionary must not require a zero-size allocation. */
+ test_cjv_zero_null = 1;
+ ret = json_value_clone(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &d, &clone);
+ test_cjv_zero_null = 0;
+ WOLFSENTRY_EXIT_ON_FALSE(ret >= 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_type(&clone) == JSON_VALUE_DICT);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &clone) < 0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
- WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+ /* Cleaning up an empty dictionary must not require a zero-size allocation. */
+ test_cjv_zero_null = 1;
+ ret = json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &d);
+ test_cjv_zero_null = 0;
+ WOLFSENTRY_EXIT_ON_FALSE(ret >= 0);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER3));
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
- /* one prefix-matched CAN address to try */
- memcpy(remote.sa.addr,"\x15\x67\x01\x22",sizeof remote.addr_buf);
+ return 0;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
- WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_ADDR_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_REMOTE_INTERFACE_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_LOCAL_INTERFACE_WILDCARD,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches, &action_results));
+static int test_cjvalue_array_shrink_to_zero(WOLFSENTRY_CONTEXT_ARGS_IN_EX(struct wolfsentry_allocator *allocator))
+{
+ JSON_VALUE arr;
+ JSON_VALUE *elem;
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr) < 0);
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, (int32_t)7) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_array_size(&arr) == 1);
+
+ /* Model an allocator for which realloc(ptr, 0) frees and returns NULL.
+ * Removing the last element shrinks the backing store to zero capacity; the
+ * array must stay usable (no dangling buffer) and finalize cleanly. */
+ test_cjv_zero_null = 1;
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_array_remove(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr, 0) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_array_size(&arr) == 0);
+
+ elem = json_value_array_append(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr);
+ WOLFSENTRY_EXIT_ON_TRUE(elem == NULL);
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_init_int32(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), elem, (int32_t)9) < 0);
+ WOLFSENTRY_EXIT_ON_FALSE(json_value_int32(json_value_array_get(&arr, 0)) == 9);
+
+ WOLFSENTRY_EXIT_ON_TRUE(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(allocator), &arr) < 0);
+ test_cjv_zero_null = 0;
+ WOLFSENTRY_EXIT_ON_FALSE(test_cjv_live == 0);
+
+ return 0;
+}
+
+static wolfsentry_errcode_t test_cjvalue_fixes(void) {
+ struct wolfsentry_allocator test_allocator;
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ memset(&test_allocator, 0, sizeof test_allocator);
+ test_allocator.malloc = test_cjv_malloc;
+ test_allocator.free = test_cjv_free;
+ test_allocator.realloc = test_cjv_realloc;
+
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_path(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
+
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_array_remove_range(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
+
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_clone_array_partial(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
- WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(action_results, WOLFSENTRY_ACTION_RES_USER4));
- }
-#endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_clone_empty_array(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
-#ifdef WOLFSENTRY_HAVE_JSON_DOM
- {
- unsigned char *test_json_document = NULL;
- int fd = -1;
- JSON_VALUE p_root;
- JSON_VALUE *v1 = NULL, *v2 = NULL, *v3 = NULL;
- struct stat st;
- static const JSON_CONFIG centijson_config = {
- 65536, /* max_total_len */
- 1000, /* max_total_values */
- 20, /* max_number_len */
- WOLFSENTRY_KV_MAX_VALUE_BYTES, /* max_string_len */
- WOLFSENTRY_MAX_LABEL_BYTES, /* max_key_len */
- 10, /* max_nesting_level */
- JSON_NOSCALARROOT /* flags */
- };
- JSON_INPUT_POS json_pos;
- const unsigned char *s;
- size_t alen, i;
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_compatible_lossy(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
- WOLFSENTRY_EXIT_ON_SYSFAILURE(fd = open(fname, O_RDONLY));
- WOLFSENTRY_EXIT_ON_SYSFAILURE(fstat(fd, &st));
- WOLFSENTRY_EXIT_ON_SYSFALSE((test_json_document = (unsigned char *)malloc((size_t)st.st_size)) != NULL);
- WOLFSENTRY_EXIT_ON_SYSFALSE(read(fd, test_json_document, (size_t)st.st_size) == st.st_size);
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_compatible_float_to_int(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
- if ((ret = json_dom_parse(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), test_json_document, (size_t)st.st_size, ¢ijson_config,
- 0 /* dom_flags */, &p_root, &json_pos)) < 0) {
- const void *p = memchr((const char *)(test_json_document + json_pos.offset), '\n', (size_t)st.st_size - json_pos.offset);
- int linelen = p ? ((int)((unsigned char *)p - (test_json_document + json_pos.offset)) + (int)json_pos.column_number - 1) :
- (((int)st.st_size - (int)json_pos.offset) + (int)json_pos.column_number - 1);
- if (WOLFSENTRY_ERROR_DECODE_SOURCE_ID(ret) == WOLFSENTRY_SOURCE_ID_UNSET)
- (void)fprintf(stderr, "json_dom_parse failed at offset " SIZET_FMT ", L%u, col %u, with centijson code %d: %s\n", json_pos.offset,json_pos.line_number, json_pos.column_number, ret, json_dom_error_str(ret));
- else
- (void)fprintf(stderr, "json_dom_parse failed at offset " SIZET_FMT ", L%u, col %u, with " WOLFSENTRY_ERROR_FMT "\n", json_pos.offset,json_pos.line_number, json_pos.column_number, WOLFSENTRY_ERROR_FMT_ARGS(ret));
- (void)fprintf(stderr,"%.*s\n", linelen, test_json_document + json_pos.offset - json_pos.column_number + 1);
- exit(1);
- }
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_dict_remove_orphan(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
- WOLFSENTRY_EXIT_ON_TRUE((v1 = json_value_path(&p_root, "wolfsentry-config-version")) == NULL);
- WOLFSENTRY_EXIT_ON_FALSE(json_value_uint32(v1) == 1U);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v1)));
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_dict_clean_retry(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
- WOLFSENTRY_EXIT_ON_TRUE((v1 = json_value_path(&p_root, "default-policies")) == NULL);
- WOLFSENTRY_EXIT_ON_TRUE((v2 = json_value_path(v1, "default-policy")) == NULL);
- WOLFSENTRY_EXIT_ON_TRUE((s = json_value_string(v2)) == NULL);
- WOLFSENTRY_EXIT_ON_FALSE(strcmp((const char *)s, "reject") == 0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v2)));
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_nested_cleanup_no_leak(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
- WOLFSENTRY_EXIT_ON_TRUE((v2 = json_value_path(v1, "default-event")) == NULL);
- WOLFSENTRY_EXIT_ON_TRUE((s = json_value_string(v2)) == NULL);
- WOLFSENTRY_EXIT_ON_FALSE(strcmp((const char *)s, "static-route-parent") == 0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v2)));
- v2 = NULL;
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_dict_clean_retry_later_node(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
- WOLFSENTRY_EXIT_ON_TRUE((v1 = json_value_path(&p_root, "static-routes-insert")) == NULL);
- WOLFSENTRY_EXIT_ON_TRUE((alen = json_value_array_size(v1)) == 0);
- for (i = 0; i < alen; ++i) {
- WOLFSENTRY_EXIT_ON_TRUE((v2 = json_value_array_get(v1, i)) == NULL);
- v3 = json_value_path(v2, "family");
- if (v3) {
- WOLFSENTRY_EXIT_ON_TRUE((json_value_string(v3) == NULL) && (json_value_int32(v3) <= 0));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v3)));
- v3 = NULL;
- }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v2)));
- v2 = NULL;
- }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v1)));
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_empty_dict(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
- WOLFSENTRY_EXIT_ON_TRUE((v1 = json_value_path(&p_root, "user-values/user-null")) == NULL);
- WOLFSENTRY_EXIT_ON_FALSE(json_value_type(v1) == JSON_VALUE_NULL);
+ test_cjv_reset();
+ WOLFSENTRY_EXIT_ON_TRUE(test_cjvalue_array_shrink_to_zero(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&test_allocator)) != 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_RETURN_OK;
+}
- if (v3)
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v3)));
- if (v2)
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v2)));
- if (v1)
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), v1)));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_centijson_errcode_translate(json_value_fini(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)), &p_root)));
- if (test_json_document != NULL)
- free(test_json_document);
- if (fd != -1)
- (void)close(fd);
- }
#endif /* WOLFSENTRY_HAVE_JSON_DOM */
+static wolfsentry_errcode_t test_json_oneshot_string(
+ const char *json,
+ wolfsentry_config_load_flags_t load_flags)
+{
+ wolfsentry_errcode_t ret, load_ret;
+ struct wolfsentry_context *wolfsentry;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- /* test aux event, flag bit filters & frobbing, and good/bad route insertion by
- * wolfsentry_builtin_action_track_peer()
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ load_ret = wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (const unsigned char *)json,
+ strlen(json),
+ load_flags,
+ NULL,
+ 0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ ret = WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ WOLFSENTRY_ERROR_RERETURN(load_ret);
+}
+
+static int test_json_uint64_negative(void) {
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"v\" : { \"uint\" : -1 } } }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+#ifndef WOLFSENTRY_NO_GETPROTOBY
+static int test_json_unknown_service_name(void) {
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"protocol\" : 6, \"remote\" : { \"port\" : \"no-such-service-zzz\" } } ] }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+
+static int test_json_unknown_protocol_name(void) {
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"protocol\" : \"no-such-protocol-zzz\" } ] }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+#endif /* !WOLFSENTRY_NO_GETPROTOBY */
+
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+static int test_json_bitmask_short_address(void) {
+ /* prefix-bits before a bitmask would right-justify only the prefix-sized
+ * leading bytes of a full-width address, so the combination is refused,
+ * as prefix-bits after a bitmask already is; address then bitmask loads.
*/
- {
- struct {
- struct wolfsentry_sockaddr sa;
- byte addr_buf[4];
- } remote, local;
- wolfsentry_route_flags_t inexact_matches;
- wolfsentry_action_res_t action_results;
- struct wolfsentry_route *ephemeral_route;
- wolfsentry_route_flags_t ephemeral_route_flags;
- int i;
+ static const char json_prefixed[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"prefix-bits\" : 16, \"address\" : \"1.2.0.0\", \"bitmask\" : \"255.255.0.0\" } } ] }";
+ static const char json_plain[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"address\" : \"1.2.0.0\", \"bitmask\" : \"255.255.0.0\" } } ] }";
+ WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(
+ CONFIG_MISPLACED_KEY,
+ test_json_oneshot_string(json_prefixed, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ test_json_oneshot_string(json_plain, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
+#endif /* WOLFSENTRY_ADDR_BITMASK_MATCHING */
- /* first, trigger insertion of DNS dynamic pinhole. */
+static int test_json_af_local_address(void) {
+ /* family 1 == WOLFSENTRY_AF_LOCAL; the loader must parse the path address
+ * that the formatter emits, keeping dump/reload symmetric.
+ */
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/tmp/sock\" } } ] }";
+ /* the formatter dumps AF_LOCAL routes with an explicit prefix-bits; that
+ * dumped form must reload, which requires the loader to accept prefix-bits
+ * for AF_LOCAL ("/tmp/sock" is 9 bytes == 72 bits).
+ */
+ static const char json_dump_form[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/tmp/sock\", \"prefix-bits\" : 72 } } ] }";
+ /* a path containing a JSON-special byte would dump unescaped and corrupt
+ * the output, so it must be rejected at load.
+ */
+ static const char json_unsafe[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/tmp/a\\\"b\" } } ] }";
+ /* a prefix-bits inconsistent with the pathname length (here 2 bytes == 16
+ * bits) would leave implicit trailing bytes, so it must be rejected in
+ * either key order; a non-byte-aligned prefix must also be rejected.
+ */
+ static const char json_overlong_prefix[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/x\", \"prefix-bits\" : 128 } } ] }";
+ static const char json_prefix_first[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"prefix-bits\" : 128, \"address\" : \"/x\" } } ] }";
+ static const char json_unaligned_prefix[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/x\", \"prefix-bits\" : 12 } } ] }";
+ /* an empty path or a zero prefix would make a zero-length, match-all
+ * address with the wildcard flag cleared; reject both in either order.
+ */
+ static const char json_empty_path[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"\" } } ] }";
+ static const char json_empty_path_then_prefix[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"\", \"prefix-bits\" : 128 } } ] }";
+ static const char json_prefix_then_empty_path[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"prefix-bits\" : 128, \"address\" : \"\" } } ] }";
+ static const char json_zero_prefix_first[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"prefix-bits\" : 0, \"address\" : \"/x\" } } ] }";
+ static const char json_zero_prefix_last[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 1, \"remote\" : { \"address\" : \"/x\", \"prefix-bits\" : 0 } } ] }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_empty_path, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_empty_path_then_prefix, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_prefix_then_empty_path, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_zero_prefix_first, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_zero_prefix_last, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ test_json_oneshot_string(json_dump_form, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_unsafe, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_overlong_prefix, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_prefix_first, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json_unaligned_prefix, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
- remote.sa.sa_family = local.sa.sa_family = AF_INET;
- remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_UDP;
- remote.sa.sa_port = 53;
- local.sa.sa_port = 65432;
- remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
- remote.sa.interface = local.sa.interface = 0;
- memcpy(remote.sa.addr,"\1\2\3\4",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\0\0\0\0",sizeof local.addr_buf);
+static int test_json_failed_load_fails_closed(void) {
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_context *wolfsentry;
+ wolfsentry_action_res_t default_policy = WOLFSENTRY_ACTION_RES_NONE;
+ static const char good[] =
+ "{ \"wolfsentry-config-version\" : 1, \"default-policies\" : { \"default-policy\" : \"accept\" } }";
+ static const char bad[] =
+ "{ \"wolfsentry-config-version\" : 1, \"user-values\" : { \"x\" : { \"uint\" : -1 } } }";
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- action_results = WOLFSENTRY_ACTION_RES_USER_BASE | (WOLFSENTRY_ACTION_RES_USER_BASE << 1U) | (WOLFSENTRY_ACTION_RES_USER_BASE << 3U);
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (const unsigned char *)good, strlen(good),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, NULL, 0));
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_INSERTED | WOLFSENTRY_ACTION_RES_USER_BASE | (WOLFSENTRY_ACTION_RES_USER_BASE << 1U) | (WOLFSENTRY_ACTION_RES_USER_BASE << 3U)));
+ /* a failing default-mode load must leave the context fail-closed. */
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (const unsigned char *)bad, strlen(bad),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, NULL, 0));
- /* next, make sure the dynamic pinhole is live, by checking for an exact match. */
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_default_policy_get(WOLFSENTRY_CONTEXT_ARGS_OUT, &default_policy));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FALSE(default_policy == WOLFSENTRY_ACTION_RES_REJECT);
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ ret = WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_USER5));
- WOLFSENTRY_EXIT_ON_FALSE(inexact_matches == WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD);
+static int test_json_actions_update_rejected(void) {
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"actions-update\" : [ ] }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
- /* make sure the pinhole route has the expected flags on it. */
+static int test_json_prefix_bits_overbound(void) {
+ /* prefix-bits beyond the family width must be rejected before the value can
+ * drive an out-of-bounds copy from the fixed staging buffer.
+ */
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"address\" : \"1.2.3.4\", \"prefix-bits\" : 65535 } } ] }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(ephemeral_route, &ephemeral_route_flags));
- /* make sure it's really ephemeral. */
- WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route->meta.purge_after != 0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route_flags ==
- (WOLFSENTRY_ROUTE_FLAG_PARENT_EVENT_WILDCARD |
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN |
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_OUT |
- WOLFSENTRY_ROUTE_FLAG_IN_TABLE |
- WOLFSENTRY_ROUTE_FLAG_INSERT_ACTIONS_CALLED |
- WOLFSENTRY_ROUTE_FLAG_GREENLISTED));
+static int test_json_link64_short_address(void) {
+ /* family 119 == WOLFSENTRY_AF_LINK64; a six-byte EUI-48 address must not be
+ * silently accepted as LINK64.
+ */
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 119, \"remote\" : { \"address\" : \"00:11:22:33:44:55\" }, \"local\" : { \"address\" : \"00:11:22:33:44:55\" } } ] }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
- /* now trigger insertion of a tracking rule to catch a simulated port scan. */
+static int test_json_duration_negative(void) {
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"config-update\" : { \"penalty-box-duration\" : \"-100s\" } }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
- remote.sa.sa_family = local.sa.sa_family = AF_INET;
- remote.sa.sa_proto = local.sa.sa_proto = IPPROTO_TCP;
- remote.sa.sa_port = 1234;
- local.sa.sa_port = 65432;
- remote.sa.addr_len = local.sa.addr_len = sizeof remote.addr_buf * BITS_PER_BYTE;
- remote.sa.interface = local.sa.interface = 0;
- memcpy(remote.sa.addr,"\1\2\3\4",sizeof remote.addr_buf);
- memcpy(local.sa.addr,"\0\0\0\0",sizeof local.addr_buf);
+static int test_json_uint8_hex_interface(void) {
+ static const char json[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"remote\" : { \"interface\" : \"0xff\" } } ] }";
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
- action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
+static int test_json_uint16_bounds(void) {
+ static const char too_big[] =
+ "{ \"wolfsentry-config-version\" : 1, \"events\" : [ { \"label\" : \"e\", \"priority\" : 70000 } ] }";
+ static const char at_max[] =
+ "{ \"wolfsentry-config-version\" : 1, \"events\" : [ { \"label\" : \"e\", \"priority\" : 65535 } ] }";
+ WOLFSENTRY_EXIT_ON_SUCCESS(
+ test_json_oneshot_string(too_big, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ test_json_oneshot_string(at_max, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+static int test_json_load_one_route_flags(
+ const char *json,
+ wolfsentry_route_flags_t *route_flags)
+{
+ wolfsentry_errcode_t ret;
+ struct wolfsentry_context *wolfsentry;
+ struct wolfsentry_route_table *main_routes;
+ struct wolfsentry_cursor *cursor;
+ struct wolfsentry_route *route;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_FALLTHROUGH | WOLFSENTRY_ACTION_RES_INSERTED | WOLFSENTRY_ACTION_RES_UNREACHABLE | (WOLFSENTRY_ACTION_RES_USER_BASE << 4U)));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
- /* iteratively increment the derog-thresh-for-penalty-boxing. */
- for (i=0; i<3; ++i) {
- action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (const unsigned char *)json, strlen(json),
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE, NULL, 0));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_main_table(WOLFSENTRY_CONTEXT_ARGS_OUT, &main_routes));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_start(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_current(main_routes, cursor, &route));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_get_flags(route, route_flags));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_table_iterate_end(WOLFSENTRY_CONTEXT_ARGS_OUT, main_routes, &cursor));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_FALLTHROUGH | WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_USER5));
- }
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ ret = WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE);
+ WOLFSENTRY_RERETURN_IF_ERROR(ret);
+ WOLFSENTRY_RETURN_OK;
+}
- /* trigger the penalty boxing. */
- action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
+static int test_json_tcplike_explicit_flag(void) {
+ /* family 2 == INET, protocol 6 == TCP; the flag key precedes "protocol",
+ * as in the route dump, so an explicit setting must survive the protocol
+ * clause's implicit default.
+ */
+ static const char explicit_false[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"tcplike-port-numbers\" : false, \"protocol\" : 6 } ] }";
+ static const char explicit_true[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"tcplike-port-numbers\" : true, \"protocol\" : 6 } ] }";
+ static const char omitted[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : 2, \"protocol\" : 6 } ] }";
+ wolfsentry_route_flags_t route_flags = WOLFSENTRY_ROUTE_FLAG_NONE;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_load_one_route_flags(explicit_false, &route_flags), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(! WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS));
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_UPDATE | WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_USER5));
+ route_flags = WOLFSENTRY_ROUTE_FLAG_NONE;
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_load_one_route_flags(explicit_true, &route_flags), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS));
- /* confirm the penalty boxing. */
- action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
+ route_flags = WOLFSENTRY_ROUTE_FLAG_NONE;
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_load_one_route_flags(omitted, &route_flags), OK));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_CHECK_BITS(route_flags, WOLFSENTRY_ROUTE_FLAG_TCPLIKE_PORT_NUMBERS));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_USER5));
+static int test_json_link_prefix_bits_key_order(void) {
+ static const char fmt[] =
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : %u,"
+ " \"remote\" : { %s }, \"local\" : { \"address\" : \"%s\" } } ] }";
+ static const char eui64[] = "00:11:22:33:44:55:66:77";
+ static const char eui48[] = "00:11:22:33:44:55";
+ char json[512];
+ int json_len;
+
+ /* generic LINK resolves to EUI-48 or EUI-64 from the address, so a
+ * prefix-bits key must be judged the same before or after the address. */
+ json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"prefix-bits\" : 64, \"address\" : \"00:11:22:33:44:55:66:77\"", eui64);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), OK));
+
+ json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"address\" : \"00:11:22:33:44:55:66:77\", \"prefix-bits\" : 64", eui64);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), OK));
+
+ json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"prefix-bits\" : 64, \"address\" : \"00:11:22:33:44:55\"", eui48);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), NUMERIC_ARG_TOO_BIG));
+
+ json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"address\" : \"00:11:22:33:44:55\", \"prefix-bits\" : 64", eui48);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), NUMERIC_ARG_TOO_BIG));
+
+ json_len = snprintf(json, sizeof json, fmt, (unsigned int)WOLFSENTRY_AF_LINK, "\"prefix-bits\" : 48, \"address\" : \"00:11:22:33:44:55\"", eui48);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
+ WOLFSENTRY_EXIT_ON_FALSE(WOLFSENTRY_ERROR_CODE_IS(test_json_oneshot_string(json, WOLFSENTRY_CONFIG_LOAD_FLAG_NONE), OK));
+ WOLFSENTRY_RETURN_OK;
+}
-#ifdef WOLFSENTRY_UNITTEST_BENCHMARKS
- {
- struct timespec start_at, end_at;
- uint64_t start_at_cycles, end_at_cycles;
- double ns_per_call, cycles_per_call;
+static int af_local_custom_parser_calls;
- test_action_enabled = 0;
+static wolfsentry_errcode_t af_local_custom_parser(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const char *addr_text,
+ int addr_text_len,
+ byte *addr_internal,
+ wolfsentry_addr_bits_t *addr_internal_bits)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)addr_text;
+ (void)addr_text_len;
+ ++af_local_custom_parser_calls;
+ addr_internal[0] = 0x5a;
+ *addr_internal_bits = 8;
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_SYSFAILURE(clock_gettime(CLOCK_MONOTONIC, &start_at));
- start_at_cycles = get_intel_cycles();
- for (i=0; i<1000000; ++i) {
- action_results = WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_DEROGATORY;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
- }
- end_at_cycles = get_intel_cycles();
- WOLFSENTRY_EXIT_ON_SYSFAILURE(clock_gettime(CLOCK_MONOTONIC, &end_at));
- ns_per_call = ((double)(end_at.tv_sec - start_at.tv_sec) * 1000000000.0 + (double)(end_at.tv_nsec - start_at.tv_nsec)) / (double)i;
- cycles_per_call = (double)(end_at_cycles - start_at_cycles) / (double)i;
- printf("benchmark wolfsentry_route_event_dispatch_with_inited_result() with JSON-loaded route table, matching penalty-boxed route: %.2f ns/call %.2f cycles/call\n", ns_per_call, cycles_per_call);
+static wolfsentry_errcode_t af_local_custom_formatter(
+ WOLFSENTRY_CONTEXT_ARGS_IN,
+ const byte *addr_internal,
+ unsigned int addr_internal_bits,
+ char *addr_text,
+ int *addr_text_len)
+{
+ WOLFSENTRY_CONTEXT_ARGS_NOT_USED;
+ (void)addr_internal;
+ (void)addr_internal_bits;
+ if (*addr_text_len < 1)
+ WOLFSENTRY_ERROR_RETURN(BUFFER_TOO_SMALL);
+ addr_text[0] = 'z';
+ *addr_text_len = 1;
+ WOLFSENTRY_RETURN_OK;
+}
-#ifdef WOLFSENTRY_MAX_CYCLES_PER_CALL_JSON_LOADED
- if (cycles_per_call > (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_JSON_LOADED) {
- (void)fprintf(stderr, "benchmark wolfsentry_route_event_dispatch_with_inited_result() with JSON-loaded route table, matching penalty-boxed route: measured %.2f cycles/call exceeds max %.2f\n", cycles_per_call, (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_JSON_LOADED);
- WOLFSENTRY_EXIT_ON_TRUE(cycles_per_call > (double)WOLFSENTRY_MAX_CYCLES_PER_CALL_JSON_LOADED);
- }
+static int test_json_af_local_registered_handler(void) {
+ struct wolfsentry_context *wolfsentry;
+ char json[256];
+ int json_len;
+#ifdef WOLFSENTRY_HAVE_DESIGNATED_INITIALIZERS
+ struct wolfsentry_eventconfig config = { .route_private_data_size = PRIVATE_DATA_SIZE, .route_private_data_alignment = PRIVATE_DATA_ALIGNMENT };
+#else
+ struct wolfsentry_eventconfig config = { PRIVATE_DATA_SIZE, PRIVATE_DATA_ALIGNMENT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
#endif
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_DEROGATORY | WOLFSENTRY_ACTION_RES_UNREACHABLE | WOLFSENTRY_ACTION_RES_USER5));
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
- test_action_enabled = 1;
- }
-#endif /* WOLFSENTRY_UNITTEST_BENCHMARKS */
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ &config,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_addr_family_handler_install(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ WOLFSENTRY_AF_LOCAL,
+ "test_local",
+ -1,
+ af_local_custom_parser,
+ af_local_custom_formatter,
+ 8));
- /* force release from penalty box with explicit _RES_COMMENDABLE.
- * note that the result will still be _REJECT because of the fallthrough policy in test-config.json.
- * in practice, port scanning defenses are only needed on configurations that default open, in which
- * case release from the penalty box would restore fallthrough to _ACCEPT.
- * on the other hand, where port scanning is being tracked purely for purposes of IDS-type visibility,
- * fallthrough to _REJECT is perfectly appropriate.
- */
- action_results = WOLFSENTRY_ACTION_RES_COMMENDABLE;
+ json_len = snprintf(
+ json, sizeof json,
+ "{ \"wolfsentry-config-version\" : 1, \"routes\" : [ { \"direction-in\" : true, \"family\" : %u, \"remote\" : { \"address\" : \"anything\" } } ] }",
+ (unsigned int)WOLFSENTRY_AF_LOCAL);
+ WOLFSENTRY_EXIT_ON_TRUE((json_len < 0) || ((size_t)json_len >= sizeof json));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+ af_local_custom_parser_calls = 0;
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_config_json_oneshot(
+ WOLFSENTRY_CONTEXT_ARGS_OUT,
+ (const unsigned char *)json,
+ (size_t)json_len,
+ WOLFSENTRY_CONFIG_LOAD_FLAG_NONE,
+ NULL,
+ 0));
+ WOLFSENTRY_EXIT_ON_FALSE(af_local_custom_parser_calls == 1);
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_COMMENDABLE | WOLFSENTRY_ACTION_RES_UPDATE | WOLFSENTRY_ACTION_RES_FALLTHROUGH | WOLFSENTRY_ACTION_RES_USER5));
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
- /* recheck to confirm no update on a second match with zero initial action_results. */
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
- WOLFSENTRY_EXIT_ON_FALSE(action_results == (WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_FALLTHROUGH | WOLFSENTRY_ACTION_RES_USER5));
+ WOLFSENTRY_RETURN_OK;
+}
- {
- struct wolfsentry_route_exports ephemeral_route_exports;
- wolfsentry_ent_id_t reinsert_id;
- wolfsentry_time_t purge_after_after_deletion;
+struct sax_dump_capture {
+ char text[128];
+ size_t len;
+};
- /* increment connection_count. */
- action_results = WOLFSENTRY_ACTION_RES_CONNECT;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+static int sax_dump_capture_cb(const unsigned char *str, size_t size, void *user_data) {
+ struct sax_dump_capture *cap = (struct sax_dump_capture *)user_data;
+ if (size >= sizeof cap->text - cap->len)
+ return -1;
+ memcpy(cap->text + cap->len, str, size);
+ cap->len += size;
+ cap->text[cap->len] = '\0';
+ return 0;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, ephemeral_route, &ephemeral_route_exports));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+static int sax_noop_process_cb(JSON_TYPE type, const unsigned char *data, size_t data_size, void *user_data) {
+ (void)type;
+ (void)data;
+ (void)data_size;
+ (void)user_data;
+ return 0;
+}
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(
- DEFERRED,
- wolfsentry_route_delete_by_id(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- NULL /* caller_arg */,
- id,
- NULL /* trigger_label */,
- 0 /* trigger_label_len */,
- &action_results));
+static int sax_dump_double_text(double dbl, struct sax_dump_capture *cap) {
+ cap->len = 0;
+ cap->text[0] = '\0';
+ return json_dump_double(dbl, sax_dump_capture_cb, cap);
+}
+
+static wolfsentry_errcode_t test_json_sax_double_roundtrip(void) {
+ static const double corpus[] = {
+ 1.0 + 2.220446049250313e-16,
+ 0.1 + 0.2,
+ 0.1,
+ 1.0 / 3.0,
+ -2.0 / 3.0,
+ 1.7976931348623157e308,
+ 2.2250738585072014e-308,
+ 4.9406564584124654e-324,
+ 1e-300,
+ 123456789.12345679
+ };
+ struct sax_dump_capture cap;
+ size_t i;
+ double reparsed;
+
+ for (i = 0; i < sizeof corpus / sizeof corpus[0]; ++i) {
+ WOLFSENTRY_EXIT_ON_FAILURE(sax_dump_double_text(corpus[i], &cap));
+ WOLFSENTRY_EXIT_ON_FAILURE(json_number_to_double((const unsigned char *)cap.text, cap.len, &reparsed));
+ if (memcmp(&reparsed, &corpus[i], sizeof reparsed) != 0) {
+ (void)fprintf(stderr, "double did not survive dump and reparse: %s\n", cap.text);
+ return 1;
+ }
+ }
+
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, ephemeral_route, &ephemeral_route_exports));
- purge_after_after_deletion = ephemeral_route_exports.meta.purge_after;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+static wolfsentry_errcode_t test_json_sax_nonfinite_dump(void) {
+ double inf;
+ double reparsed;
+ struct sax_dump_capture cap;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_reset_metadata_exports(&ephemeral_route_exports));
+ WOLFSENTRY_EXIT_ON_FAILURE(json_number_to_double((const unsigned char *)"1e999", 5, &inf));
+ WOLFSENTRY_EXIT_ON_FALSE(inf > 1.7976931348623157e308);
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_SUCCESS(
- ALREADY_OK,
- wolfsentry_route_insert_by_exports(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- NULL /* caller_arg */,
- &ephemeral_route_exports,
- &reinsert_id,
- &action_results));
+ WOLFSENTRY_EXIT_ON_FAILURE(sax_dump_double_text(inf, &cap));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(cap.text, "null") == 0);
- WOLFSENTRY_EXIT_ON_FALSE(reinsert_id == id);
+ WOLFSENTRY_EXIT_ON_FAILURE(sax_dump_double_text(-inf, &cap));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(cap.text, "null") == 0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, ephemeral_route, &ephemeral_route_exports));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_FAILURE(json_number_to_double((const unsigned char *)"nan", 3, &reparsed));
+ WOLFSENTRY_EXIT_ON_FAILURE(sax_dump_double_text(reparsed, &cap));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(cap.text, "null") == 0);
- WOLFSENTRY_EXIT_ON_TRUE(ephemeral_route_exports.meta.purge_after == purge_after_after_deletion);
- WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route_exports.meta.connection_count == 1);
+ WOLFSENTRY_EXIT_ON_FAILURE(sax_dump_double_text(1.0, &cap));
+ WOLFSENTRY_EXIT_ON_FALSE(strcmp(cap.text, "1.0") == 0);
+ WOLFSENTRY_EXIT_ON_FAILURE(json_number_to_double((const unsigned char *)cap.text, cap.len, &reparsed));
+ WOLFSENTRY_EXIT_ON_FALSE(reparsed == 1.0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_purge_time_set(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- ephemeral_route,
- 0));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_RETURN_OK;
+}
- /* decrement connection_count. */
- action_results = WOLFSENTRY_ACTION_RES_DISCONNECT;
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_event_dispatch_with_inited_result(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- &remote.sa,
- &local.sa,
- WOLFSENTRY_ROUTE_FLAG_DIRECTION_IN,
- "call-in-from-unit-test",
- WOLFSENTRY_LENGTH_NULL_TERMINATED,
- (void *)0x12345678 /* caller_arg */,
- &id,
- &inexact_matches,
- &action_results));
+static wolfsentry_errcode_t test_json_sax_number_text_boundary(void) {
+ unsigned char num[65];
+ double result;
+ size_t len;
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_export(WOLFSENTRY_CONTEXT_ARGS_OUT, ephemeral_route, &ephemeral_route_exports));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ memset(num, '0', sizeof num);
+ num[0] = '1';
- WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route_exports.meta.purge_after == 0);
- WOLFSENTRY_EXIT_ON_FALSE(ephemeral_route_exports.meta.connection_count == 0);
+ for (len = 62; len <= 63; ++len) {
+ result = 0.0;
+ WOLFSENTRY_EXIT_ON_FAILURE(json_number_to_double(num, len, &result));
+ /* the cast drops x87 excess precision from the constants */
+ WOLFSENTRY_EXIT_ON_FALSE(result == (double)((len == 62) ? 1e61 : 1e62));
+ }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_mutex(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_ON_FAILURE(
- wolfsentry_route_purge_time_set(
- WOLFSENTRY_CONTEXT_ARGS_OUT,
- ephemeral_route,
- 1));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+ WOLFSENTRY_EXIT_ON_TRUE(json_number_to_double(num, 64, &result) >= 0);
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_route_stale_purge(WOLFSENTRY_CONTEXT_ARGS_OUT, NULL, NULL));
+ WOLFSENTRY_RETURN_OK;
+}
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_lock_shared(WOLFSENTRY_CONTEXT_ARGS_OUT));
- WOLFSENTRY_EXIT_UNLESS_EXPECTED_FAILURE(ITEM_NOT_FOUND, wolfsentry_table_ent_get_by_id(WOLFSENTRY_CONTEXT_ARGS_OUT, id, (struct wolfsentry_table_ent_header **)&ephemeral_route));
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_context_unlock(WOLFSENTRY_CONTEXT_ARGS_OUT));
+static wolfsentry_errcode_t test_json_sax_truncated_input(void) {
+ static const JSON_CALLBACKS callbacks = { sax_noop_process_cb };
+ static const JSON_CONFIG sax_config = {
+ 4096, /* max_total_len */
+ 1000, /* max_total_values */
+ 20, /* max_number_len */
+ 256, /* max_string_len */
+ 64, /* max_key_len */
+ 10, /* max_nesting_level */
+ 0 /* flags */
+ };
+ static const char * const truncated[] = {
+ "t", "tr", "tru",
+ "f", "fa", "fal", "fals",
+ "n", "nu", "nul",
+ "[t", "[fals", "[nul",
+ "{\"a\":t", "{\"a\":tru", "{\"a\":nu",
+ "[true", "[false", "[null", "[1", "{\"a\":true", "{\"a\":1", "{\"a\":[null"
+ };
+ static const char * const complete[] = {
+ "true", "false", "null", "[true]", "[null, false]", "{\"a\":true}"
+ };
+ struct wolfsentry_context *wolfsentry;
+ size_t i;
+ JSON_INPUT_POS pos;
+ int ret;
+
+ WOLFSENTRY_THREAD_HEADER_CHECKED(WOLFSENTRY_THREAD_FLAG_NONE);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(
+ wolfsentry_init_ex(
+ wolfsentry_build_settings,
+ WOLFSENTRY_CONTEXT_ARGS_OUT_EX(WOLFSENTRY_TEST_HPI),
+ NULL /* config */,
+ &wolfsentry,
+ WOLFSENTRY_INIT_FLAG_NONE));
+
+ for (i = 0; i < sizeof truncated / sizeof truncated[0]; ++i) {
+ ret = json_parse(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)),
+ (const unsigned char *)truncated[i], strlen(truncated[i]),
+ &callbacks, &sax_config, NULL, &pos);
+ if (ret >= 0) {
+ (void)fprintf(stderr, "truncated JSON accepted: %s\n", truncated[i]);
+ return 1;
+ }
+ }
+
+ for (i = 0; i < sizeof complete / sizeof complete[0]; ++i) {
+ ret = json_parse(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(wolfsentry_get_allocator(wolfsentry)),
+ (const unsigned char *)complete[i], strlen(complete[i]),
+ &callbacks, &sax_config, NULL, &pos);
+ if (ret < 0) {
+ (void)fprintf(stderr, "complete JSON rejected: %s\n", complete[i]);
+ return 1;
}
}
@@ -5654,99 +14763,418 @@ static int test_json_corpus(void) {
scenario = MAP_FAILED;
}
- if ((ret < 0) && (! ignore_failed_parse)) {
- ret = wolfsentry_centijson_errcode_translate(ret);
- ret = WOLFSENTRY_ERROR_RECODE(ret);
- break;
- }
- }
+ if ((ret < 0) && (! ignore_failed_parse)) {
+ ret = wolfsentry_centijson_errcode_translate(ret);
+ ret = WOLFSENTRY_ERROR_RECODE(ret);
+ break;
+ }
+ }
+
+ WOLFSENTRY_EXIT_ON_SYSFAILURE(closedir(corpus_dir));
+ } while (0);
+
+ WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+
+ WOLFSENTRY_ERROR_RERETURN(ret);
+}
+
+#endif /* TEST_JSON_CORPUS */
+
+#ifdef FREERTOS
+
+int main(int argc, char* argv[]) {
+ int ret = 0;
+ (void)argc;
+ (void)argv;
+
+#ifdef TEST_LWIP
+ #ifdef TEST_JSON_CONFIG_PATH
+ ret = test_lwip(TEST_JSON_CONFIG_PATH);
+ #else
+ ret = test_lwip(NULL);
+ #endif
+#endif
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+#else /* !FREERTOS */
+
+int main(int argc, char* argv[]) {
+ wolfsentry_errcode_t ret = 0; /* cppcheck-suppress unreadVariable
+ */
+ int err = 0;
+ (void)argc;
+ (void)argv;
+
+#ifdef WOLFSENTRY_ERROR_STRINGS
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_REGISTER_SOURCE());
+ WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_REGISTER_ERROR(UNIT_TEST_FAILURE, UNIT_TEST_FAILURE_MSG));
+#endif
+
+#ifdef TEST_INIT
+ ret = test_init();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_init failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_base64_decode_lenient_padding();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_base64_decode_lenient_padding failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_base64_decode_quantum();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_base64_decode_quantum failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_eventconfig_overlap_bits();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_eventconfig_overlap_bits failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_eventconfig_alignment();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_eventconfig_alignment failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_eventconfig_derogatory_threshold();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_eventconfig_derogatory_threshold failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#ifdef WOLFSENTRY_ERROR_STRINGS
+ ret = test_user_string_table_bounds();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_user_string_table_bounds failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+#endif
+
+#ifdef TEST_LWIP
+ #ifdef TEST_JSON_CONFIG_PATH
+ ret = test_lwip(TEST_JSON_CONFIG_PATH);
+ #else
+ ret = test_lwip(NULL);
+ #endif
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_lwip failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+
+#ifdef TEST_RWLOCKS
+ ret = test_rw_locks();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_rw_locks failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_rw_locks_auto_downgrade();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_rw_locks_auto_downgrade failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_rw_locks_reader_wakeup();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_rw_locks_reader_wakeup failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_object_release_tableless();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_object_release_tableless failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_context_clone_readonly_thread();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_context_clone_readonly_thread failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_context_clone_fallthrough();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_context_clone_fallthrough failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_init_interval_conversion_failure();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_init_interval_conversion_failure failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_context_exchange_default_policy();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_context_exchange_default_policy failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+
+#ifdef TEST_STATIC_ROUTES
+ ret = test_static_routes();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_static_routes failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_route_lookup_af_wildcard_span_terminates();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_lookup_af_wildcard_span_terminates failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_route_lookup_exact_match_honors_exclude_reject();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_lookup_exact_match_honors_exclude_reject failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_route_lookup_action_res_filter_bits_unset();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_lookup_action_res_filter_bits_unset failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_route_lookup_full_scan_honors_exclude_reject();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_lookup_full_scan_honors_exclude_reject failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_route_insert_action_failure_detaches_route();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_insert_action_failure_detaches_route failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+ ret = test_route_insert_action_failure_releases_bitmask_af();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_insert_action_failure_releases_bitmask_af failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+ ret = test_route_insert_with_actions_marks_parent_event();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_insert_with_actions_marks_parent_event failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_route_insert_rejects_expired_purge_after();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_insert_rejects_expired_purge_after failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#if !defined(WOLFSENTRY_NO_JSON) || defined(WOLFSENTRY_JSON_DUMP_UTILS)
+ ret = test_route_format_json_truncation_is_bounded();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_format_json_truncation_is_bounded failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_route_format_json_escapes_parent_event_label();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_format_json_escapes_parent_event_label failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_route_format_json_unknown_protocol_numeric();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_route_format_json_unknown_protocol_numeric failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+#endif
+
+#ifdef TEST_STATIC_ROUTES
+ ret = test_static_routes_clone_addr_family_links();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_static_routes_clone_addr_family_links failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_static_routes_id_wrap();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_static_routes_id_wrap failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_static_routes_id_reclaim();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_static_routes_id_reclaim failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+
+#ifdef TEST_STATIC_ROUTES
+ ret = test_static_routes_guards();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_static_routes_guards failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
- WOLFSENTRY_EXIT_ON_SYSFAILURE(closedir(corpus_dir));
- } while (0);
+#ifdef TEST_DYNAMIC_RULES
+ ret = test_dynamic_rules();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dynamic_rules failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_inhibited_insert_actions();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_inhibited_insert_actions failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_handler_deletes_own_event();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_handler_deletes_own_event failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_handler_error();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_handler_error failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_context_exchange_during_dispatch();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_context_exchange_during_dispatch failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_reentrant_action_delete();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_reentrant_action_delete failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_action_drop_reference_clears_results();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_action_drop_reference_clears_results failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_inhibit_actions_flag();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_inhibit_actions_flag failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_action_stop();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_action_stop failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_disabled_action();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_disabled_action failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_dont_count_hits();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_dont_count_hits failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dispatch_insert_delete_already_guard();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dispatch_insert_delete_already_guard failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_dynamic_rules_regressions();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_dynamic_rules_regressions failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_track_peer_ephemeral_route_bound();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_track_peer_ephemeral_route_bound failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_track_peer_connect_count();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_track_peer_connect_count failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
- WOLFSENTRY_EXIT_ON_FAILURE(wolfsentry_shutdown(WOLFSENTRY_CONTEXT_ARGS_OUT_EX(&wolfsentry)));
+ ret = test_event_max_length_label();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_max_length_label failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
- WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_THREAD_TAILER(WOLFSENTRY_THREAD_FLAG_NONE));
+ ret = test_event_max_length_label_lookups();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_max_length_label_lookups failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
- WOLFSENTRY_ERROR_RERETURN(ret);
-}
+ ret = test_event_insert_failure_leaves_id_untouched();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_insert_failure_leaves_id_untouched failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
-#endif /* TEST_JSON_CORPUS */
+ ret = test_event_update_config_null();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_update_config_null failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
-#ifdef FREERTOS
+ ret = test_event_update_config_layout_with_routes();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_update_config_layout_with_routes failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
-int main(int argc, char* argv[]) {
- int ret = 0;
- (void)argc;
- (void)argv;
+ ret = test_event_aux_event_self_reference();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_aux_event_self_reference failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
-#ifdef TEST_LWIP
- #ifdef TEST_JSON_CONFIG_PATH
- ret = test_lwip(TEST_JSON_CONFIG_PATH);
- #else
- ret = test_lwip(NULL);
- #endif
-#endif
- if (ret < 0)
- return ret;
- else
- return 0;
-}
+ ret = test_event_aux_event_reuse_after_clear();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_aux_event_reuse_after_clear failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
-#else /* !FREERTOS */
+ ret = test_event_aux_mark_cleared_on_owner_free();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_aux_mark_cleared_on_owner_free failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
-int main(int argc, char* argv[]) {
- wolfsentry_errcode_t ret = 0; /* cppcheck-suppress unreadVariable
- */
- int err = 0;
- (void)argc;
- (void)argv;
+ ret = test_event_aux_event_rejects_subevent_owner();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_aux_event_rejects_subevent_owner failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
-#ifdef WOLFSENTRY_ERROR_STRINGS
- WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_REGISTER_SOURCE());
- WOLFSENTRY_EXIT_ON_FAILURE(WOLFSENTRY_REGISTER_ERROR(UNIT_TEST_FAILURE, UNIT_TEST_FAILURE_MSG));
-#endif
+ ret = test_event_aux_event_rejects_parent_event();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_aux_event_rejects_parent_event failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_event_former_aux_becomes_route_parent();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_event_former_aux_becomes_route_parent failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
-#ifdef TEST_INIT
- ret = test_init();
+#ifndef WOLFSENTRY_NO_JSON
+ ret = test_event_default_priority();
if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
- (void)fprintf(stderr, "test_init failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ (void)fprintf(stderr, "test_event_default_priority failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
err = 1;
}
-#endif
-#ifdef TEST_LWIP
- #ifdef TEST_JSON_CONFIG_PATH
- ret = test_lwip(TEST_JSON_CONFIG_PATH);
- #else
- ret = test_lwip(NULL);
- #endif
+ ret = test_json_event_max_length_label();
if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
- (void)fprintf(stderr, "test_lwip failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ (void)fprintf(stderr, "test_json_event_max_length_label failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
err = 1;
}
#endif
-#ifdef TEST_RWLOCKS
- ret = test_rw_locks();
+ ret = test_action_insert_failure_leaves_id_untouched();
if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
- (void)fprintf(stderr, "test_rw_locks failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ (void)fprintf(stderr, "test_action_insert_failure_leaves_id_untouched failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
err = 1;
}
-#endif
-#ifdef TEST_STATIC_ROUTES
- ret = test_static_routes();
+ ret = test_event_aux_event_layout_pinned();
if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
- (void)fprintf(stderr, "test_static_routes failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ (void)fprintf(stderr, "test_event_aux_event_layout_pinned failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
err = 1;
}
-#endif
-#ifdef TEST_DYNAMIC_RULES
- ret = test_dynamic_rules();
+ ret = test_event_layout_guard_held_deleted_route();
if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
- (void)fprintf(stderr, "test_dynamic_rules failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ (void)fprintf(stderr, "test_event_layout_guard_held_deleted_route failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
err = 1;
}
#endif
@@ -5757,6 +15185,60 @@ int main(int argc, char* argv[]) {
(void)fprintf(stderr, "test_user_values failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
err = 1;
}
+ ret = test_kv_reference_lifetime();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_reference_lifetime failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_kv_get2_reference_balance();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_get2_reference_balance failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_kv_get_bad_key_errcode();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_get_bad_key_errcode failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_kv_render_large_int();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_render_large_int failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_kv_render_float_roundtrip();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_render_float_roundtrip failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#ifndef WOLFSENTRY_C89
+ ret = test_kv_render_nonfinite_float();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_render_nonfinite_float failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif /* !WOLFSENTRY_C89 */
+ ret = test_kv_replace_keeps_value_on_id_failure();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_replace_keeps_value_on_id_failure failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_kv_readonly_overwrite_rejected();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_readonly_overwrite_rejected failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_kv_empty_key_rejected();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_empty_key_rejected failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#ifndef WOLFSENTRY_HAVE_JSON_DOM
+ ret = test_kv_render_string_escape();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_kv_render_string_escape failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
#endif
#ifdef TEST_USER_ADDR_FAMILIES
@@ -5784,6 +15266,116 @@ int main(int argc, char* argv[]) {
(void)fprintf(stderr, "test_json failed for " TEST_NUMERIC_JSON_CONFIG_PATH ", " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
err = 1;
}
+#ifdef WOLFSENTRY_HAVE_JSON_DOM
+ ret = test_cjvalue_fixes();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_cjvalue_fixes failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+ ret = test_json_uint64_negative();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_uint64_negative failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_uint16_bounds();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_uint16_bounds failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_uint8_hex_interface();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_uint8_hex_interface failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_duration_negative();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_duration_negative failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#ifdef WOLFSENTRY_ADDR_BITMASK_MATCHING
+ ret = test_json_bitmask_short_address();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_bitmask_short_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+ ret = test_json_af_local_address();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_af_local_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_failed_load_fails_closed();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_failed_load_fails_closed failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_actions_update_rejected();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_actions_update_rejected failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_prefix_bits_overbound();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_prefix_bits_overbound failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_link64_short_address();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_link64_short_address failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#ifndef WOLFSENTRY_NO_GETPROTOBY
+ ret = test_json_unknown_service_name();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_unknown_service_name failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_unknown_protocol_name();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_unknown_protocol_name failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+#endif
+ ret = test_json_tcplike_explicit_flag();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_tcplike_explicit_flag failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_link_prefix_bits_key_order();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_link_prefix_bits_key_order failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+ ret = test_json_af_local_registered_handler();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_af_local_registered_handler failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+
+ ret = test_json_sax_double_roundtrip();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_sax_double_roundtrip failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+
+ ret = test_json_sax_nonfinite_dump();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_sax_nonfinite_dump failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+
+ ret = test_json_sax_number_text_boundary();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_sax_number_text_boundary failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
+
+ ret = test_json_sax_truncated_input();
+ if (! WOLFSENTRY_ERROR_CODE_IS(ret, OK)) {
+ (void)fprintf(stderr, "test_json_sax_truncated_input failed, " WOLFSENTRY_ERROR_FMT "\n", WOLFSENTRY_ERROR_FMT_ARGS(ret));
+ err = 1;
+ }
#endif
#ifdef TEST_JSON_CORPUS
diff --git a/wolfsentry/centijson_dom.h b/wolfsentry/centijson_dom.h
index a502b47b..f02c339b 100644
--- a/wolfsentry/centijson_dom.h
+++ b/wolfsentry/centijson_dom.h
@@ -152,7 +152,7 @@ WOLFSENTRY_API int json_dom_parse(
* callback.
*
* The provided writing function must write all the data provided to it
- * and return zero to indicate success, or non-zero to indicate an error
+ * and return zero to indicate success, or a negative value to indicate an error
* and abort the operation.
*
* Returns zero on success, JSON_ERR_OUTOFMEMORY, or an error the code returned
diff --git a/wolfsentry/centijson_sax.h b/wolfsentry/centijson_sax.h
index 828cf2ff..7e974e50 100644
--- a/wolfsentry/centijson_sax.h
+++ b/wolfsentry/centijson_sax.h
@@ -169,8 +169,8 @@ typedef struct JSON_CALLBACKS {
* corresponding values in the alternating fashion (i.e. in the order
* as they are in the JSON input.).
*
- * Application can abort the parsing operation by returning a non-zero.
- * Note the non-zero return value of the callback is propagated to
+ * Application can abort the parsing operation by returning a negative
+ * value. Note the negative return value of the callback is propagated to
* json_feed() and json_fini().
*/
int (*process)(JSON_TYPE /*type*/, const unsigned char* /*data*/,
@@ -337,7 +337,7 @@ typedef int (*JSON_DUMP_CALLBACK)(const unsigned char* /*str*/, size_t /*size*/,
* handles proper escaping of control characters.
*
* The provided writer callback must write all the data provided to it and
- * return zero to indicate success, or non-zero to indicate an error and abort
+ * return zero to indicate success, or a negative value to indicate an error and abort
* the operation.
*
* All these return zero on success, JSON_ERR_OUTOFMEMORY, or an error code
diff --git a/wolfsentry/centijson_value.h b/wolfsentry/centijson_value.h
index 03241ca4..d1928fd8 100644
--- a/wolfsentry/centijson_value.h
+++ b/wolfsentry/centijson_value.h
@@ -514,8 +514,9 @@ WOLFSENTRY_API int json_value_dict_remove(
/* Walking over all items in the dictionary. The callback function is called
* for every item in the dictionary, providing key and value and propagating
- * the user data into it. If the callback returns non-zero, the function
- * aborts immediately.
+ * the user data into it. If the callback returns a negative value, the
+ * function aborts immediately and returns that value; otherwise the walk
+ * continues.
*
* Note dict_walk_ordered() is supported only if DICT_MAINTAINORDER
* flag was used in init_dict().
diff --git a/wolfsentry/wolfsentry.h b/wolfsentry/wolfsentry.h
index 225980b6..dbb52436 100644
--- a/wolfsentry/wolfsentry.h
+++ b/wolfsentry/wolfsentry.h
@@ -61,15 +61,15 @@
#define WOLFSENTRY_VERSION WOLFSENTRY_VERSION_ENCODE(WOLFSENTRY_VERSION_MAJOR, WOLFSENTRY_VERSION_MINOR, WOLFSENTRY_VERSION_TINY)
/*!< \brief The version recorded in wolfsentry.h, encoded as an integer @hideinitializer */
#define WOLFSENTRY_VERSION_GT(major, minor, tiny) (WOLFSENTRY_VERSION > WOLFSENTRY_VERSION_ENCODE(major, minor, tiny))
- /*!< \brief Helper macro that is true if the given version is greater than that in wolfsentry.h. @hideinitializer */
+ /*!< \brief Helper macro that is true if the version in wolfsentry.h is greater than the given version. @hideinitializer */
#define WOLFSENTRY_VERSION_GE(major, minor, tiny) (WOLFSENTRY_VERSION >= WOLFSENTRY_VERSION_ENCODE(major, minor, tiny))
- /*!< \brief Helper macro that is true if the given version is greater than or equal to that in wolfsentry.h. @hideinitializer */
+ /*!< \brief Helper macro that is true if the version in wolfsentry.h is greater than or equal to the given version. @hideinitializer */
#define WOLFSENTRY_VERSION_EQ(major, minor, tiny) (WOLFSENTRY_VERSION == WOLFSENTRY_VERSION_ENCODE(major, minor, tiny))
- /*!< \brief Helper macro that is true if the given version equals that in wolfsentry.h. @hideinitializer */
+ /*!< \brief Helper macro that is true if the version in wolfsentry.h equals the given version. @hideinitializer */
#define WOLFSENTRY_VERSION_LT(major, minor, tiny) (WOLFSENTRY_VERSION < WOLFSENTRY_VERSION_ENCODE(major, minor, tiny))
- /*!< \brief Helper macro that is true if the given version is less than that in wolfsentry.h. @hideinitializer */
+ /*!< \brief Helper macro that is true if the version in wolfsentry.h is less than the given version. @hideinitializer */
#define WOLFSENTRY_VERSION_LE(major, minor, tiny) (WOLFSENTRY_VERSION <= WOLFSENTRY_VERSION_ENCODE(major, minor, tiny))
- /*!< \brief Helper macro that is true if the given version is less than or equal to that in wolfsentry.h. @hideinitializer */
+ /*!< \brief Helper macro that is true if the version in wolfsentry.h is less than or equal to the given version. @hideinitializer */
/*! \brief flags to pass to wolfsentry_init_ex(), to be `OR`d together. */
typedef enum {
@@ -198,7 +198,7 @@ struct wolfsentry_timecbs {
*/
typedef int (*sem_init_cb_t)(sem_t *sem, int pshared, unsigned int value);
- /*!< Pointer to function with arguments and semantics of POSIX `sem_init()`. Currently, `pshared` and `value` are always zero as called by wolfSentry, so implementations can ignore them. */
+ /*!< Pointer to function with arguments and semantics of POSIX `sem_init()`. Currently, `value` is always zero as called by wolfSentry. `pshared` is zero unless the lock is initialized with `WOLFSENTRY_LOCK_FLAG_PSHARED`, in which case it is nonzero. */
typedef int (*sem_post_cb_t)(sem_t *sem);
/*!< Pointer to function with arguments and semantics of POSIX `sem_post()` */
typedef int (*sem_wait_cb_t)(sem_t *sem);
@@ -709,7 +709,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_lock_shared2mutex_is_reserved(str
\param lock a pointer to the lock
\param thread pointer to the `wolfsentry_thread_context`
- \param flags optional `wolfsentry_lock_flags_t`
+ \param flags non-NULL pointer to a `wolfsentry_lock_flags_t` that receives the current flags
\return WOLFSENTRY_IS_SUCCESS(ret) is true on success.
@@ -1003,7 +1003,7 @@ typedef wolfsentry_errcode_t (*wolfsentry_action_callback_t)(
* @{
*/
-#define WOLFSENTRY_ROUTE_DEFAULT_POLICY_MASK (WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_STOP | WOLFSENTRY_ACTION_RES_ERROR)
+#define WOLFSENTRY_ROUTE_DEFAULT_POLICY_MASK (WOLFSENTRY_ACTION_RES_ACCEPT | WOLFSENTRY_ACTION_RES_REJECT | WOLFSENTRY_ACTION_RES_STOP | WOLFSENTRY_ACTION_RES_ERROR | WOLFSENTRY_ACTION_RES_PORT_RESET)
/*!< \brief Bit mask spanning the bits allowed by wolfsentry_route_table_default_policy_set() */
/*! \brief bit field specifying attributes of a route/rule */
@@ -1578,7 +1578,7 @@ typedef enum {
\brief Clones a wolfsentry context
\param clone the destination wolfsentry context, should be a pointer to a NULL pointer as this function will malloc
- \param flags set to WOLFSENTRY_CLONE_FLAG_AT_CREATION to use the config at the creation of the original wolfsentry context instead of the current configuration
+ \param flags set to WOLFSENTRY_CLONE_FLAG_AS_AT_CREATION to use the config at the creation of the original wolfsentry context instead of the current configuration
\return WOLFSENTRY_IS_SUCCESS(ret) is true on success.
@@ -1852,7 +1852,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_delete_from_table(
\param flags flags for the route
\param trigger_label a label for the trigger event (or null)
\param trigger_label_len the length of the trigger_label parameter
- \param action_results a pointer to results of the insert action -- all bits are cleared on entry.
+ \param action_results a pointer to results of the delete action -- all bits are cleared on entry.
\param n_deleted a counter for the number of entries deleted
\return WOLFSENTRY_IS_SUCCESS(ret) is true on success.
@@ -1877,7 +1877,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_delete(
\param id the object ID, as returned by wolfsentry_route_insert() or wolfsentry_get_object_id()
\param trigger_label a label for a trigger event (or null)
\param trigger_label_len the length of the trigger_label parameter
- \param action_results a pointer to results of the insert action -- all bits are cleared on entry.
+ \param action_results a pointer to results of the delete action -- all bits are cleared on entry.
\return WOLFSENTRY_IS_SUCCESS(ret) is true on success.
@@ -2192,7 +2192,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_with_table(
/*!< \brief Variant of wolfsentry_route_event_dispatch() that accepts an explicit \p route_table. */
/*!
- \brief Submit an event into wolfsentry and pass it through the filters. The action_results are cleared on entry, and can be checked to see what actions wolfsentry took, and what actions the caller should take (most saliently, #WOLFSENTRY_ACTION_RES_ACCEPT or #WOLFSENTRY_ACTION_RES_REJECT). \p action_results can be filtered with constructs like `WOLFSENTRY_MASKIN_BITS(action_results, WOLFSENTRY_ACTION_RES_REJECT)`
+ \brief Submit an event into wolfsentry and pass it through the filters. The action_results are cleared on entry, and can be checked to see what actions wolfsentry took, and what actions the caller should take (most saliently, #WOLFSENTRY_ACTION_RES_ACCEPT or #WOLFSENTRY_ACTION_RES_REJECT). \p action_results can be filtered with constructs like `WOLFSENTRY_MASKIN_BITS(*action_results, WOLFSENTRY_ACTION_RES_REJECT)`
\param remote the remote sockaddr details
\param local the local sockaddr details
@@ -2275,7 +2275,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_by_route(
void *caller_arg, /* passed to action callback(s) as the caller_arg. */
wolfsentry_action_res_t *action_results
);
- /*!< \brief Variant of wolfsentry_route_event_dispatch() that preselects the matched route by ID, mainly for use by application code that tracks route/session relationships. */
+ /*!< \brief Variant of wolfsentry_route_event_dispatch() that preselects the matched route with the supplied route pointer, mainly for use by application code that tracks route/session relationships. */
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_by_route_with_inited_result(
WOLFSENTRY_CONTEXT_ARGS_IN,
@@ -2285,7 +2285,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_event_dispatch_by_route_wit
void *caller_arg, /* passed to action callback(s) as the caller_arg. */
wolfsentry_action_res_t *action_results
);
- /*!< \brief Variant of wolfsentry_route_event_dispatch() that preselects the matched route by ID, and doesn't clear \p action_results on entry, mainly for use by application code that tracks route/session relationships. */
+ /*!< \brief Variant of wolfsentry_route_event_dispatch() that preselects the matched route with the supplied route pointer, and doesn't clear \p action_results on entry, mainly for use by application code that tracks route/session relationships. */
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_table_max_purgeable_routes_get(
WOLFSENTRY_CONTEXT_ARGS_IN,
@@ -2416,7 +2416,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_route_get_flags(
\brief Gets the metadata for a route
\param route the route to get the metadata for
- \param metadata a pointer to a pointer to receive the metadata
+ \param metadata a pointer to a caller-provided `struct wolfsentry_route_metadata_exports` to populate
\return WOLFSENTRY_IS_SUCCESS(ret) is true on success.
*/
@@ -2801,7 +2801,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_get_config(
\param label_len the length of the label
\param config the updated configuration
- \return WOLFSENTRY_IS_SUCCESS(ret) is true on success.
+ \return WOLFSENTRY_IS_SUCCESS(ret) is true on success. Once any route has used the event as its parent, or while the event is another event's aux event or is otherwise referenced, changes to `route_private_data_size` or `route_private_data_alignment` are rejected with `INCOMPATIBLE_STATE`, even after that route is deleted, because a deleted route can still be held by a checked-out reference.
\sa WOLFSENTRY_CONTEXT_ARGS_IN
*/
@@ -2921,6 +2921,11 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_action_insert_after(
\return WOLFSENTRY_IS_SUCCESS(ret) is true on success.
+ An action handler invoked during event dispatch must not call this from
+ within the dispatch; it returns `BUSY` while any dispatch
+ is in progress. Perform action-list deletions outside dispatch;
+ append/prepend/insert remain permitted.
+
\sa WOLFSENTRY_CONTEXT_ARGS_IN
*/
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_event_action_delete(
@@ -3075,7 +3080,7 @@ struct wolfsentry_kv_pair {
#define WOLFSENTRY_KV_V_UINT(kv) ((kv)->a.v_uint)
/*!< \brief Evaluates to the `uint64_t` value of a `wolfsentry_kv_pair` of type `WOLFSENTRY_KV_UINT`. @hideinitializer */
#define WOLFSENTRY_KV_V_SINT(kv) ((kv)->a.v_sint)
- /*!< \brief Evaluates to the `int64_t` value of a `wolfsentry_kv_pair` of type `WOLFSENTRY_KV_INT`. @hideinitializer */
+ /*!< \brief Evaluates to the `int64_t` value of a `wolfsentry_kv_pair` of type `WOLFSENTRY_KV_SINT`. @hideinitializer */
#define WOLFSENTRY_KV_V_FLOAT(kv) ((kv)->a.v_float)
/*!< \brief Evaluates to the `double` value of a `wolfsentry_kv_pair` of type `WOLFSENTRY_KV_FLOAT`. @hideinitializer */
#define WOLFSENTRY_KV_V_STRING_LEN(kv) ((kv)->a.string_len)
@@ -3179,7 +3184,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_sint(
const char *key,
int key_len,
int64_t *value);
- /*!< \brief Gets a `WOLFSENTRY_KV_UINT` value with the designated `key`. */
+ /*!< \brief Gets a `WOLFSENTRY_KV_SINT` value with the designated `key`. */
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_double(
WOLFSENTRY_CONTEXT_ARGS_IN,
@@ -3194,7 +3199,7 @@ WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_get_float(
const char *key,
int key_len,
double *value);
- /*!< \brief Gets a `WOLFSENTRY_KV_UINT` value with the designated `key`. */
+ /*!< \brief Gets a `WOLFSENTRY_KV_FLOAT` value with the designated `key`. */
WOLFSENTRY_API wolfsentry_errcode_t wolfsentry_user_value_store_string(
WOLFSENTRY_CONTEXT_ARGS_IN,
diff --git a/wolfsentry/wolfsentry_errcodes.h b/wolfsentry/wolfsentry_errcodes.h
index 6dda150b..df262daa 100644
--- a/wolfsentry/wolfsentry_errcodes.h
+++ b/wolfsentry/wolfsentry_errcodes.h
@@ -260,7 +260,7 @@ static inline int WOLFSENTRY_ERROR_DECODE_LINE_NUMBER(wolfsentry_errcode_t x) {
/*!< \brief Get a shared lock on the current context, and on failure, return the `wolfsentry_errcode_t`. @hideinitializer */
#define WOLFSENTRY_PROMOTABLE_EX(ctx) wolfsentry_context_lock_shared_with_reservation_abstimed(ctx, thread, NULL)
- /*!< \brief Get a mutex on a `wolfsentry_context`, evaluating to the resulting `wolfsentry_errcode_t`. @hideinitializer */
+ /*!< \brief Get a shared lock with mutex promotion reservation on a `wolfsentry_context`, evaluating to the resulting `wolfsentry_errcode_t`. @hideinitializer */
#define WOLFSENTRY_PROMOTABLE_OR_RETURN() do { \
wolfsentry_errcode_t _lock_ret; \
@@ -304,7 +304,7 @@ static inline int WOLFSENTRY_ERROR_DECODE_LINE_NUMBER(wolfsentry_errcode_t x) {
/*!< \brief Calculate the `wolfsentry_errcode_t` return value for an expression `y`, then unlock the current context, and finally, return the encoded `wolfsentry_errcode_t`. @hideinitializer */
#define WOLFSENTRY_SUCCESS_UNLOCK_AND_RETURN(name) do { WOLFSENTRY_UNLOCK_FOR_RETURN(); WOLFSENTRY_SUCCESS_RETURN(name); } while (0)
- /*!< \brief Unlock the current context, and return a `wolfsentry_errcode_t` encoding the current source ID and line number, and the designated short-form success `name` (e.g. `INVALID_ARG`). @hideinitializer */
+ /*!< \brief Unlock the current context, and return a `wolfsentry_errcode_t` encoding the current source ID and line number, and the designated short-form success `name` (e.g. `OK`). @hideinitializer */
#define WOLFSENTRY_SUCCESS_UNLOCK_AND_RETURN_RECODED(x) do { WOLFSENTRY_UNLOCK_FOR_RETURN(); WOLFSENTRY_SUCCESS_RETURN_RECODED(x); } while (0)
/*!< \brief Unlock the current context, then take an encoded `wolfsentry_errcode_t` `x`, recode it with the current source ID and line number, and return it. @hideinitializer */
#define WOLFSENTRY_SUCCESS_UNLOCK_AND_RERETURN(x) do { WOLFSENTRY_UNLOCK_FOR_RETURN(); WOLFSENTRY_SUCCESS_RERETURN(x); } while (0)
diff --git a/wolfsentry/wolfsentry_lwip.h b/wolfsentry/wolfsentry_lwip.h
index 2a459f94..202d7c4f 100644
--- a/wolfsentry/wolfsentry_lwip.h
+++ b/wolfsentry/wolfsentry_lwip.h
@@ -39,6 +39,8 @@
* `FILT_PORT_UNREACHABLE` -- Call into wolfSentry when inbound traffic attempts to reach an unlistened/unbound port
* `FILT_INBOUND_ERR` -- Call into wolfSentry when inbound traffic results in detection of an error by lwIP
* `FILT_OUTBOUND_ERR` -- Call into wolfSentry when outbound traffic results in detection of an error by lwIP
+ *
+ * lwIP has a single process-wide set of filter slots, so at most one wolfSentry context can own them at a time. The first context to install a callback with a nonzero mask becomes the owner of all slots. While it owns them, any installation or disabling call from a different context fails with `WOLFSENTRY_ERROR_ID_INCOMPATIBLE_STATE` and leaves the owner's callbacks in place. Ownership is released when the owner disables every slot it installed (mask zero), or when the owner is shut down, which runs `wolfsentry_cleanup_lwip_filter_callbacks()`; a replacement context can install only after that. A filter callback denies the packet whenever the wolfSentry dispatch fails or no context is attached.
*/
#ifndef WOLFSENTRY_LWIP_H
diff --git a/wolfsentry/wolfsentry_settings.h b/wolfsentry/wolfsentry_settings.h
index 6670084c..5864a0f0 100644
--- a/wolfsentry/wolfsentry_settings.h
+++ b/wolfsentry/wolfsentry_settings.h
@@ -255,7 +255,7 @@
#undef WOLFSENTRY_NO_ERROR_STRINGS
#define WOLFSENTRY_NO_PROTOCOL_NAMES
- /*!< \brief If defined, omit APIs for rendering error codes and source code files in human readable form. They will be rendered numerically. */
+ /*!< \brief If defined, omit APIs for converting between address family names and numbers. Address families will be rendered numerically. Protocol and service name lookup is controlled separately by `WOLFSENTRY_NO_GETPROTOBY`. */
#undef WOLFSENTRY_NO_PROTOCOL_NAMES
#define WOLFSENTRY_NO_ADDR_BITMASK_MATCHING
diff --git a/wolfsentry/wolfssl_test.h b/wolfsentry/wolfssl_test.h
index 6163b935..a3c2a51b 100644
--- a/wolfsentry/wolfssl_test.h
+++ b/wolfsentry/wolfssl_test.h
@@ -373,7 +373,7 @@ static int wolfsentry_setup(
WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD,
- 0 /* event_label_len */, 0 /* event_label */, &id,
+ NULL /* event_label */, 0 /* event_label_len */, &id,
&action_results)) < 0) {
fprintf(stderr, "wolfsentry_route_insert() returned "
WOLFSENTRY_ERROR_FMT "\n",
@@ -432,7 +432,7 @@ static int wolfsentry_setup(
WOLFSENTRY_ROUTE_FLAG_SA_PROTO_WILDCARD |
WOLFSENTRY_ROUTE_FLAG_SA_REMOTE_PORT_WILDCARD |
WOLFSENTRY_ROUTE_FLAG_SA_LOCAL_PORT_WILDCARD,
- 0 /* event_label_len */, 0 /* event_label */, &id,
+ NULL /* event_label */, 0 /* event_label_len */, &id,
&action_results)) < 0) {
fprintf(stderr, "wolfsentry_route_insert() returned "
WOLFSENTRY_ERROR_FMT "\n",