diff options
author | Oto Šťáva <oto.stava@nic.cz> | 2024-06-04 17:38:58 +0200 |
---|---|---|
committer | Oto Šťáva <oto.stava@nic.cz> | 2024-06-04 17:38:58 +0200 |
commit | b868b2584ef0bd3841256de596e3074443e9397b (patch) | |
tree | 2bcc576adb9edcf2e540b8bb237d8d9c434a7b50 | |
parent | Merge branch rrl-wip-sample into rrl-wip (diff) | |
parent | Merge branch 'nits' into 'master' (diff) | |
download | knot-resolver-b868b2584ef0bd3841256de596e3074443e9397b.tar.xz knot-resolver-b868b2584ef0bd3841256de596e3074443e9397b.zip |
Merge branch 'master' into 'rrl-wip'
104 files changed, 1543 insertions, 2005 deletions
diff --git a/.clang-tidy b/.clang-tidy index b496044c..4d77c1a7 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,54 @@ --- -Checks: 'bugprone-*,cert-*,-cert-dcl03-c,-clang-analyzer-unix.Malloc,-clang-analyzer-deadcode.DeadStores,-clang-analyzer-valist.Uninitialized,readability-*,-readability-braces-*,-readability-else-after-return,-readability-redundant-declaration,-readability-non-const-parameter,google-readability-casting,misc-*,-misc-static-assert,-misc-macro-parentheses,-misc-unused-parameters' -WarningsAsErrors: 'cert-*,misc-*,readability-*,clang-analyzer-*,-readability-non-const-parameter' +Checks: |- + bugprone-*, + cert-*, + google-readability-casting, + misc-*, + readability-*, + + -bugprone-assignment-in-if-condition, + -bugprone-branch-clone, + -bugprone-easily-swappable-parameters, + -bugprone-inc-dec-in-conditions, + -bugprone-multi-level-implicit-pointer-conversion, + -bugprone-narrowing-conversions, + -bugprone-not-null-terminated-result, + -bugprone-sizeof-expression, + -bugprone-suspicious-string-compare, + -cert-dcl03-c, + -clang-analyzer-deadcode.DeadStores, + -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, + -clang-analyzer-unix.Malloc, + -clang-analyzer-valist.Uninitialized, + -clang-analyzer-optin.core.EnumCastOutOfRange, + -misc-include-cleaner, + -misc-macro-parentheses, + -misc-no-recursion, + -misc-static-assert, + -misc-unused-parameters, + -readability-avoid-nested-conditional-operator, + -readability-avoid-unconditional-preprocessor-if, + -readability-braces-*, + -readability-cognitive-complexity, + -readability-else-after-return, + -readability-function-cognitive-complexity, + -readability-identifier-length, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-non-const-parameter, + -readability-redundant-declaration, + -clang-analyzer-core.UndefinedBinaryOperatorResult + +# TODO: remove `-clang-analyzer-core.UndefinedBinaryOperatorResult` when we +# upgrade to Clang >=18 (it's a false positive ) + +WarningsAsErrors: |- + cert-*, + clang-analyzer-*, + misc-*, + readability-*, + -readability-non-const-parameter, + HeaderFilterRegex: 'contrib/ucw/*.h' CheckOptions: - key: readability-identifier-naming @@ -65,6 +65,7 @@ /self.key /stamp-h1 /tags +/tests/dnstap/src/dnstap-test/go.sum /tests/pytests/*/tcproxy /tests/pytests/*/tlsproxy /tests/pytests/pytests.*.html diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e8239a97..2395ebb0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,7 +17,13 @@ variables: PREFIX: $CI_PROJECT_DIR/.local EMAIL: 'ci@nic' -image: $CI_REGISTRY/knot/knot-resolver/ci/debian-11:knot-$KNOT_VERSION + # IMAGE_TAG is a Git branch/tag name from https://gitlab.nic.cz/knot/knot-resolver-ci + # In general, keep it pointing to a tag - use a branch only for development. + # More info in the knot-resolver-ci repository. + IMAGE_TAG: 'v20240604' + IMAGE_PREFIX: '$CI_REGISTRY/knot/knot-resolver-ci' + +image: $IMAGE_PREFIX/debian12-knot_3_3:$IMAGE_TAG default: interruptible: true tags: @@ -63,7 +69,7 @@ stages: .after_build: &after_build <<: *common needs: - - build + - build-stable before_script: # meson detects changes and performs useless rebuild; hide the log - ninja -C build_ci* &>/dev/null @@ -78,6 +84,12 @@ stages: reports: junit: build_ci*/meson-logs/integration.deckard.junit.xml +.after_build_arch: &after_build_arch + <<: *after_build + image: $IMAGE_PREFIX/arch:$IMAGE_TAG + needs: + - build-arch + .nodep: &nodep <<: *common needs: [] @@ -94,6 +106,9 @@ stages: - pkg reports: junit: build_ci*/meson-logs/testlog.junit.xml + before_script: + - "echo \"PATH: $PATH\"" + - "echo \"Using Python at: $(which python)\"" after_script: - ci/fix-meson-junit.sh build_ci*/meson-logs/testlog.junit.xml @@ -103,38 +118,89 @@ archive: script: - apkg make-archive -build: +build-arch: + <<: *build + image: $IMAGE_PREFIX/arch:$IMAGE_TAG + script: + - meson build_ci_arch --prefix=$PREFIX -Dmalloc=disabled -Dwerror=true + - ninja -C build_ci_arch + - ninja -C build_ci_arch install >/dev/null + - ${MESON_TEST} --suite unit --suite config --suite dnstap --no-suite snowflake + +build-stable: + <<: *build + script: + - meson build_ci_stable --prefix=$PREFIX -Dmalloc=disabled -Dwerror=true -Dextra_tests=enabled + - ninja -C build_ci_stable + - ninja -C build_ci_stable install >/dev/null + - ${MESON_TEST} --suite unit --suite config --suite dnstap --no-suite snowflake + +build-deb11-knot31: <<: *build + image: $IMAGE_PREFIX/debian11-knot_3_1:$IMAGE_TAG script: - - meson build_ci --prefix=$PREFIX -Dmalloc=disabled -Dwerror=true -Dextra_tests=enabled - - ninja -C build_ci - - ninja -C build_ci install >/dev/null + - meson build_ci_deb11_knot31 --prefix=$PREFIX -Dmalloc=disabled -Dwerror=true -Dextra_tests=enabled + - ninja -C build_ci_deb11_knot31 + - ninja -C build_ci_deb11_knot31 install >/dev/null - ${MESON_TEST} --suite unit --suite config --suite dnstap --no-suite snowflake -build-knot32: +build-deb11-knot32: <<: *build - image: $CI_REGISTRY/knot/knot-resolver/ci/debian-11:knot-3.2 + image: $IMAGE_PREFIX/debian11-knot_3_2:$IMAGE_TAG script: - - meson build_ci_knot32 --prefix=$PREFIX -Dmalloc=disabled -Dwerror=true -Dextra_tests=enabled - - ninja -C build_ci_knot32 - - ninja -C build_ci_knot32 install >/dev/null + - meson build_ci_deb11_knot32 --prefix=$PREFIX -Dmalloc=disabled -Dwerror=true -Dextra_tests=enabled + - ninja -C build_ci_deb11_knot32 + - ninja -C build_ci_deb11_knot32 install >/dev/null - ${MESON_TEST} --suite unit --suite config --suite dnstap --no-suite snowflake -build-asan: +build-deb12-knot32: <<: *build + image: $IMAGE_PREFIX/debian12-knot_3_2:$IMAGE_TAG script: - # issues with UBSan and ASan in CI: - # - `ahocorasick.so` causes C++ problems - # - `--default-library=shared` causes link problems - - CC=clang CXX=clang++ CFLAGS=-fno-sanitize-recover=all CXXFLAGS=-fno-sanitize=undefined meson build_ci_asan --default-library=static --prefix=$PREFIX -Dmalloc=jemalloc -Db_sanitize=address,undefined -Dextra_tests=enabled - - ninja -C build_ci_asan - - ninja -C build_ci_asan install >/dev/null - # TODO _leaks: not sure what exactly is wrong in leak detection on config tests - # TODO skip_asan: all three of these disappear locally when using gcc 9.1 (except some leaks) - - MESON_TESTTHREADS=1 ASAN_OPTIONS=detect_leaks=0 ${MESON_TEST} --suite unit --suite config --suite dnstap --no-suite skip_asan --no-suite snowflake + - meson build_ci_deb12_knot32 --prefix=$PREFIX -Dmalloc=disabled -Dwerror=true -Dextra_tests=enabled + - ninja -C build_ci_deb12_knot32 + - ninja -C build_ci_deb12_knot32 install >/dev/null + - ${MESON_TEST} --suite unit --suite config --suite dnstap --no-suite snowflake + +build-deb12-knot-master: + <<: *build + image: $IMAGE_PREFIX/debian12-knot_master:$IMAGE_TAG + script: + - meson build_ci_deb12_knot_master --prefix=$PREFIX -Dmalloc=disabled -Dwerror=true -Dextra_tests=enabled + - ninja -C build_ci_deb12_knot_master + - ninja -C build_ci_deb12_knot_master install >/dev/null + - ${MESON_TEST} --suite unit --suite config --suite dnstap --no-suite snowflake + allow_failure: true + +build-stable-asan-gcc: + <<: *build + script: + - CFLAGS=-fno-sanitize-recover=all meson build_ci_asan_gcc --prefix=$PREFIX -Dmalloc=jemalloc -Db_sanitize=address,undefined -Dextra_tests=enabled + - ninja -C build_ci_asan_gcc + - ninja -C build_ci_asan_gcc install >/dev/null + - MESON_TESTTHREADS=1 ${MESON_TEST} --suite unit --suite dnstap --no-suite skip_asan --no-suite snowflake + - MESON_TESTTHREADS=1 ASAN_OPTIONS=detect_leaks=0 ${MESON_TEST} --suite config --no-suite skip_asan --no-suite snowflake + + +# TODO: Clang sanitizer seems to be broken in the current version of Debian. Use +# GCC above and maybe re-enable the Clang one once we update at some point. + +#build-stable-asan-clang: +# <<: *build +# script: +# # issues with UBSan and ASan in CI: +# # - `ahocorasick.so` causes C++ problems +# # - `--default-library=shared` causes link problems +# - CC=clang CXX=clang++ CFLAGS=-fno-sanitize-recover=all CXXFLAGS=-fno-sanitize=undefined meson build_ci_asan_clang --default-library=static --prefix=$PREFIX -Dmalloc=jemalloc -Db_sanitize=address,undefined -Dextra_tests=enabled +# - ninja -C build_ci_asan_clang +# - ninja -C build_ci_asan_clang install >/dev/null +# # TODO _leaks: not sure what exactly is wrong in leak detection on config tests +# # TODO skip_asan: all three of these disappear locally when using gcc 9.1 (except some leaks) +# - MESON_TESTTHREADS=1 ASAN_OPTIONS=detect_leaks=0 ${MESON_TEST} --suite unit --suite config --suite dnstap --no-suite skip_asan --no-suite snowflake build:macOS: <<: *nodep + image: python:3-alpine only: refs: - branches@knot/knot-resolver @@ -142,7 +208,8 @@ build:macOS: when: delayed start_in: 3 minutes # allow some time for mirroring, job creation script: - - ci/gh_actions.py ${CI_COMMIT_REF_NAME} ${CI_COMMIT_SHA} + - pip3 install -U requests + - python3 ./ci/gh_actions.py ${CI_COMMIT_REF_NAME} ${CI_COMMIT_SHA} docker: <<: *nodep @@ -163,25 +230,6 @@ docker: after_script: # remove dangling images to avoid running out of disk space - docker rmi ${DOCKER_IMAGE_NAME} - docker rmi $(docker images -f "dangling=true" -q) - -sonarcloud: - <<: *nodep - stage: build - except: null - only: - - tags - - master@knot/knot-resolver - script: - - meson build_sonarcloud --prefix=$PREFIX -Dmalloc=disabled - - build-wrapper-linux-x86-64 --out-dir bw-output ninja -C build_sonarcloud - - > - sonar-scanner - -Dsonar.organization=cz-nic - -Dsonar.projectKey=CZ-NIC_knot-resolver - -Dsonar.sources=. - -Dsonar.cfamily.build-wrapper-output=bw-output - -Dsonar.host.url=https://sonarcloud.io - -Dsonar.projectVersion="$(git describe)" # }}} # sanity {{{ @@ -230,29 +278,8 @@ lint:pedantic: -Wpedantic -Wno-newline-eof -Wno-gnu-zero-variadic-macro-arguments -Wno-gnu-folding-constant' - ninja -C build_pedantic_clang -lint:scan-build: - <<: *after_build - # TODO migrate lint to debian-11 - image: $CI_REGISTRY/knot/knot-resolver/ci/debian-buster:knot-$KNOT_VERSION - before_script: - # -- end TODO - stage: sanity - artifacts: - when: on_failure - expire_in: '1 day' - paths: - - build_ci*/meson-logs/scanbuild - script: - - export SCANBUILD="$(realpath ./scripts/run-scanbuild-with-args.sh)" - - ninja -C build_ci* scan-build || true - - test "$(ls build_ci*/meson-logs/scanbuild/*/report-*.html | wc -l)" = 23 # we have this many errors ATM :-) - lint:tidy: - <<: *after_build - # TODO migrate lint to debian-11 - image: $CI_REGISTRY/knot/knot-resolver/ci/debian-buster:knot-$KNOT_VERSION - before_script: - # -- end TODO + <<: *after_build_arch stage: sanity script: - ninja -C build_ci* tidy @@ -260,7 +287,7 @@ lint:tidy: # Coverity reference: https://www.synopsys.com/blogs/software-security/integrating-coverity-scan-with-gitlab-ci/ lint:coverity: <<: *sanity - image: $CI_REGISTRY/knot/knot-resolver/ci/debian-11-coverity:knot-$KNOT_VERSION + image: $IMAGE_PREFIX/coverity:$IMAGE_TAG only: refs: - nightly@knot/knot-resolver @@ -273,6 +300,7 @@ lint:coverity: --form token=$COVERITY_SCAN_TOKEN --form email="knot-resolver@labs.nic.cz" --form file=@cov-int.tar.gz --form version="`git describe --tags`" --form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID" + --fail-with-body .kres-gen: &kres-gen <<: *sanity @@ -281,15 +309,12 @@ lint:coverity: - ninja -C build_ci_lib daemon/kresd - ninja -C build_ci_lib kres-gen - git diff --quiet || (git diff; exit 1) -kres-gen-30: - <<: *kres-gen - image: $CI_REGISTRY/knot/knot-resolver/ci/debian-11:knot-3.0 kres-gen-31: <<: *kres-gen - image: $CI_REGISTRY/knot/knot-resolver/ci/debian-11:knot-3.1 + image: $IMAGE_PREFIX/debian11-knot_3_1:$IMAGE_TAG kres-gen-32: <<: *kres-gen - image: $CI_REGISTRY/knot/knot-resolver/ci/debian-11:knot-3.2 + image: $IMAGE_PREFIX/debian12-knot_3_2:$IMAGE_TAG root.hints: <<: *sanity @@ -298,6 +323,21 @@ root.hints: - /^release.*$/ script: - scripts/update-root-hints.sh + +ci-image-is-tag: + <<: *sanity + image: alpine:3 + variables: + GIT_STRATEGY: none + script: + - apk add git + - ( + git ls-remote --tags --exit-code + https://gitlab.nic.cz/knot/knot-resolver-ci.git + refs/tags/$IMAGE_TAG + && echo "Everything is OK!" + ) + || (echo "'$IMAGE_TAG' is not a tag (probably a branch). Make sure to set it to a tag in production!"; exit 2) # }}} # test {{{ @@ -328,7 +368,7 @@ respdiff:basic: <<: *after_build stage: test needs: - - build-asan + - build-stable-asan-gcc script: - ulimit -n "$(ulimit -Hn)" # applies only for kresd ATM - ./ci/respdiff/start-resolvers.sh @@ -381,7 +421,7 @@ manager: pytests: <<: *test_flaky needs: - - build-asan + - build-stable-asan-gcc artifacts: when: always paths: @@ -688,13 +728,10 @@ pkg:arch: # docs: {{{ docs:build: - image: $CI_REGISTRY/packaging/apkg/lxc/fedora-36 stage: deploy needs: [] script: - git submodule update --init --recursive - - apkg build-dep -y - - dnf install -y python3-sphinx texinfo doxygen - pip3 install -U -r doc/requirements.txt - pip3 install -U sphinx_rtd_theme - meson build_doc -Ddoc=enabled @@ -14,6 +14,7 @@ Improvements * predict module: prefetching expiring records moved to prefetch module * prefetch module: new module to prefetch expiring records +- stats: add separate metrics for IPv6 and IPv4 (!1545) .. TODO: Change the link below to a versioned one when releasing. @@ -25,6 +26,10 @@ Incompatible changes <https://www.knot-resolver.cz/documentation/latest/config-cache-predict.html>`_ for more. +Bugfixes +-------- +- fix startup with `dnssec: false` (!1548) + Knot Resolver 6.0.7 (2024-03-27) ================================ @@ -100,6 +105,13 @@ https://www.knot-resolver.cz/documentation/latest/upgrading-to-6.html 5.x branch longterm support ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Knot Resolver 5.7.3 (2024-0m-dd) +================================ + +Improvements +------------ +- stats: add separate metrics for IPv6 and IPv4 (!1544) + Knot Resolver 5.7.2 (2024-03-27) ================================ diff --git a/ci/images/README.md b/ci/images/README.md deleted file mode 100644 index 52e49faf..00000000 --- a/ci/images/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Container images for CI - -## Image purpose - -### debian-11 - -The main image used by shared runners to execute most CI builds and tests. - -### debian-11-coverity - -A stripped down version of `debian-11`. It only contains build (not test) -dependencies of `kresd`. It also contains the `cov-build` tool for generating -inputs for [Coverity Scan](https://scan.coverity.com/). - -It is used by the `coverity` CI job to generate and send data to Coverity Scan -for analysis. - -To build this image, you need to retrieve the Coverity Scan token from the -dashboard and pass it to the `build.sh` script using the `COVERITY_SCAN_TOKEN` -environment variable, e.g.: - -``` -$ COVERITY_SCAN_TOKEN=the_secret_token ./build.sh debian-11-coverity -``` - -Sometimes, the Coverity Scan binaries need to be updated in order to maintain -compatibility with the cloud service. Simply rebuild this image and push it to -the registry to achieve this, no other changes (e.g. to the `Dockerfile`) are -required. - -### debian-buster (10) - -Used to serve the same purpose as `debian-11`. As of 2022-03-09, it is still -used by some jobs (linters). - -## Maintenance - -The `ci/images/` directory contains utility scripts to build, push or update -the container images. - -``` -$ ./build.sh debian-11 # builds a debian-11 image locally -$ ./push.sh debian-11 # pushes the local image into target registry -$ ./update.sh debian-11 # utility wrapper that both builds and pushes the image -$ ./update.sh */ # use shell expansion of dirnames to update all images -``` - -By default, a branch of Knot DNS deemed to be stable is selected according to -the `vars.sh` file. To build an image for a different Knot DNS branch, set the -`KNOT_BRANCH` environment variable to the name of the branch, e.g.: - -``` -$ KNOT_BRANCH='3.2' ./update.sh debian-11 -``` diff --git a/ci/images/build.sh b/ci/images/build.sh deleted file mode 100755 index 1e9eabb5..00000000 --- a/ci/images/build.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# build specified docker image - -CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -source "${CURRENT_DIR}"/vars.sh "$@" -set -ex - -if [ -n "$COVERITY_SCAN_TOKEN" ]; then - SECRETS="$SECRETS --secret id=coverity-token,env=COVERITY_SCAN_TOKEN" -fi - -DOCKERFILE="$(realpath "${IMAGE}")/Dockerfile" - -cd "$CURRENT_DIR/../.." -export DOCKER_BUILDKIT=1 # Enables using secrets in docker-build -docker build \ - --pull \ - --no-cache \ - --tag "${FULL_NAME}" \ - --file "${DOCKERFILE}" \ - . \ - --build-arg KNOT_BRANCH=${KNOT_BRANCH} \ - $SECRETS diff --git a/ci/images/debian-11-coverity/Dockerfile b/ci/images/debian-11-coverity/Dockerfile deleted file mode 100644 index 19156145..00000000 --- a/ci/images/debian-11-coverity/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -FROM debian:bullseye -MAINTAINER Knot Resolver <knot-resolver@labs.nic.cz> -# >= 3.0 needed because of --enable-xdp=yes -ARG KNOT_BRANCH=3.1 -ARG COVERITY_SCAN_PROJECT_NAME=CZ-NIC/knot-resolver -ENV DEBIAN_FRONTEND=noninteractive - -WORKDIR /root -CMD ["/bin/bash"] - -# generic cleanup -RUN apt-get update -qq - -# Knot and Knot Resolver dependencies -RUN apt-get install -y -qqq git make cmake pkg-config meson \ - build-essential bsdmainutils libtool autoconf libcmocka-dev \ - liburcu-dev libgnutls28-dev libedit-dev liblmdb-dev libcap-ng-dev libsystemd-dev \ - libelf-dev libmnl-dev libidn11-dev libuv1-dev \ - libluajit-5.1-dev lua-http libssl-dev libnghttp2-dev - -# LuaJIT binary for stand-alone scripting -RUN apt-get install -y -qqq luajit - -# build and install latest version of Knot DNS -RUN git clone --depth=1 --branch=$KNOT_BRANCH https://gitlab.nic.cz/knot/knot-dns.git /tmp/knot -WORKDIR /tmp/knot -RUN pwd -RUN autoreconf -if -RUN ./configure --prefix=/usr --enable-xdp=yes -RUN CFLAGS="-g" make -RUN make install -RUN ldconfig - -# curl and tar (for downloading Coverity tools and uploading logs) -RUN apt-get install -y curl tar - -RUN --mount=type=secret,id=coverity-token \ - curl -o /tmp/cov-analysis-linux64.tar.gz https://scan.coverity.com/download/cxx/linux64 \ - --form project=$COVERITY_SCAN_PROJECT_NAME --form token=$(cat /run/secrets/coverity-token) -RUN tar xfz /tmp/cov-analysis-linux64.tar.gz -RUN mv cov-analysis-linux64-* /opt/cov-analysis diff --git a/ci/images/debian-11/Dockerfile b/ci/images/debian-11/Dockerfile deleted file mode 100644 index 0241a6d4..00000000 --- a/ci/images/debian-11/Dockerfile +++ /dev/null @@ -1,146 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -FROM debian:bullseye -MAINTAINER Knot Resolver <knot-resolver@labs.nic.cz> -# >= 3.0 needed because of --enable-xdp=yes -ARG KNOT_BRANCH=3.1 -ENV DEBIAN_FRONTEND=noninteractive - -WORKDIR /root -CMD ["/bin/bash"] - -# generic cleanup -RUN apt-get update -qq - -# Knot and Knot Resolver dependencies -RUN apt-get install -y -qqq git make cmake pkg-config meson \ - build-essential bsdmainutils libtool autoconf libcmocka-dev \ - liburcu-dev libgnutls28-dev libedit-dev liblmdb-dev libcap-ng-dev libsystemd-dev \ - libelf-dev libmnl-dev libidn11-dev libuv1-dev libjemalloc-dev \ - libluajit-5.1-dev lua-http libssl-dev libnghttp2-dev - -# Build and testing deps for Resolver's dnstap module (go stuff is just for testing) -RUN apt-get install -y -qqq \ - protobuf-c-compiler libprotobuf-c-dev libfstrm-dev \ - golang-any -COPY ./tests/dnstap /root/tests/dnstap -WORKDIR /root/tests/dnstap/src/dnstap-test -RUN go get . -WORKDIR /root - -# documentation dependencies -RUN apt-get install -y -qqq doxygen python3-sphinx python3-breathe python3-sphinx-rtd-theme - -# Python packages required for Deckard CI -# Python: grab latest versions from PyPi -# (Augeas binding in Debian packages are slow and buggy) -RUN apt-get install -y -qqq python3-pip wget augeas-tools -RUN pip3 install --upgrade pip -RUN pip3 install pylint -RUN pip3 install pep8 -# FIXME replace with dnspython >= 2.2.0 once released -RUN pip3 install git+https://github.com/bwelling/dnspython.git@72348d4698a8f8b209fbdf9e72738904ad31b930 -# tests/pytest dependencies: skip over broken versions -RUN pip3 install jinja2 'pytest != 6.0.0' pytest-html pytest-xdist pytest-forked -# apkg for packaging -RUN pip3 install apkg - -# packet capture tools for Deckard -RUN apt-get install --no-install-suggests --no-install-recommends -y -qqq tcpdump wireshark-common - -# Faketime for Deckard -RUN apt-get install -y -qqq faketime - -# C dependencies for python-augeas -RUN apt-get install -y -qqq libaugeas-dev libffi-dev -# Python dependencies for Deckard -RUN wget https://gitlab.nic.cz/knot/deckard/raw/master/requirements.txt -O /tmp/deckard-req.txt -RUN pip3 install -r /tmp/deckard-req.txt - -# build and install latest version of Knot DNS -RUN git clone --depth=1 --branch=$KNOT_BRANCH https://gitlab.nic.cz/knot/knot-dns.git /tmp/knot -WORKDIR /tmp/knot -RUN pwd -RUN autoreconf -if -RUN ./configure --prefix=/usr --enable-xdp=yes -RUN CFLAGS="-g" make -RUN make install -RUN ldconfig - -# Valgrind for kresd CI -RUN apt-get install valgrind -y -qqq -RUN wget https://github.com/LuaJIT/LuaJIT/raw/v2.1.0-beta3/src/lj.supp -O /lj.supp -# TODO: rebuild LuaJIT with Valgrind support - -# Lua lint for kresd CI -RUN apt-get install luarocks -y -qqq -RUN luarocks --lua-version 5.1 install luacheck - -# respdiff for kresd CI -RUN apt-get install lmdb-utils -y -qqq -RUN git clone --depth=1 https://gitlab.nic.cz/knot/respdiff /var/opt/respdiff -RUN pip3 install -r /var/opt/respdiff/requirements.txt - -# Python static analysis for respdiff -RUN pip3 install mypy -RUN pip3 install flake8 - -# Python requests for CI scripts -RUN pip3 install requests - -# docker-py for packaging tests -RUN pip3 install docker - -# Unbound for respdiff -RUN apt-get install unbound unbound-anchor -y -qqq -RUN printf "server:\n interface: 127.0.0.1@53535\n use-syslog: yes\n do-ip6: no\nremote-control:\n control-enable: no\n" >> /etc/unbound/unbound.conf - -# BIND for respdiff -RUN apt-get install bind9 -y -qqq -RUN printf '\nOPTIONS="-4 $OPTIONS"' >> /etc/default/bind9 -RUN printf 'options {\n directory "/var/cache/bind";\n listen-on port 53533 { 127.0.0.1; };\n listen-on-v6 port 53533 { ::1; };\n};\n' > /etc/bind/named.conf.options - -# PowerDNS Recursor for Deckard CI -RUN apt-get install pdns-recursor -y -qqq - -# dnsdist for Deckard CI -RUN apt-get install dnsdist -y -qqq - -# code coverage -RUN apt-get install -y -qqq lcov -RUN luarocks --lua-version 5.1 install luacov - -# LuaJIT binary for stand-alone scripting -RUN apt-get install -y -qqq luajit - -# clang for kresd CI, version updated as debian updates it -RUN apt-get install -y -qqq clang clang-tools clang-tidy - -# OpenBuildService CLI tool -RUN apt-get install -y osc - -# curl (API) -RUN apt-get install -y curl - -# configure knot-resolver-testing OBS repo for dependencies missing in Debian -RUN echo 'deb http://download.opensuse.org/repositories/home:/CZ-NIC:/knot-resolver-testing/Debian_11/ /' > /etc/apt/sources.list.d/knot-resolver-testing.list -RUN wget -nv https://download.opensuse.org/repositories/home:CZ-NIC:knot-resolver-testing/Debian_11/Release.key -O Release.key -RUN APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 apt-key add Release.key -RUN rm Release.key -RUN apt-get update -qq - -# packages from our knot-resolver-testing repo -RUN apt-get update -RUN apt-get install -y -qqq lua-psl - -# en_US.UTF-8 locale for scripts.update-authors.sh -RUN apt-get install -y -qqq locales -RUN sed -i "/en_US.UTF-8/ s/^#\(.*\)/\1/" /etc/locale.gen -RUN locale-gen - -# SonarCloud scanner -RUN wget -O /var/opt/wrapper.zip https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip -RUN wget -O /var/opt/scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip -RUN unzip -d /var/opt /var/opt/wrapper.zip -RUN unzip -d /var/opt /var/opt/scanner.zip -ENV PATH "$PATH:/var/opt/build-wrapper-linux-x86:/var/opt/sonar-scanner-5.0.1.3006-linux/bin" diff --git a/ci/images/debian-buster/Dockerfile b/ci/images/debian-buster/Dockerfile deleted file mode 100644 index 39f43277..00000000 --- a/ci/images/debian-buster/Dockerfile +++ /dev/null @@ -1,146 +0,0 @@ -# SPDX-License-Identifier: GPL-3.0-or-later - -FROM debian:buster -MAINTAINER Knot Resolver <knot-resolver@labs.nic.cz> -# >= 3.0 needed because of --enable-xdp=yes -ARG KNOT_BRANCH=3.0 -ENV DEBIAN_FRONTEND=noninteractive - -WORKDIR /root -CMD ["/bin/bash"] - -# generic cleanup -RUN apt-get update -qq -# TODO: run upgrade once buster reaches a stable release -# RUN apt-get upgrade -y -qqq - -# Knot and Knot Resolver dependencies -RUN apt-get install -y -qqq git make cmake pkg-config meson \ - build-essential bsdmainutils libtool autoconf libcmocka-dev \ - liburcu-dev libgnutls28-dev libedit-dev liblmdb-dev libcap-ng-dev libsystemd-dev \ - libelf-dev libmnl-dev libidn11-dev libuv1-dev \ - libluajit-5.1-dev lua-http libssl-dev libnghttp2-dev - -# Build and testing deps for Resolver's dnstap module (go stuff is just for testing) -RUN apt-get install -y -qqq \ - protobuf-c-compiler libprotobuf-c-dev libfstrm-dev \ - golang-any -COPY ./tests/dnstap /root/tests/dnstap -WORKDIR /root/tests/dnstap/src/dnstap-test -RUN go get . -WORKDIR /root - -# documentation dependencies -RUN apt-get install -y -qqq doxygen python3-sphinx python3-breathe python3-sphinx-rtd-theme - -# Python packages required for Deckard CI -# Python: grab latest versions from PyPi -# (Augeas binding in Debian packages are slow and buggy) -RUN apt-get install -y -qqq python3-pip wget augeas-tools -RUN pip3 install --upgrade pip -RUN pip3 install pylint -RUN pip3 install pep8 -RUN pip3 install pytest-xdist -# tests/pytest dependencies: skip over broken versions -RUN pip3 install 'dnspython != 2.0.0' 'jinja2 == 2.11.3' 'pytest != 6.0.0' pytest-html pytest-xdist - -# packet capture tools for Deckard -RUN apt-get install --no-install-suggests --no-install-recommends -y -qqq tcpdump wireshark-common - -# Faketime for Deckard -RUN apt-get install -y -qqq faketime - -# C dependencies for python-augeas -RUN apt-get install -y -qqq libaugeas-dev libffi-dev -# Python dependencies for Deckard -RUN wget https://gitlab.nic.cz/knot/deckard/raw/master/requirements.txt -O /tmp/deckard-req.txt -RUN pip3 install -r /tmp/deckard-req.txt - -# build and install latest version of Knot DNS -RUN git clone --depth=1 --branch=$KNOT_BRANCH https://gitlab.nic.cz/knot/knot-dns.git /tmp/knot -WORKDIR /tmp/knot -RUN pwd -RUN autoreconf -if -RUN ./configure --prefix=/usr --enable-xdp=yes -RUN CFLAGS="-g" make -RUN make install -RUN ldconfig - -# Valgrind for kresd CI -RUN apt-get install valgrind -y -qqq -RUN wget https://github.com/LuaJIT/LuaJIT/raw/v2.1.0-beta3/src/lj.supp -O /lj.supp -# TODO: rebuild LuaJIT with Valgrind support - -# Lua lint for kresd CI -RUN apt-get install luarocks -y -qqq -RUN luarocks --lua-version 5.1 install luacheck - -# respdiff for kresd CI -RUN apt-get install lmdb-utils -y -qqq -RUN git clone --depth=1 https://gitlab.nic.cz/knot/respdiff /var/opt/respdiff -RUN pip3 install -r /var/opt/respdiff/requirements.txt - -# Python static analysis for respdiff -RUN pip3 install mypy -RUN pip3 install flake8 - -# Python requests for CI scripts -RUN pip3 install requests - -# docker-py for packaging tests -RUN pip3 install docker - -# Unbound for respdiff -RUN apt-get install unbound unbound-anchor -y -qqq -RUN printf "server:\n interface: 127.0.0.1@53535\n use-syslog: yes\n do-ip6: no\nremote-control:\n control-enable: no\n" >> /etc/unbound/unbound.conf - -# BIND for respdiff -RUN apt-get install bind9 -y -qqq -RUN printf '\nOPTIONS="-4 $OPTIONS"' >> /etc/default/bind9 -RUN printf 'options {\n directory "/var/cache/bind";\n listen-on port 53533 { 127.0.0.1; };\n listen-on-v6 port 53533 { ::1; };\n};\n' > /etc/bind/named.conf.options - -# PowerDNS Recursor for Deckard CI -RUN apt-get install pdns-recursor -y -qqq - -# code coverage -RUN apt-get install -y -qqq lcov -RUN luarocks --lua-version 5.1 install luacov - -# LuaJIT binary for stand-alone scripting -RUN apt-get install -y -qqq luajit - -# clang for kresd CI, version updated as debian updates it -RUN apt-get install -y -qqq clang clang-tools clang-tidy - -# OpenBuildService CLI tool -RUN apt-get install -y osc - -# curl (API) -RUN apt-get install -y curl - -# configure knot-resolver-testing OBS repo for dependencies missing in Debian -RUN echo 'deb http://download.opensuse.org/repositories/home:/CZ-NIC:/knot-resolver-testing/Debian_10/ /' > /etc/apt/sources.list.d/knot-resolver-testing.list -RUN wget -nv https://download.opensuse.org/repositories/home:CZ-NIC:knot-resolver-testing/Debian_10/Release.key -O Release.key -RUN APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 apt-key add Release.key -RUN rm Release.key -RUN apt-get update -qq - -# packages from our knot-resolver-testing repo -RUN apt-get install -y -qqq lua-http lua-psl - -# en_US.UTF-8 locale for scripts.update-authors.sh -RUN apt-get install -y -qqq locales -RUN sed -i "/en_US.UTF-8/ s/^#\(.*\)/\1/" /etc/locale.gen -RUN locale-gen - -# SonarCloud scanner -RUN wget -O /var/opt/wrapper.zip https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip -RUN wget -O /var/opt/scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.4.0.2170-linux.zip -RUN unzip -d /var/opt /var/opt/wrapper.zip -RUN unzip -d /var/opt /var/opt/scanner.zip -ENV PATH "$PATH:/var/opt/build-wrapper-linux-x86:/var/opt/sonar-scanner-4.4.0.2170-linux/bin" - -# let's get newer meson from backports -RUN echo 'deb http://deb.debian.org/debian buster-backports main' > /etc/apt/sources.list.d/backports.list -RUN apt-get update -qq -RUN apt-get -t buster-backports install -y -qqq meson diff --git a/ci/images/push.sh b/ci/images/push.sh deleted file mode 100755 index 75f5f878..00000000 --- a/ci/images/push.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# upload docker image into registry - -CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" -source "${CURRENT_DIR}"/vars.sh "$@" -set -ex - -docker push "${FULL_NAME}" diff --git a/ci/images/update.sh b/ci/images/update.sh deleted file mode 100755 index 7be51727..00000000 --- a/ci/images/update.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -# build and upload docker image(s) into registry -# -# this is a simple wrapper around build.sh and update.sh -# -# to build & upload all images: ./update.sh */ - -if [[ $# -le 0 ]]; then - echo "usage: $0 IMAGE..." - exit 1 -fi -set -e - -for ARG in "$@" -do - IMAGE=${ARG%/} - echo "Building $IMAGE..." - ./build.sh $IMAGE - echo "Pushing $IMAGE..." - ./push.sh $IMAGE -done - diff --git a/ci/images/vars.sh b/ci/images/vars.sh deleted file mode 100755 index f2ea4655..00000000 --- a/ci/images/vars.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# define common variables for image build scripts - -KNOT_BRANCH="${KNOT_BRANCH:-3.1}" - -REGISTRY="registry.nic.cz/knot/knot-resolver/ci" -IMAGE=$1 -if [ -z "${IMAGE}" ]; then - echo "image name not provided" - exit 1 -fi -TAG="knot-${KNOT_BRANCH}" -FULL_NAME="${REGISTRY}/${IMAGE}:${TAG}" diff --git a/ci/pkgtest.yaml b/ci/pkgtest.yaml index b7b87c35..2ac4d4cf 100644 --- a/ci/pkgtest.yaml +++ b/ci/pkgtest.yaml @@ -119,7 +119,8 @@ nixos-unstable:pkgbuild: - docker - linux - ${PLATFORM} - image: nixos/nix + # https://github.com/NixOS/nix/issues/10648#issuecomment-2101993746 + image: docker.io/nixos/nix:latest-${PLATFORM} variables: NIX_PATH: nixpkgs=https://github.com/nixos/nixpkgs/archive/nixos-unstable.tar.gz diff --git a/contrib/dynarray.h b/contrib/dynarray.h deleted file mode 100644 index 7cbb686b..00000000 --- a/contrib/dynarray.h +++ /dev/null @@ -1,112 +0,0 @@ -/* Copyright (C) CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -/*! - * \brief Simple write-once allocation-optimal dynamic array. - * - * Include it into your .c file - * - * prefix - identifier prefix, e.g. ptr -> struct ptr_dynarray, ptr_dynarray_add(), ... - * ntype - data type to be stored. Let it be a number, pointer or small struct - * initial_capacity - how many data items will be allocated on stack and copied with assignment - * - * prefix_dynarray_add() - add a data item - * prefix_dynarray_fix() - call EVERYTIME the array is copied from some already invalid stack - * prefix_dynarray_free() - call EVERYTIME you dismiss all copies of the array - * - */ - -#include <stdlib.h> -#include <assert.h> - -#pragma once - -#define DYNARRAY_VISIBILITY_STATIC static -#define DYNARRAY_VISIBILITY_PUBLIC -#define DYNARRAY_VISIBILITY_LIBRARY __public__ - -#define dynarray_declare(prefix, ntype, visibility, initial_capacity) \ - typedef struct prefix ## _dynarray { \ - ssize_t capacity; \ - ssize_t size; \ - ntype *(*arr)(struct prefix ## _dynarray *dynarray); \ - ntype init[initial_capacity]; \ - ntype *_arr; \ - } prefix ## _dynarray_t; \ - \ - visibility ntype *prefix ## _dynarray_arr(prefix ## _dynarray_t *dynarray); \ - visibility void prefix ## _dynarray_add(prefix ## _dynarray_t *dynarray, \ - ntype const *to_add); \ - visibility void prefix ## _dynarray_free(prefix ## _dynarray_t *dynarray); - -#define dynarray_foreach(prefix, ntype, ptr, array) \ - for (ntype *ptr = prefix ## _dynarray_arr(&(array)); \ - ptr < prefix ## _dynarray_arr(&(array)) + (array).size; ptr++) - -#define dynarray_define(prefix, ntype, visibility) \ - \ - static void prefix ## _dynarray_free__(struct prefix ## _dynarray *dynarray) \ - { \ - if (dynarray->capacity > sizeof(dynarray->init) / sizeof(*dynarray->init)) { \ - free(dynarray->_arr); \ - } \ - } \ - \ - __attribute__((unused)) \ - visibility ntype *prefix ## _dynarray_arr(struct prefix ## _dynarray *dynarray) \ - { \ - assert(dynarray->size <= dynarray->capacity); \ - return (dynarray->capacity <= sizeof(dynarray->init) / sizeof(*dynarray->init) ? \ - dynarray->init : dynarray->_arr); \ - } \ - \ - static ntype *prefix ## _dynarray_arr_init__(struct prefix ## _dynarray *dynarray) \ - { \ - assert(dynarray->capacity == sizeof(dynarray->init) / sizeof(*dynarray->init)); \ - return dynarray->init; \ - } \ - \ - static ntype *prefix ## _dynarray_arr_arr__(struct prefix ## _dynarray *dynarray) \ - { \ - assert(dynarray->capacity > sizeof(dynarray->init) / sizeof(*dynarray->init)); \ - return dynarray->_arr; \ - } \ - \ - __attribute__((unused)) \ - visibility void prefix ## _dynarray_add(struct prefix ## _dynarray *dynarray, \ - ntype const *to_add) \ - { \ - if (dynarray->capacity < 0) { \ - return; \ - } \ - if (dynarray->capacity == 0) { \ - dynarray->capacity = sizeof(dynarray->init) / sizeof(*dynarray->init); \ - dynarray->arr = prefix ## _dynarray_arr_init__; \ - } \ - if (dynarray->size >= dynarray->capacity) { \ - ssize_t new_capacity = dynarray->capacity * 2 + 1; \ - ntype *new_arr = calloc(new_capacity, sizeof(ntype)); \ - if (new_arr == NULL) { \ - prefix ## _dynarray_free__(dynarray); \ - dynarray->capacity = dynarray->size = -1; \ - return; \ - } \ - if (dynarray->capacity > 0) { \ - memcpy(new_arr, prefix ## _dynarray_arr(dynarray), \ - dynarray->capacity * sizeof(ntype)); \ - } \ - prefix ## _dynarray_free__(dynarray); \ - dynarray->_arr = new_arr; \ - dynarray->capacity = new_capacity; \ - dynarray->arr = prefix ## _dynarray_arr_arr__; \ - } \ - prefix ## _dynarray_arr(dynarray)[dynarray->size++] = *to_add; \ - } \ - \ - __attribute__((unused)) \ - visibility void prefix ## _dynarray_free(struct prefix ## _dynarray *dynarray) \ - { \ - prefix ## _dynarray_free__(dynarray); \ - memset(dynarray, 0, sizeof(*dynarray)); \ - } diff --git a/contrib/dynarray.spdx b/contrib/dynarray.spdx deleted file mode 100644 index 02911c9c..00000000 --- a/contrib/dynarray.spdx +++ /dev/null @@ -1,10 +0,0 @@ -SPDXVersion: SPDX-2.1 -DataLicense: CC0-1.0 -SPDXID: SPDXRef-DOCUMENT -DocumentName: knotdns-dynarray -DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-ce6423dd-ac6a-4e78-90c3-5cbdef1e252c - -PackageName: knotdns-dynarray -PackageDownloadLocation: git+https://gitlab.nic.cz/knot/knot-dns.git@48c8b4f38cf5f7bf505c79b56adf7580688f6d3d#src/contrib/dynarray.h -PackageOriginator: Organization: Knot DNS contributors -PackageLicenseDeclared: GPL-3.0-or-later diff --git a/daemon/bindings/event.c b/daemon/bindings/event.c index 686e33e9..7e14dcd6 100644 --- a/daemon/bindings/event.c +++ b/daemon/bindings/event.c @@ -59,12 +59,12 @@ static int event_sched(lua_State *L, unsigned timeout, unsigned repeat) /* Start timer with the reference */ uv_loop_t *loop = uv_default_loop(); - uv_timer_init(loop, timer); - int ret = uv_timer_start(timer, event_callback, timeout, repeat); - if (ret != 0) { - free(timer); - lua_error_p(L, "couldn't start the event"); - } + int ret = uv_timer_init(loop, timer); + if (ret != 0) + goto exit_err; + ret = uv_timer_start(timer, event_callback, timeout, repeat); + if (ret != 0) + goto exit_err; /* Save callback and timer in registry */ lua_newtable(L); @@ -78,6 +78,10 @@ static int event_sched(lua_State *L, unsigned timeout, unsigned repeat) timer->data = (void *) (intptr_t)ref; lua_pushinteger(L, ref); return 1; + +exit_err: + free(timer); + lua_error_p(L, "couldn't start the event"); } static int event_after(lua_State *L) diff --git a/daemon/bindings/net.c b/daemon/bindings/net.c index d278ed17..aaeef238 100644 --- a/daemon/bindings/net.c +++ b/daemon/bindings/net.c @@ -468,7 +468,7 @@ static int net_interfaces(lua_State *L) /* Hardware address. */ char *p = buf; for (int k = 0; k < sizeof(iface.phys_addr); ++k) { - sprintf(p, "%.2x:", (uint8_t)iface.phys_addr[k]); + (void)sprintf(p, "%.2x:", (uint8_t)iface.phys_addr[k]); p += 3; } p[-1] = '\0'; @@ -788,7 +788,7 @@ static int net_tls_client(lua_State *L) /* Sort the strings for easier comparison later. */ if (newcfg->ca_files.len) { qsort(&newcfg->ca_files.at[0], newcfg->ca_files.len, - sizeof(newcfg->ca_files.at[0]), strcmp_p); + array_member_size(newcfg->ca_files), strcmp_p); } } lua_pop(L, 1); @@ -828,7 +828,7 @@ static int net_tls_client(lua_State *L) /* Sort the raw strings for easier comparison later. */ if (newcfg->pins.len) { qsort(&newcfg->pins.at[0], newcfg->pins.len, - sizeof(newcfg->pins.at[0]), cmp_sha256); + array_member_size(newcfg->pins), cmp_sha256); } } lua_pop(L, 1); @@ -1031,7 +1031,11 @@ static int net_tls_sticket_secret_file(lua_State *L) STR(net_tls_sticket_MIN_SECRET_LEN) " bytes", file_name); } - fclose(fp); + if (fclose(fp) == EOF) { + lua_error_p(L, + "net.tls_sticket_secret_file - reading of file '%s' failed", + file_name); + } tls_session_ticket_ctx_destroy(the_network->tls_session_ticket_ctx); the_network->tls_session_ticket_ctx = diff --git a/daemon/engine.c b/daemon/engine.c index 275718ee..509915df 100644 --- a/daemon/engine.c +++ b/daemon/engine.c @@ -29,8 +29,6 @@ #include "lib/dnssec/ta.h" #include "lib/log.h" -/* Cleanup engine state every 5 minutes */ -const size_t CLEANUP_TIMER = 5*60*1000; /* Execute byte code */ #define l_dobytecode(L, arr, len, name) \ @@ -544,7 +542,7 @@ int init_lua(void) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat" /* %1$ is not in C standard */ /* Save original package.path to package._path */ - snprintf(l_paths, MAXPATHLEN - 1, + (void)snprintf(l_paths, MAXPATHLEN - 1, "if package._path == nil then package._path = package.path end\n" "package.path = '%1$s/?.lua;%1$s/?/init.lua;'..package._path\n" "if package._cpath == nil then package._cpath = package.cpath end\n" diff --git a/daemon/http.c b/daemon/http.c index 7d1f0899..61ebcf9e 100644 --- a/daemon/http.c +++ b/daemon/http.c @@ -298,7 +298,7 @@ static int http_send_response(struct pl_http_sess_data *http, int32_t stream_id, max_age_len = asprintf(&max_age, "%s%" PRIu32, directive_max_age, ctx->payload.ttl); kr_require(max_age_len >= 0); - /* TODO: add a per-protolayer_grp option for content-type if we + /* TODO: add a per-kr_proto option for content-type if we * need to support protocols other than DNS here */ push_nv(&hdrs, MAKE_STATIC_NV("content-type", "application/dns-message")); push_nv(&hdrs, MAKE_STATIC_KEY_NV("content-length", size, size_len)); @@ -389,9 +389,9 @@ static ssize_t send_callback(nghttp2_session *h2, const uint8_t *data, size_t le memcpy(send_ctx->data, data, length); kr_log_debug(DOH, "[%p] send_callback: %p\n", (void *)h2, (void *)send_ctx->data); - session2_wrap_after(http->h.session, PROTOLAYER_PROTOCOL_HTTP, - protolayer_buffer(send_ctx->data, length, false), NULL, - callback_finished_free_baton, send_ctx); + session2_wrap_after(http->h.session, PROTOLAYER_TYPE_HTTP, + protolayer_payload_buffer(send_ctx->data, length, false), + NULL, callback_finished_free_baton, send_ctx); return length; } @@ -505,8 +505,8 @@ static int send_data_callback(nghttp2_session *h2, nghttp2_frame *frame, const u dest_iov[cur++] = (struct iovec){ (void *)padding, padlen - 1 }; kr_assert(cur == iovcnt); - int ret = session2_wrap_after(http->h.session, PROTOLAYER_PROTOCOL_HTTP, - protolayer_iovec(dest_iov, cur, false), + int ret = session2_wrap_after(http->h.session, PROTOLAYER_TYPE_HTTP, + protolayer_payload_iovec(dest_iov, cur, false), NULL, callback_finished_free_baton, sdctx); if (ret < 0) @@ -732,8 +732,9 @@ static int submit_to_wirebuffer(struct pl_http_sess_data *ctx) } ret = 0; - session2_unwrap_after(ctx->h.session, PROTOLAYER_PROTOCOL_HTTP, - protolayer_wire_buf(wb, false), NULL, NULL, NULL); + session2_unwrap_after(ctx->h.session, PROTOLAYER_TYPE_HTTP, + protolayer_payload_wire_buf(wb, false), + NULL, NULL, NULL); cleanup: http_cleanup_stream(ctx); return ret; @@ -835,7 +836,7 @@ static ssize_t read_callback(nghttp2_session *h2, int32_t stream_id, uint8_t *bu return send; } -static int pl_http_sess_init(struct protolayer_manager *manager, +static int pl_http_sess_init(struct session2 *session, void *data, void *param) { struct pl_http_sess_data *http = data; @@ -867,17 +868,17 @@ static int pl_http_sess_init(struct protolayer_manager *manager, http->current_method = HTTP_METHOD_NONE; http->uri_path = NULL; http->status = HTTP_STATUS_OK; - wire_buf_init(&http->wire_buf, manager->wire_buf.size); + wire_buf_init(&http->wire_buf, session->wire_buf.size); ret = nghttp2_session_server_new(&http->h2, callbacks, http); if (ret < 0) goto exit_callbacks; nghttp2_submit_settings(http->h2, NGHTTP2_FLAG_NONE, iv, ARRAY_SIZE(iv)); - struct sockaddr *peer = session2_get_peer(manager->session); + struct sockaddr *peer = session2_get_peer(session); kr_log_debug(DOH, "[%p] h2 session created for %s\n", (void *)http->h2, kr_straddr(peer)); - manager->session->custom_emalf_handling = true; + session->custom_emalf_handling = true; ret = kr_ok(); @@ -902,8 +903,7 @@ static int stream_write_data_break_err(trie_val_t *val, void *baton) return 0; } -static int pl_http_sess_deinit(struct protolayer_manager *manager, - void *data) +static int pl_http_sess_deinit(struct session2 *session, void *data) { struct pl_http_sess_data *http = data; @@ -938,7 +938,7 @@ static enum protolayer_iter_cb_result pl_http_unwrap( struct protolayer_payload pld = ctx->payload; if (pld.type == PROTOLAYER_PAYLOAD_WIRE_BUF) { - pld = protolayer_as_buffer(&pld); + pld = protolayer_payload_as_buffer(&pld); } if (pld.type == PROTOLAYER_PAYLOAD_BUFFER) { @@ -969,7 +969,7 @@ static enum protolayer_iter_cb_result pl_http_unwrap( if (ret < 0) { kr_log_debug(DOH, "[%p] nghttp2_session_send failed: %s (%zd)\n", (void *)http->h2, nghttp2_strerror(ret), ret); - return kr_error(EIO); + return protolayer_break(ctx, kr_error(EIO)); } if (!http_status_has_category(http->status, 2)) { @@ -1001,7 +1001,7 @@ static enum protolayer_iter_cb_result pl_http_wrap( static enum protolayer_event_cb_result pl_http_event_unwrap( enum protolayer_event_type event, void **baton, - struct protolayer_manager *manager, void *sess_data) + struct session2 *session, void *sess_data) { struct pl_http_sess_data *http = sess_data; @@ -1013,7 +1013,7 @@ static enum protolayer_event_cb_result pl_http_event_unwrap( return PROTOLAYER_EVENT_PROPAGATE; } -static void pl_http_request_init(struct protolayer_manager *manager, +static void pl_http_request_init(struct session2 *session, struct kr_request *req, void *sess_data) { @@ -1032,7 +1032,7 @@ static void pl_http_request_init(struct protolayer_manager *manager, void http_protolayers_init(void) { - protolayer_globals[PROTOLAYER_PROTOCOL_HTTP] = (struct protolayer_globals) { + protolayer_globals[PROTOLAYER_TYPE_HTTP] = (struct protolayer_globals) { .sess_size = sizeof(struct pl_http_sess_data), .sess_deinit = pl_http_sess_deinit, .wire_buf_overhead = HTTP_MAX_FRAME_SIZE, diff --git a/daemon/io.c b/daemon/io.c index ea98a7f0..b6b289ae 100644 --- a/daemon/io.c +++ b/daemon/io.c @@ -17,7 +17,6 @@ #endif #include "daemon/network.h" -#include "daemon/proxyv2.h" #include "daemon/worker.h" #include "daemon/tls.h" #include "daemon/http.h" @@ -40,7 +39,7 @@ static void check_bufsize(uv_handle_t* handle) * This is magic presuming we can pull in a whole recvmmsg width in one wave. * Linux will double this the bufsize wanted. */ - const int BUF_SIZE = 2 * sizeof(RECVMMSG_BATCH * KNOT_WIRE_MAX_PKTSIZE); + const int BUF_SIZE = 2 * RECVMMSG_BATCH * KNOT_WIRE_MAX_PKTSIZE; negotiate_bufsize(uv_recv_buffer_size, handle, BUF_SIZE); negotiate_bufsize(uv_send_buffer_size, handle, BUF_SIZE); } @@ -50,7 +49,7 @@ static void check_bufsize(uv_handle_t* handle) static void handle_getbuf(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { struct session2 *s = handle->data; - struct wire_buf *wb = &s->layers->wire_buf; + struct wire_buf *wb = &s->wire_buf; buf->base = wire_buf_free_space(wb); buf->len = wire_buf_free_space_length(wb); @@ -59,7 +58,7 @@ static void handle_getbuf(uv_handle_t* handle, size_t suggested_size, uv_buf_t* static void udp_on_unwrapped(int status, struct session2 *session, const struct comm_info *comm, void *baton) { - wire_buf_reset(&session->layers->wire_buf); + wire_buf_reset(&session->wire_buf); } void udp_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, @@ -88,9 +87,9 @@ void udp_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, return; } - int ret = wire_buf_consume(&s->layers->wire_buf, nread); + int ret = wire_buf_consume(&s->wire_buf, nread); if (ret) { - wire_buf_reset(&s->layers->wire_buf); + wire_buf_reset(&s->wire_buf); return; } @@ -98,7 +97,7 @@ void udp_recv(uv_udp_t *handle, ssize_t nread, const uv_buf_t *buf, .comm_addr = comm_addr, .src_addr = comm_addr }; - session2_unwrap(s, protolayer_wire_buf(&s->layers->wire_buf, false), + session2_unwrap(s, protolayer_payload_wire_buf(&s->wire_buf, true), &in_comm, udp_on_unwrapped, NULL); } @@ -107,7 +106,7 @@ static int family_to_freebind_option(sa_family_t sa_family, int *level, int *nam #define LOG_NO_FB kr_log_error(NETWORK, "your system does not support 'freebind', " \ "please remove it from your configuration\n") switch (sa_family) { - case AF_INET: + case AF_INET: // NOLINT(bugprone-branch-clone): The branches are only cloned for specific macro configs *level = IPPROTO_IP; #if defined(IP_FREEBIND) *name = IP_FREEBIND; @@ -137,76 +136,9 @@ static int family_to_freebind_option(sa_family_t sa_family, int *level, int *nam } -struct pl_udp_iter_data { - struct protolayer_data h; - struct proxy_result proxy; - bool has_proxy; -}; - -static enum protolayer_iter_cb_result pl_udp_unwrap( - void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx) -{ - ctx->payload = protolayer_as_buffer(&ctx->payload); - if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_BUFFER)) { - /* unsupported payload */ - return protolayer_break(ctx, kr_error(EINVAL)); - } - - struct session2 *s = ctx->manager->session; - struct pl_udp_iter_data *udp = iter_data; - - char *data = ctx->payload.buffer.buf; - ssize_t data_len = ctx->payload.buffer.len; - struct comm_info *comm = &ctx->comm; - if (!s->outgoing && proxy_header_present(data, data_len)) { - if (!proxy_allowed(comm->comm_addr)) { - kr_log_debug(IO, "<= ignoring PROXYv2 UDP from disallowed address '%s'\n", - kr_straddr(comm->comm_addr)); - return protolayer_break(ctx, kr_error(EPERM)); - } - - ssize_t trimmed = proxy_process_header(&udp->proxy, data, data_len); - if (trimmed == KNOT_EMALF) { - if (kr_log_is_debug(IO, NULL)) { - kr_log_debug(IO, "<= ignoring malformed PROXYv2 UDP " - "from address '%s'\n", - kr_straddr(comm->comm_addr)); - } - return protolayer_break(ctx, kr_error(EINVAL)); - } else if (trimmed < 0) { - if (kr_log_is_debug(IO, NULL)) { - kr_log_debug(IO, "<= error processing PROXYv2 UDP " - "from address '%s', ignoring\n", - kr_straddr(comm->comm_addr)); - } - return protolayer_break(ctx, kr_error(EINVAL)); - } - - if (udp->proxy.command == PROXY2_CMD_PROXY && udp->proxy.family != AF_UNSPEC) { - udp->has_proxy = true; - - comm->src_addr = &udp->proxy.src_addr.ip; - comm->dst_addr = &udp->proxy.dst_addr.ip; - comm->proxy = &udp->proxy; - - if (kr_log_is_debug(IO, NULL)) { - kr_log_debug(IO, "<= UDP query from '%s'\n", - kr_straddr(comm->src_addr)); - kr_log_debug(IO, "<= proxied through '%s'\n", - kr_straddr(comm->comm_addr)); - } - } - - ctx->payload = protolayer_buffer( - data + trimmed, data_len - trimmed, false); - } - - return protolayer_continue(ctx); -} - static enum protolayer_event_cb_result pl_udp_event_wrap( enum protolayer_event_type event, void **baton, - struct protolayer_manager *manager, void *sess_data) + struct session2 *session, void *sess_data) { if (event == PROTOLAYER_EVENT_STATS_SEND_ERR) { the_worker->stats.err_udp += 1; @@ -219,132 +151,20 @@ static enum protolayer_event_cb_result pl_udp_event_wrap( return PROTOLAYER_EVENT_PROPAGATE; } - -struct pl_tcp_sess_data { - struct protolayer_data h; - struct proxy_result proxy; - struct wire_buf wire_buf; - bool had_data : 1; - bool has_proxy : 1; -}; - -static int pl_tcp_sess_init(struct protolayer_manager *manager, - void *data, - void *param) +static int pl_tcp_sess_init(struct session2 *session, + void *data, void *param) { - struct sockaddr *peer = session2_get_peer(manager->session); - manager->session->comm = (struct comm_info) { + struct sockaddr *peer = session2_get_peer(session); + session->comm_storage = (struct comm_info) { .comm_addr = peer, .src_addr = peer }; return 0; } -static int pl_tcp_sess_deinit(struct protolayer_manager *manager, void *sess_data) -{ - struct pl_tcp_sess_data *tcp = sess_data; - wire_buf_deinit(&tcp->wire_buf); - return 0; -} - -static enum protolayer_iter_cb_result pl_tcp_unwrap( - void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx) -{ - struct session2 *s = ctx->manager->session; - struct pl_tcp_sess_data *tcp = sess_data; - struct sockaddr *peer = session2_get_peer(s); - - if (ctx->payload.type == PROTOLAYER_PAYLOAD_BUFFER) { - const char *buf = ctx->payload.buffer.buf; - const size_t len = ctx->payload.buffer.len; - - /* Copy a simple buffer into internal wirebuffer. */ - if (len > KNOT_WIRE_MAX_PKTSIZE) - return protolayer_break(ctx, kr_error(EMSGSIZE)); - - if (!tcp->wire_buf.buf) { - int ret = wire_buf_reserve(&tcp->wire_buf, - KNOT_WIRE_MAX_PKTSIZE); - if (ret) - return protolayer_break(ctx, ret); - } - - /* Try to make space */ - while (len > wire_buf_free_space_length(&tcp->wire_buf)) { - if (wire_buf_data_length(&tcp->wire_buf) > 0 || - tcp->wire_buf.start == 0) - return protolayer_break(ctx, kr_error(EMSGSIZE)); - - wire_buf_movestart(&tcp->wire_buf); - } - - memcpy(wire_buf_free_space(&tcp->wire_buf), buf, len); - wire_buf_consume(&tcp->wire_buf, ctx->payload.buffer.len); - ctx->payload = protolayer_wire_buf(&tcp->wire_buf, false); - } - - if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF)) { - /* TODO: iovec support unimplemented */ - return protolayer_break(ctx, kr_error(EINVAL)); - } - - char *data = wire_buf_data(ctx->payload.wire_buf); /* layer's or session's wirebuf */ - ssize_t data_len = wire_buf_data_length(ctx->payload.wire_buf); - struct comm_info *comm = &ctx->manager->session->comm; - if (!s->outgoing && !tcp->had_data && proxy_header_present(data, data_len)) { - if (!proxy_allowed(comm->src_addr)) { - if (kr_log_is_debug(IO, NULL)) { - kr_log_debug(IO, "<= connection to '%s': PROXYv2 not allowed " - "for this peer, close\n", - kr_straddr(peer)); - } - worker_end_tcp(s); - return protolayer_break(ctx, kr_error(ECONNRESET)); - } - - ssize_t trimmed = proxy_process_header(&tcp->proxy, data, data_len); - if (trimmed < 0) { - if (kr_log_is_debug(IO, NULL)) { - if (trimmed == KNOT_EMALF) { - kr_log_debug(IO, "<= connection to '%s': " - "malformed PROXYv2 header, close\n", - kr_straddr(comm->src_addr)); - } else { - kr_log_debug(IO, "<= connection to '%s': " - "error processing PROXYv2 header, close\n", - kr_straddr(comm->src_addr)); - } - } - worker_end_tcp(s); - return protolayer_break(ctx, kr_error(ECONNRESET)); - } else if (trimmed == 0) { - session2_close(s); - return protolayer_break(ctx, kr_error(ECONNRESET)); - } - - if (tcp->proxy.command != PROXY2_CMD_LOCAL && tcp->proxy.family != AF_UNSPEC) { - comm->src_addr = &tcp->proxy.src_addr.ip; - comm->dst_addr = &tcp->proxy.dst_addr.ip; - - if (kr_log_is_debug(IO, NULL)) { - kr_log_debug(IO, "<= TCP stream from '%s'\n", - kr_straddr(comm->src_addr)); - kr_log_debug(IO, "<= proxied through '%s'\n", - kr_straddr(comm->comm_addr)); - } - } - - wire_buf_trim(ctx->payload.wire_buf, trimmed); - } - - tcp->had_data = true; - ctx->comm = ctx->manager->session->comm; - return protolayer_continue(ctx); -} - static enum protolayer_event_cb_result pl_tcp_event_wrap( enum protolayer_event_type event, void **baton, - struct protolayer_manager *manager, void *sess_data) + struct session2 *session, void *sess_data) { if (event == PROTOLAYER_EVENT_STATS_SEND_ERR) { the_worker->stats.err_tcp += 1; @@ -359,17 +179,12 @@ static enum protolayer_event_cb_result pl_tcp_event_wrap( void io_protolayers_init(void) { - protolayer_globals[PROTOLAYER_PROTOCOL_UDP] = (struct protolayer_globals){ - .iter_size = sizeof(struct pl_udp_iter_data), - .unwrap = pl_udp_unwrap, + protolayer_globals[PROTOLAYER_TYPE_UDP] = (struct protolayer_globals){ .event_wrap = pl_udp_event_wrap, }; - protolayer_globals[PROTOLAYER_PROTOCOL_TCP] = (struct protolayer_globals){ - .sess_size = sizeof(struct pl_tcp_sess_data), + protolayer_globals[PROTOLAYER_TYPE_TCP] = (struct protolayer_globals){ .sess_init = pl_tcp_sess_init, - .sess_deinit = pl_tcp_sess_deinit, - .unwrap = pl_tcp_unwrap, .event_wrap = pl_tcp_event_wrap, }; } @@ -460,7 +275,7 @@ int io_listen_udp(uv_loop_t *loop, uv_udp_t *handle, int fd) uv_handle_t *h = (uv_handle_t *)handle; check_bufsize(h); /* Handle is already created, just create context. */ - struct session2 *s = session2_new_io(h, PROTOLAYER_GRP_DOUDP, NULL, 0, false); + struct session2 *s = session2_new_io(h, KR_PROTO_UDP53, NULL, 0, false); kr_require(s); int socklen = sizeof(union kr_sockaddr); @@ -503,21 +318,21 @@ static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) return; } - if (kr_fails_assert(buf->base == wire_buf_free_space(&s->layers->wire_buf))) { + if (kr_fails_assert(buf->base == wire_buf_free_space(&s->wire_buf))) { return; } - int ret = wire_buf_consume(&s->layers->wire_buf, nread); + int ret = wire_buf_consume(&s->wire_buf, nread); if (ret) { - wire_buf_reset(&s->layers->wire_buf); + wire_buf_reset(&s->wire_buf); return; } - session2_unwrap(s, protolayer_wire_buf(&s->layers->wire_buf, false), + session2_unwrap(s, protolayer_payload_wire_buf(&s->wire_buf, false), NULL, NULL, NULL); } -static void _tcp_accept(uv_stream_t *master, int status, enum protolayer_grp grp) +static void tcp_accept_internal(uv_stream_t *master, int status, enum kr_proto grp) { if (status != 0) { return; @@ -578,18 +393,18 @@ static void _tcp_accept(uv_stream_t *master, int status, enum protolayer_grp grp static void tcp_accept(uv_stream_t *master, int status) { - _tcp_accept(master, status, PROTOLAYER_GRP_DOTCP); + tcp_accept_internal(master, status, KR_PROTO_TCP53); } static void tls_accept(uv_stream_t *master, int status) { - _tcp_accept(master, status, PROTOLAYER_GRP_DOTLS); + tcp_accept_internal(master, status, KR_PROTO_DOT); } #if ENABLE_DOH2 static void https_accept(uv_stream_t *master, int status) { - _tcp_accept(master, status, PROTOLAYER_GRP_DOHTTPS); + tcp_accept_internal(master, status, KR_PROTO_DOH); } #endif @@ -790,18 +605,27 @@ void io_tty_process_input(uv_stream_t *stream, ssize_t nread, const uv_buf_t *bu len_s = 0; } uint32_t len_n = htonl(len_s); - fwrite(&len_n, sizeof(len_n), 1, out); - if (len_s > 0) - fwrite(message, len_s, 1, out); + if (fwrite(&len_n, sizeof(len_n), 1, out) != 1) + goto finish; + if (len_s > 0) { + if (fwrite(message, len_s, 1, out) != 1) + goto finish; + } break; case IO_MODE_TEXT: /* Human-readable and console-printable mode */ - if (message) - fprintf(out, "%s", message); - if (message || !args->quiet) - fprintf(out, "\n"); - if (!args->quiet) - fprintf(out, "> "); + if (message) { + if (fprintf(out, "%s", message) < 0) + goto finish; + } + if (message || !args->quiet) { + if (fprintf(out, "\n") < 0) + goto finish; + } + if (!args->quiet) { + if (fprintf(out, "> ") < 0) + goto finish; + } break; } @@ -824,7 +648,7 @@ void io_tty_process_input(uv_stream_t *stream, ssize_t nread, const uv_buf_t *bu finish: /* Close if redirected */ if (stream_fd != STDIN_FILENO) { - fclose(out); + (void)fclose(out); } /* If a LMDB transaction got open, we can't leave it hanging. * We accept the changes, if any. */ @@ -943,7 +767,7 @@ static void xdp_rx(uv_poll_t* handle, int status, int events) memcpy(comm.eth_from, msg->eth_from, sizeof(comm.eth_from)); memcpy(comm.eth_to, msg->eth_to, sizeof(comm.eth_to)); session2_unwrap(xhd->session, - protolayer_buffer( + protolayer_payload_buffer( msg->payload.iov_base, msg->payload.iov_len, false), &comm, NULL, NULL); @@ -1034,7 +858,7 @@ int io_listen_xdp(uv_loop_t *loop, struct endpoint *ep, const char *ifname) return kr_error(ret); } - xhd->session = session2_new_io(ep->handle, PROTOLAYER_GRP_DOUDP, + xhd->session = session2_new_io(ep->handle, KR_PROTO_UDP53, NULL, 0, false); kr_require(xhd->session); session2_get_sockname(xhd->session)->sa_family = AF_XDP; // to have something in there @@ -1046,7 +870,7 @@ int io_listen_xdp(uv_loop_t *loop, struct endpoint *ep, const char *ifname) #endif int io_create(uv_loop_t *loop, struct session2 **out_session, int type, - unsigned family, enum protolayer_grp grp, + unsigned family, enum kr_proto grp, struct protolayer_data_param *layer_param, size_t layer_param_count, bool outgoing) { diff --git a/daemon/io.h b/daemon/io.h index 305f26cf..b03c6aae 100644 --- a/daemon/io.h +++ b/daemon/io.h @@ -45,7 +45,7 @@ void tcp_timeout_trigger(uv_timer_t *timer); * \param family = AF_* * \param has_tls has meanings only when type is SOCK_STREAM */ int io_create(uv_loop_t *loop, struct session2 **out_session, int type, - unsigned family, enum protolayer_grp grp, + unsigned family, enum kr_proto grp, struct protolayer_data_param *layer_param, size_t layer_param_count, bool outgoing); void io_free(uv_handle_t *handle); diff --git a/daemon/main.c b/daemon/main.c index 1f65ea77..6ea94310 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -7,6 +7,7 @@ #include "contrib/ucw/mempool.h" #include "daemon/engine.h" #include "daemon/io.h" +#include "daemon/proxyv2.h" #include "daemon/network.h" #include "daemon/udp_queue.h" #include "daemon/worker.h" @@ -448,9 +449,9 @@ int main(int argc, char **argv) { kr_log_group_reset(); if (setvbuf(stdout, NULL, _IONBF, 0) || setvbuf(stderr, NULL, _IONBF, 0)) { - kr_log_error(SYSTEM, "failed to to set output buffering (ignored): %s\n", + kr_log_error(SYSTEM, "failed to set output buffering (ignored): %s\n", strerror(errno)); - fflush(stderr); + (void)fflush(stderr); } if (strcmp("linux", OPERATING_SYSTEM) != 0) kr_log_warning(SYSTEM, "Knot Resolver is tested on Linux, other platforms might exhibit bugs.\n" @@ -513,7 +514,7 @@ int main(int argc, char **argv) if (ret) { kr_log_error(SYSTEM, "failed to get or set file-descriptor limit: %s\n", strerror(errno)); - } else if (rlim.rlim_cur < 512*1024) { + } else if (rlim.rlim_cur < (rlim_t)512 * 1024) { kr_log_warning(SYSTEM, "warning: hard limit for number of file-descriptors is only %ld but recommended value is 524288\n", (long)rlim.rlim_cur); } @@ -586,6 +587,7 @@ int main(int argc, char **argv) io_protolayers_init(); tls_protolayers_init(); + proxy_protolayers_init(); #ifdef ENABLE_DOH2 http_protolayers_init(); #endif diff --git a/daemon/proxyv2.c b/daemon/proxyv2.c index 73eb5769..8f1ca8f1 100644 --- a/daemon/proxyv2.c +++ b/daemon/proxyv2.c @@ -3,14 +3,17 @@ */ #include "daemon/network.h" +#include "daemon/session2.h" +#include "daemon/worker.h" #include "lib/generic/trie.h" #include "daemon/proxyv2.h" -const char PROXY2_SIGNATURE[12] = { +static const char PROXY2_SIGNATURE[12] = { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A }; +#define PROXY2_MIN_SIZE 16 #define PROXY2_IP6_ADDR_SIZE 16 #define PROXY2_UNIX_ADDR_SIZE 108 @@ -167,7 +170,10 @@ bool proxy_allowed(const struct sockaddr *saddr) return kr_bitcmp((char *)&addr, (char *)&found->addr, found->netmask) == 0; } -ssize_t proxy_process_header(struct proxy_result *out, +/** Parses the PROXYv2 header from buf of size nread and writes the result into + * out. The function assumes that the PROXYv2 signature is present + * and has been already checked by the caller (like `udp_recv` or `tcp_recv`). */ +static ssize_t proxy_process_header(struct proxy_result *out, const void *buf, const ssize_t nread) { if (!buf) @@ -277,6 +283,7 @@ ssize_t proxy_process_header(struct proxy_result *out, &addr->ipv6_addr.dst_addr, sizeof(out->dst_addr.ip6.sin6_addr.s6_addr)); break; + default:; /* Keep zero from initializer. */ } /* Process additional information */ @@ -285,10 +292,172 @@ ssize_t proxy_process_header(struct proxy_result *out, case TLV_TYPE_SSL: out->has_tls = true; break; - /* TODO: add more TLV types if needed */ + default:; /* Ignore others - add more if needed */ } } fill_wirebuf: return hdr_len; } + +/** Checks for a PROXY protocol version 2 signature in the specified buffer. */ +static inline bool proxy_header_present(const void* buf, const ssize_t nread) +{ + return nread >= PROXY2_MIN_SIZE && + memcmp(buf, PROXY2_SIGNATURE, sizeof(PROXY2_SIGNATURE)) == 0; +} + + +struct pl_proxyv2_dgram_iter_data { + struct protolayer_data h; + struct proxy_result proxy; + bool has_proxy; +}; + +static enum protolayer_iter_cb_result pl_proxyv2_dgram_unwrap( + void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx) +{ + ctx->payload = protolayer_payload_as_buffer(&ctx->payload); + if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_BUFFER)) { + /* unsupported payload */ + return protolayer_break(ctx, kr_error(EINVAL)); + } + + struct session2 *s = ctx->session; + struct pl_proxyv2_dgram_iter_data *udp = iter_data; + + char *data = ctx->payload.buffer.buf; + ssize_t data_len = ctx->payload.buffer.len; + struct comm_info *comm = ctx->comm; + if (!s->outgoing && proxy_header_present(data, data_len)) { + if (!proxy_allowed(comm->comm_addr)) { + kr_log_debug(IO, "<= ignoring PROXYv2 UDP from disallowed address '%s'\n", + kr_straddr(comm->comm_addr)); + return protolayer_break(ctx, kr_error(EPERM)); + } + + ssize_t trimmed = proxy_process_header(&udp->proxy, data, data_len); + if (trimmed == KNOT_EMALF) { + if (kr_log_is_debug(IO, NULL)) { + kr_log_debug(IO, "<= ignoring malformed PROXYv2 UDP " + "from address '%s'\n", + kr_straddr(comm->comm_addr)); + } + return protolayer_break(ctx, kr_error(EINVAL)); + } else if (trimmed < 0) { + if (kr_log_is_debug(IO, NULL)) { + kr_log_debug(IO, "<= error processing PROXYv2 UDP " + "from address '%s', ignoring\n", + kr_straddr(comm->comm_addr)); + } + return protolayer_break(ctx, kr_error(EINVAL)); + } + + if (udp->proxy.command == PROXY2_CMD_PROXY && udp->proxy.family != AF_UNSPEC) { + udp->has_proxy = true; + + comm->src_addr = &udp->proxy.src_addr.ip; + comm->dst_addr = &udp->proxy.dst_addr.ip; + comm->proxy = &udp->proxy; + + if (kr_log_is_debug(IO, NULL)) { + kr_log_debug(IO, "<= UDP query from '%s'\n", + kr_straddr(comm->src_addr)); + kr_log_debug(IO, "<= proxied through '%s'\n", + kr_straddr(comm->comm_addr)); + } + } + + ctx->payload = protolayer_payload_buffer( + data + trimmed, data_len - trimmed, false); + } + + return protolayer_continue(ctx); +} + + +struct pl_proxyv2_stream_sess_data { + struct protolayer_data h; + struct proxy_result proxy; + bool had_data : 1; + bool has_proxy : 1; +}; + +static enum protolayer_iter_cb_result pl_proxyv2_stream_unwrap( + void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx) +{ + struct session2 *s = ctx->session; + struct pl_proxyv2_stream_sess_data *tcp = sess_data; + struct sockaddr *peer = session2_get_peer(s); + + if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF)) { + /* Only wire buffer is supported */ + return protolayer_break(ctx, kr_error(EINVAL)); + } + + char *data = wire_buf_data(ctx->payload.wire_buf); /* layer's or session's wirebuf */ + ssize_t data_len = wire_buf_data_length(ctx->payload.wire_buf); + struct comm_info *comm = ctx->comm; + if (!s->outgoing && !tcp->had_data && proxy_header_present(data, data_len)) { + if (!proxy_allowed(comm->src_addr)) { + if (kr_log_is_debug(IO, NULL)) { + kr_log_debug(IO, "<= connection to '%s': PROXYv2 not allowed " + "for this peer, close\n", + kr_straddr(peer)); + } + worker_end_tcp(s); + return protolayer_break(ctx, kr_error(ECONNRESET)); + } + + ssize_t trimmed = proxy_process_header(&tcp->proxy, data, data_len); + if (trimmed < 0) { + if (kr_log_is_debug(IO, NULL)) { + if (trimmed == KNOT_EMALF) { + kr_log_debug(IO, "<= connection to '%s': " + "malformed PROXYv2 header, close\n", + kr_straddr(comm->src_addr)); + } else { + kr_log_debug(IO, "<= connection to '%s': " + "error processing PROXYv2 header, close\n", + kr_straddr(comm->src_addr)); + } + } + worker_end_tcp(s); + return protolayer_break(ctx, kr_error(ECONNRESET)); + } else if (trimmed == 0) { + session2_close(s); + return protolayer_break(ctx, kr_error(ECONNRESET)); + } + + if (tcp->proxy.command != PROXY2_CMD_LOCAL && tcp->proxy.family != AF_UNSPEC) { + comm->src_addr = &tcp->proxy.src_addr.ip; + comm->dst_addr = &tcp->proxy.dst_addr.ip; + + if (kr_log_is_debug(IO, NULL)) { + kr_log_debug(IO, "<= TCP stream from '%s'\n", + kr_straddr(comm->src_addr)); + kr_log_debug(IO, "<= proxied through '%s'\n", + kr_straddr(comm->comm_addr)); + } + } + + wire_buf_trim(ctx->payload.wire_buf, trimmed); + } + + tcp->had_data = true; + return protolayer_continue(ctx); +} + + +void proxy_protolayers_init(void) +{ + protolayer_globals[PROTOLAYER_TYPE_PROXYV2_DGRAM] = (struct protolayer_globals){ + .iter_size = sizeof(struct pl_proxyv2_dgram_iter_data), + .unwrap = pl_proxyv2_dgram_unwrap, + }; + + protolayer_globals[PROTOLAYER_TYPE_PROXYV2_STREAM] = (struct protolayer_globals){ + .sess_size = sizeof(struct pl_proxyv2_stream_sess_data), + .unwrap = pl_proxyv2_stream_unwrap, + }; +} diff --git a/daemon/proxyv2.h b/daemon/proxyv2.h index a21f14b1..9bba3476 100644 --- a/daemon/proxyv2.h +++ b/daemon/proxyv2.h @@ -8,10 +8,6 @@ #include "lib/utils.h" -extern const char PROXY2_SIGNATURE[12]; - -#define PROXY2_MIN_SIZE 16 - enum proxy2_command { PROXY2_CMD_LOCAL = 0x0, PROXY2_CMD_PROXY = 0x1 @@ -35,19 +31,9 @@ struct proxy_result { bool has_tls : 1; }; -/** Checks for a PROXY protocol version 2 signature in the specified buffer. */ -static inline bool proxy_header_present(const void* buf, const ssize_t nread) -{ - return nread >= PROXY2_MIN_SIZE && - memcmp(buf, PROXY2_SIGNATURE, sizeof(PROXY2_SIGNATURE)) == 0; -} - /** Checks whether the use of PROXYv2 protocol is allowed for the specified * address. */ bool proxy_allowed(const struct sockaddr *saddr); -/** Parses the PROXYv2 header from buf of size nread and writes the result into - * out. The function assumes that the PROXYv2 signature is present - * and has been already checked by the caller (like `udp_recv` or `tcp_recv`). */ -ssize_t proxy_process_header(struct proxy_result *out, - const void *buf, ssize_t nread); +/** Initializes the protocol layers managed by the PROXYv2 "module". */ +void proxy_protolayers_init(void); diff --git a/daemon/session2.c b/daemon/session2.c index a2c519a0..5f6f7d00 100644 --- a/daemon/session2.c +++ b/daemon/session2.c @@ -32,73 +32,79 @@ static uint32_t next_log_id = 1; -struct protolayer_globals protolayer_globals[PROTOLAYER_PROTOCOL_COUNT] = {{0}}; +struct protolayer_globals protolayer_globals[PROTOLAYER_TYPE_COUNT] = {{0}}; -static const enum protolayer_protocol protolayer_grp_doudp[] = { - PROTOLAYER_PROTOCOL_UDP, - PROTOLAYER_PROTOCOL_DNS_DGRAM, - PROTOLAYER_PROTOCOL_NULL +static const enum protolayer_type protolayer_grp_udp53[] = { + PROTOLAYER_TYPE_UDP, + PROTOLAYER_TYPE_PROXYV2_DGRAM, + PROTOLAYER_TYPE_DNS_DGRAM, }; -static const enum protolayer_protocol protolayer_grp_dotcp[] = { - PROTOLAYER_PROTOCOL_TCP, - PROTOLAYER_PROTOCOL_DNS_MULTI_STREAM, - PROTOLAYER_PROTOCOL_NULL +static const enum protolayer_type protolayer_grp_tcp53[] = { + PROTOLAYER_TYPE_TCP, + PROTOLAYER_TYPE_PROXYV2_STREAM, + PROTOLAYER_TYPE_DNS_MULTI_STREAM, }; -static const enum protolayer_protocol protolayer_grp_dot[] = { - PROTOLAYER_PROTOCOL_TCP, - PROTOLAYER_PROTOCOL_TLS, - PROTOLAYER_PROTOCOL_DNS_MULTI_STREAM, - PROTOLAYER_PROTOCOL_NULL +static const enum protolayer_type protolayer_grp_dot[] = { + PROTOLAYER_TYPE_TCP, + PROTOLAYER_TYPE_PROXYV2_STREAM, + PROTOLAYER_TYPE_TLS, + PROTOLAYER_TYPE_DNS_MULTI_STREAM, }; -static const enum protolayer_protocol protolayer_grp_doh[] = { - PROTOLAYER_PROTOCOL_TCP, - PROTOLAYER_PROTOCOL_TLS, - PROTOLAYER_PROTOCOL_HTTP, - PROTOLAYER_PROTOCOL_DNS_UNSIZED_STREAM, - PROTOLAYER_PROTOCOL_NULL +static const enum protolayer_type protolayer_grp_doh[] = { + PROTOLAYER_TYPE_TCP, + PROTOLAYER_TYPE_PROXYV2_STREAM, + PROTOLAYER_TYPE_TLS, + PROTOLAYER_TYPE_HTTP, + PROTOLAYER_TYPE_DNS_UNSIZED_STREAM, }; -/** Sequences of layers, mapped by `enum protolayer_grp`. +static const enum protolayer_type protolayer_grp_doq[] = { + // not yet used + PROTOLAYER_TYPE_NULL, +}; + +struct protolayer_grp { + const enum protolayer_type *layers; + size_t num_layers; +}; + +#define PROTOLAYER_GRP(p_array) { \ + .layers = (p_array), \ + .num_layers = sizeof((p_array)) / sizeof((p_array)[0]), \ +} + +/** Sequences of layers, or groups, mapped by `enum kr_proto`. + * + * Each group represents a sequence of layers in the unwrap direction (wrap + * direction being the opposite). The sequence dictates the order in which + * individual layers are processed. This macro is used to generate global data + * about groups. * - * To define a new group, add a new entry in the `PROTOLAYER_GRP_MAP` macro and + * To define a new group, add a new entry in the `KR_PROTO_MAP()` macro and * create a new static `protolayer_grp_*` array above, similarly to the already - * existing ones. Each array must end with `PROTOLAYER_GRP_NULL`, to indicate - * the end of the list of protocol layers. The array name's suffix must be the - * one defined as *Variable name* (2nd parameter) in the `PROTOLAYER_GRP_MAP` - * macro. */ -static const enum protolayer_protocol *protolayer_grps[PROTOLAYER_GRP_COUNT] = { -#define XX(cid, vid, name) [PROTOLAYER_GRP_ ## cid] = protolayer_grp_ ## vid, - PROTOLAYER_GRP_MAP(XX) + * existing ones. Each array must end with `PROTOLAYER_TYPE_NULL`, to + * indicate the end of the list of protocol layers. The array name's suffix must + * be the one defined as *Variable name* (2nd parameter) in the + * `KR_PROTO_MAP` macro. */ +static const struct protolayer_grp protolayer_grps[KR_PROTO_COUNT] = { +#define XX(cid, vid, name) [KR_PROTO_##cid] = PROTOLAYER_GRP(protolayer_grp_##vid), + KR_PROTO_MAP(XX) #undef XX }; -const char *protolayer_protocol_name(enum protolayer_protocol p) +const char *protolayer_layer_name(enum protolayer_type p) { switch (p) { - case PROTOLAYER_PROTOCOL_NULL: + case PROTOLAYER_TYPE_NULL: return "(null)"; -#define XX(cid) case PROTOLAYER_PROTOCOL_ ## cid: \ +#define XX(cid) case PROTOLAYER_TYPE_ ## cid: \ return #cid; - PROTOLAYER_PROTOCOL_MAP(XX) -#undef XX - default: - return "(invalid)"; - } -} - -const char *protolayer_grp_name(enum protolayer_grp g) -{ - switch (g) { - case PROTOLAYER_GRP_NULL: - return "(null)"; -#define XX(cid, vid, name) case PROTOLAYER_GRP_ ## cid: \ - return (name); - PROTOLAYER_GRP_MAP(XX) + PROTOLAYER_TYPE_MAP(XX) #undef XX default: return "(invalid)"; @@ -228,7 +234,8 @@ size_t protolayer_payload_copy(void *dest, } } -struct protolayer_payload protolayer_as_buffer(const struct protolayer_payload *payload) +struct protolayer_payload protolayer_payload_as_buffer( + const struct protolayer_payload *payload) { if (payload->type == PROTOLAYER_PAYLOAD_BUFFER) return *payload; @@ -300,14 +307,15 @@ bool protolayer_queue_has_payload(const protolayer_iter_ctx_queue_t *queue) /** Gets layer-specific session data for the layer with the specified index * from the manager. */ static inline struct protolayer_data *protolayer_sess_data_get( - struct protolayer_manager *m, size_t layer_ix) + struct session2 *s, size_t layer_ix) { - if (kr_fails_assert(layer_ix < m->num_layers)) + const struct protolayer_grp *grp = &protolayer_grps[s->proto]; + if (kr_fails_assert(layer_ix < grp->num_layers)) return NULL; - /* See doc comment of `struct protolayer_manager::data` */ - const ssize_t *offsets = (ssize_t *)m->data; - char *pl_data_beg = &m->data[2 * m->num_layers * sizeof(*offsets)]; + /* See doc comment of `struct session2::layer_data` */ + const ssize_t *offsets = (ssize_t *)s->layer_data; + char *pl_data_beg = &s->layer_data[2 * grp->num_layers * sizeof(*offsets)]; ssize_t offset = offsets[layer_ix]; if (offset < 0) /* No session data for this layer */ @@ -321,12 +329,13 @@ static inline struct protolayer_data *protolayer_sess_data_get( static inline struct protolayer_data *protolayer_iter_data_get( struct protolayer_iter_ctx *ctx, size_t layer_ix) { - struct protolayer_manager *m = ctx->manager; - if (kr_fails_assert(layer_ix < m->num_layers)) + struct session2 *s = ctx->session; + const struct protolayer_grp *grp = &protolayer_grps[s->proto]; + if (kr_fails_assert(layer_ix < grp->num_layers)) return NULL; - /* See doc comment of `struct protolayer_manager::data` */ - const ssize_t *offsets = (ssize_t *)&m->data[m->num_layers * sizeof(*offsets)]; + /* See doc comment of `struct session2::layer_data` */ + const ssize_t *offsets = (ssize_t *)&s->layer_data[grp->num_layers * sizeof(*offsets)]; ssize_t offset = offsets[layer_ix]; if (offset < 0) /* No iteration data for this layer */ @@ -335,11 +344,12 @@ static inline struct protolayer_data *protolayer_iter_data_get( return (struct protolayer_data *)(ctx->data + offset); } -static inline ssize_t protolayer_manager_get_protocol( - struct protolayer_manager *m, enum protolayer_protocol protocol) +static inline ssize_t session2_get_protocol( + struct session2 *s, enum protolayer_type protocol) { - for (ssize_t i = 0; i < m->num_layers; i++) { - enum protolayer_protocol found = protolayer_grps[m->grp][i]; + const struct protolayer_grp *grp = &protolayer_grps[s->proto]; + for (ssize_t i = 0; i < grp->num_layers; i++) { + enum protolayer_type found = grp->layers[i]; if (protocol == found) return i; } @@ -350,7 +360,7 @@ static inline ssize_t protolayer_manager_get_protocol( static inline bool protolayer_iter_ctx_is_last(struct protolayer_iter_ctx *ctx) { unsigned int last_ix = (ctx->direction == PROTOLAYER_UNWRAP) - ? ctx->manager->num_layers - 1 + ? protolayer_grps[ctx->session->proto].num_layers - 1 : 0; return ctx->layer_ix == last_ix; } @@ -363,46 +373,45 @@ static inline void protolayer_iter_ctx_next(struct protolayer_iter_ctx *ctx) ctx->layer_ix--; } -static inline const char *layer_name(enum protolayer_grp grp, ssize_t layer_ix) +static inline const char *layer_name(enum kr_proto grp, ssize_t layer_ix) { - if (grp >= PROTOLAYER_GRP_COUNT) + if (grp >= KR_PROTO_COUNT) return "(invalid)"; - enum protolayer_protocol p = protolayer_grps[grp][layer_ix]; - return protolayer_protocol_name(p); + enum protolayer_type p = protolayer_grps[grp].layers[layer_ix]; + return protolayer_layer_name(p); } static inline const char *layer_name_ctx(struct protolayer_iter_ctx *ctx) { - return layer_name(ctx->manager->grp, ctx->layer_ix); + return layer_name(ctx->session->proto, ctx->layer_ix); } static int protolayer_iter_ctx_finish(struct protolayer_iter_ctx *ctx, int ret) { - struct session2 *session = ctx->manager->session; - - struct protolayer_manager *m = ctx->manager; - struct protolayer_globals *globals = &protolayer_globals[m->grp]; - for (size_t i = 0; i < m->num_layers; i++) { + struct session2 *s = ctx->session; + const struct protolayer_globals *globals = &protolayer_globals[s->proto]; + const struct protolayer_grp *grp = &protolayer_grps[s->proto]; + for (size_t i = 0; i < grp->num_layers; i++) { struct protolayer_data *d = protolayer_iter_data_get(ctx, i); if (globals->iter_deinit) - globals->iter_deinit(m, ctx, d); + globals->iter_deinit(ctx, d); } if (ret) - VERBOSE_LOG(session, "layer context of group '%s' (on %u: %s) ended with return code %d\n", - protolayer_grp_name(ctx->manager->grp), + VERBOSE_LOG(s, "layer context of group '%s' (on %u: %s) ended with return code %d\n", + kr_proto_name(s->proto), ctx->layer_ix, layer_name_ctx(ctx), ret); if (ctx->status) - VERBOSE_LOG(session, "iteration of group '%s' (on %u: %s) ended with status %d\n", - protolayer_grp_name(ctx->manager->grp), + VERBOSE_LOG(s, "iteration of group '%s' (on %u: %s) ended with status %d\n", + kr_proto_name(s->proto), ctx->layer_ix, layer_name_ctx(ctx), ctx->status); if (ctx->finished_cb) - ctx->finished_cb(ret, session, &ctx->comm, + ctx->finished_cb(ret, s, ctx->comm, ctx->finished_cb_baton); - free(ctx->async_buffer); + mm_ctx_delete(&ctx->pool); free(ctx); return ret; @@ -418,10 +427,10 @@ static void protolayer_push_finished(int status, struct session2 *s, const struc /** Pushes the specified protocol layer's payload to the session's transport. */ static int protolayer_push(struct protolayer_iter_ctx *ctx) { - struct session2 *session = ctx->manager->session; + struct session2 *session = ctx->session; if (ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF) { - ctx->payload = protolayer_as_buffer(&ctx->payload); + ctx->payload = protolayer_payload_as_buffer(&ctx->payload); } if (kr_log_is_debug(PROTOLAYER, NULL)) { @@ -433,12 +442,12 @@ static int protolayer_push(struct protolayer_iter_ctx *ctx) session2_transport_push(session, ctx->payload.buffer.buf, ctx->payload.buffer.len, ctx->payload.short_lived, - &ctx->comm, protolayer_push_finished, ctx); + ctx->comm, protolayer_push_finished, ctx); } else if (ctx->payload.type == PROTOLAYER_PAYLOAD_IOVEC) { session2_transport_pushv(session, ctx->payload.iovec.iov, ctx->payload.iovec.cnt, ctx->payload.short_lived, - &ctx->comm, protolayer_push_finished, ctx); + ctx->comm, protolayer_push_finished, ctx); } else { kr_assert(false && "Invalid payload type"); return kr_error(EINVAL); @@ -447,7 +456,7 @@ static int protolayer_push(struct protolayer_iter_ctx *ctx) return PROTOLAYER_RET_ASYNC; } -static void protolayer_ensure_long_lived(struct protolayer_iter_ctx *ctx) +static void protolayer_payload_ensure_long_lived(struct protolayer_iter_ctx *ctx) { if (!ctx->payload.short_lived) return; @@ -456,60 +465,74 @@ static void protolayer_ensure_long_lived(struct protolayer_iter_ctx *ctx) if (kr_fails_assert(buf_len)) return; - void *buf = malloc(buf_len); + void *buf = mm_alloc(&ctx->pool, buf_len); kr_require(buf); protolayer_payload_copy(buf, &ctx->payload, buf_len); - ctx->async_buffer = buf; - ctx->payload = protolayer_buffer(buf, buf_len, false); + ctx->payload = protolayer_payload_buffer(buf, buf_len, false); } /** Processes as many layers as possible synchronously, returning when either * a layer has gone asynchronous, or when the whole sequence has finished. * - * May be called multiple times on the same `ctx` to continue processing - * after an asynchronous operation. */ + * May be called multiple times on the same `ctx` to continue processing after + * an asynchronous operation - user code will do this via *layer sequence return + * functions*. */ static int protolayer_step(struct protolayer_iter_ctx *ctx) { while (true) { - if (kr_fails_assert(ctx->manager->grp < PROTOLAYER_GRP_COUNT)) + if (kr_fails_assert(ctx->session->proto < KR_PROTO_COUNT)) return kr_error(EFAULT); - enum protolayer_protocol protocol = protolayer_grps[ctx->manager->grp][ctx->layer_ix]; + enum protolayer_type protocol = protolayer_grps[ctx->session->proto].layers[ctx->layer_ix]; struct protolayer_globals *globals = &protolayer_globals[protocol]; + bool was_async = ctx->async_mode; ctx->async_mode = false; - ctx->status = 0; - ctx->action = PROTOLAYER_ITER_ACTION_NULL; - protolayer_iter_cb cb = (ctx->direction == PROTOLAYER_UNWRAP) - ? globals->unwrap : globals->wrap; + /* Basically if we went asynchronous, we want to "resume" from + * underneath this `if` block. */ + if (!was_async) { + ctx->status = 0; + ctx->action = PROTOLAYER_ITER_ACTION_NULL; - if (ctx->manager->session->closing) { - return protolayer_iter_ctx_finish( - ctx, kr_error(ECANCELED)); - } + protolayer_iter_cb cb = (ctx->direction == PROTOLAYER_UNWRAP) + ? globals->unwrap : globals->wrap; - if (cb) { - struct protolayer_data *sess_data = protolayer_sess_data_get( - ctx->manager, ctx->layer_ix); - struct protolayer_data *iter_data = protolayer_iter_data_get( - ctx, ctx->layer_ix); - enum protolayer_iter_cb_result result = cb(sess_data, iter_data, ctx); - if (kr_fails_assert(result == PROTOLAYER_ITER_CB_RESULT_MAGIC)) { - /* Callback did not use a continuation function to return. */ - return protolayer_iter_ctx_finish(ctx, kr_error(EINVAL)); + if (ctx->session->closing) { + return protolayer_iter_ctx_finish( + ctx, kr_error(ECANCELED)); } - } else { - ctx->action = PROTOLAYER_ITER_ACTION_CONTINUE; - } + if (cb) { + struct protolayer_data *sess_data = protolayer_sess_data_get( + ctx->session, ctx->layer_ix); + struct protolayer_data *iter_data = protolayer_iter_data_get( + ctx, ctx->layer_ix); + enum protolayer_iter_cb_result result = cb(sess_data, iter_data, ctx); + if (kr_fails_assert(result == PROTOLAYER_ITER_CB_RESULT_MAGIC)) { + /* Callback did not use a *layer + * sequence return function* (see + * glossary). */ + return protolayer_iter_ctx_finish(ctx, kr_error(EINVAL)); + } + } else { + ctx->action = PROTOLAYER_ITER_ACTION_CONTINUE; + } - if (!ctx->action) { - /* Next step is from a callback */ - ctx->async_mode = true; - protolayer_ensure_long_lived(ctx); - return PROTOLAYER_RET_ASYNC; + if (!ctx->action) { + /* We're going asynchronous - the next step is + * probably going to be from some sort of a + * callback and we will "resume" from underneath + * this `if` block. */ + ctx->async_mode = true; + protolayer_payload_ensure_long_lived(ctx); + return PROTOLAYER_RET_ASYNC; + } + } + + if (kr_fails_assert(ctx->action)) { + return protolayer_iter_ctx_finish(ctx, kr_error(EINVAL)); } if (ctx->action == PROTOLAYER_ITER_ACTION_BREAK) { @@ -519,7 +542,7 @@ static int protolayer_step(struct protolayer_iter_ctx *ctx) if (kr_fails_assert(ctx->status == 0)) { /* Status should be zero without a BREAK. */ - return protolayer_iter_ctx_finish(ctx, kr_error(ECANCELED)); + return protolayer_iter_ctx_finish(ctx, kr_error(EINVAL)); } if (ctx->action == PROTOLAYER_ITER_ACTION_CONTINUE) { @@ -543,20 +566,25 @@ static int protolayer_step(struct protolayer_iter_ctx *ctx) /** Submits the specified buffer to the sequence of layers represented by the * specified protolayer manager. The sequence will be processed in the - * specified direction. + * specified `direction`, starting by the layer specified by `layer_ix`. * * Returns PROTOLAYER_RET_NORMAL when all layers have finished, * PROTOLAYER_RET_ASYNC when some layers are asynchronous and waiting for * continuation, or a negative number for errors (kr_error). */ -static int protolayer_manager_submit( - struct session2 *s, +static int session2_submit( + struct session2 *session, enum protolayer_direction direction, size_t layer_ix, struct protolayer_payload payload, const struct comm_info *comm, protolayer_finished_cb cb, void *baton) { - struct protolayer_manager *manager = s->layers; - if (!comm) - comm = &manager->session->comm; + if (session->closing) + return kr_error(ECANCELED); + if (kr_fails_assert(session->proto < KR_PROTO_COUNT)) + return kr_error(EFAULT); + + bool had_comm_param = (comm != NULL); + if (!had_comm_param) + comm = &session->comm_storage; // RRL: at this point we might start doing nontrivial work, // but we may not know the client's IP yet. @@ -566,62 +594,72 @@ static int protolayer_manager_submit( kr_rrl_sample_start(); // In particular we don't want to miss en/decryption work // for regular connections from clients. - if (!s->outgoing && s->secure && !proxy_allowed(comm->comm_addr)) + if (!session->outgoing && session->secure && !proxy_allowed(comm->comm_addr)) kr_rrl_sample_addr((const union kr_sockaddr *)comm->comm_addr); } int ret; - if (manager->session->closing) { - ret = kr_error(ECANCELED); - goto finish_ret; - } - - struct protolayer_iter_ctx *ctx = malloc(manager->cb_ctx_size); + struct protolayer_iter_ctx *ctx = malloc(session->iter_ctx_size); kr_require(ctx); - VERBOSE_LOG(manager->session, + VERBOSE_LOG(session, "%s submitted to grp '%s' in %s direction (%zu: %s)\n", protolayer_payload_name(payload.type), - protolayer_grp_name(manager->grp), + kr_proto_name(session->proto), (direction == PROTOLAYER_UNWRAP) ? "unwrap" : "wrap", - layer_ix, layer_name(manager->grp, layer_ix)); + layer_ix, layer_name(session->proto, layer_ix)); *ctx = (struct protolayer_iter_ctx) { .payload = payload, - .comm = *comm, .direction = direction, .layer_ix = layer_ix, - .manager = manager, + .session = session, .finished_cb = cb, .finished_cb_baton = baton }; - - for (size_t i = 0; i < manager->num_layers; i++) { - if (kr_fails_assert(ctx->manager->grp < PROTOLAYER_GRP_COUNT)) { - ret = kr_error(EFAULT); - goto finish_ret; + if (had_comm_param) { + struct comm_addr_storage *addrst = &ctx->comm_addr_storage; + if (comm->src_addr) { + memcpy(&addrst->src_addr.ip, comm->src_addr, + kr_sockaddr_len(comm->src_addr)); + ctx->comm_storage.src_addr = &addrst->src_addr.ip; + } + if (comm->comm_addr) { + memcpy(&addrst->comm_addr.ip, comm->comm_addr, + kr_sockaddr_len(comm->comm_addr)); + ctx->comm_storage.comm_addr = &addrst->comm_addr.ip; } + if (comm->dst_addr) { + memcpy(&addrst->dst_addr.ip, comm->dst_addr, + kr_sockaddr_len(comm->dst_addr)); + ctx->comm_storage.dst_addr = &addrst->dst_addr.ip; + } + ctx->comm = &ctx->comm_storage; + } else { + ctx->comm = &session->comm_storage; + } + mm_ctx_mempool(&ctx->pool, CPU_PAGE_SIZE); - enum protolayer_protocol p = protolayer_grps[manager->grp][i]; - struct protolayer_globals *globals = &protolayer_globals[p]; + const struct protolayer_grp *grp = &protolayer_grps[session->proto]; + for (size_t i = 0; i < grp->num_layers; i++) { + struct protolayer_globals *globals = &protolayer_globals[grp->layers[i]]; struct protolayer_data *iter_data = protolayer_iter_data_get(ctx, i); if (iter_data) { memset(iter_data, 0, globals->iter_size); - iter_data->session = manager->session; + iter_data->session = session; } if (globals->iter_init) - globals->iter_init(manager, ctx, iter_data); + globals->iter_init(ctx, iter_data); } ret = protolayer_step(ctx); -finish_ret: if (direction == PROTOLAYER_UNWRAP) kr_rrl_sample_stop(); return ret; } -static void *get_init_param(enum protolayer_protocol p, +static void *get_init_param(enum protolayer_type p, struct protolayer_data_param *layer_param, size_t layer_param_count) { @@ -634,127 +672,26 @@ static void *get_init_param(enum protolayer_protocol p, return NULL; } -/** Allocates and initializes a new manager. */ -static struct protolayer_manager *protolayer_manager_new( - struct session2 *s, - enum protolayer_grp grp, - struct protolayer_data_param *layer_param, - size_t layer_param_count) -{ - if (kr_fails_assert(s && grp)) - return NULL; - - size_t num_layers = 0; - size_t manager_size = sizeof(struct protolayer_manager); - size_t cb_ctx_size = sizeof(struct protolayer_iter_ctx); - - const enum protolayer_protocol *protocols = protolayer_grps[grp]; - if (kr_fails_assert(protocols)) - return NULL; - const enum protolayer_protocol *p = protocols; - - /* Space for offset index */ - for (; *p; p++) - num_layers++; - if (kr_fails_assert(num_layers)) - return NULL; - - size_t wire_buf_length = 0; - size_t wire_buf_max_length = 0; - ssize_t offsets[2 * num_layers]; - manager_size += sizeof(offsets); - - ssize_t *sess_offsets = offsets; - ssize_t *iter_offsets = &offsets[num_layers]; - - /* Space for layer-specific data, guaranteeing alignment */ - size_t total_sess_data_size = 0; - size_t total_iter_data_size = 0; - for (size_t i = 0; i < num_layers; i++) { - const struct protolayer_globals *g = &protolayer_globals[protocols[i]]; - - sess_offsets[i] = g->sess_size ? total_sess_data_size : -1; - total_sess_data_size += ALIGN_TO(g->sess_size, CPU_STRUCT_ALIGN); - - iter_offsets[i] = g->iter_size ? total_iter_data_size : -1; - total_iter_data_size += ALIGN_TO(g->iter_size, CPU_STRUCT_ALIGN); - - size_t wire_buf_overhead = (g->wire_buf_overhead_cb) - ? g->wire_buf_overhead_cb(s->outgoing) - : g->wire_buf_overhead; - wire_buf_length += wire_buf_overhead; - wire_buf_max_length += MAX(g->wire_buf_max_overhead, wire_buf_overhead); - } - manager_size += total_sess_data_size; - cb_ctx_size += total_iter_data_size; - - /* Allocate and initialize manager */ - struct protolayer_manager *m = calloc(1, manager_size); - kr_require(m); - m->grp = grp; - m->session = s; - m->num_layers = num_layers; - m->cb_ctx_size = cb_ctx_size; - memcpy(m->data, offsets, sizeof(offsets)); - - m->wire_buf_max_length = wire_buf_max_length; - int ret = wire_buf_init(&m->wire_buf, wire_buf_length); - kr_require(!ret); - - /* Initialize the layer's session data */ - for (size_t i = 0; i < num_layers; i++) { - struct protolayer_globals *globals = &protolayer_globals[protocols[i]]; - struct protolayer_data *sess_data = protolayer_sess_data_get(m, i); - if (sess_data) { - memset(sess_data, 0, globals->sess_size); - sess_data->session = s; - } - - void *param = get_init_param(protocols[i], layer_param, layer_param_count); - if (globals->sess_init) - globals->sess_init(m, sess_data, param); - } - - return m; -} - -/** Deinitializes all layer data in the manager and deallocates it. */ -static void protolayer_manager_free(struct protolayer_manager *m) +/** Called by *Layer sequence return functions* to proceed with protolayer + * processing. If the */ +static inline void maybe_async_do_step(struct protolayer_iter_ctx *ctx) { - if (!m) return; - - for (size_t i = 0; i < m->num_layers; i++) { - enum protolayer_protocol p = protolayer_grps[m->grp][i]; - struct protolayer_globals *globals = &protolayer_globals[p]; - if (globals->sess_deinit) { - struct protolayer_data *sess_data = protolayer_sess_data_get(m, i); - globals->sess_deinit(m, sess_data); - } - } - - wire_buf_deinit(&m->wire_buf); - free(m); + if (ctx->async_mode) + protolayer_step(ctx); } enum protolayer_iter_cb_result protolayer_continue(struct protolayer_iter_ctx *ctx) { - if (ctx->async_mode) { - protolayer_iter_ctx_next(ctx); - protolayer_step(ctx); - } else { - ctx->action = PROTOLAYER_ITER_ACTION_CONTINUE; - } + ctx->action = PROTOLAYER_ITER_ACTION_CONTINUE; + maybe_async_do_step(ctx); return PROTOLAYER_ITER_CB_RESULT_MAGIC; } enum protolayer_iter_cb_result protolayer_break(struct protolayer_iter_ctx *ctx, int status) { ctx->status = status; - if (ctx->async_mode) { - protolayer_iter_ctx_finish(ctx, PROTOLAYER_RET_NORMAL); - } else { - ctx->action = PROTOLAYER_ITER_ACTION_BREAK; - } + ctx->action = PROTOLAYER_ITER_ACTION_BREAK; + maybe_async_do_step(ctx); return PROTOLAYER_ITER_CB_RESULT_MAGIC; } @@ -782,8 +719,9 @@ int wire_buf_reserve(struct wire_buf *wb, size_t size) if (wb->buf && wb->size >= size) return kr_ok(); - wb->buf = realloc(wb->buf, size); - kr_require(wb->buf); + char *newbuf = realloc(wb->buf, size); + kr_require(newbuf); + wb->buf = newbuf; wb->size = size; return kr_ok(); } @@ -836,14 +774,48 @@ int wire_buf_reset(struct wire_buf *wb) struct session2 *session2_new(enum session2_transport_type transport_type, - enum protolayer_grp layer_grp, + enum kr_proto proto, struct protolayer_data_param *layer_param, size_t layer_param_count, bool outgoing) { - kr_require(transport_type && layer_grp); + kr_require(transport_type && proto); + + size_t session_size = sizeof(struct session2); + size_t iter_ctx_size = sizeof(struct protolayer_iter_ctx); - struct session2 *s = malloc(sizeof(*s)); + const struct protolayer_grp *grp = &protolayer_grps[proto]; + if (kr_fails_assert(grp->num_layers)) + return NULL; + + size_t wire_buf_length = 0; + ssize_t offsets[2 * grp->num_layers]; + session_size += sizeof(offsets); + + ssize_t *sess_offsets = offsets; + ssize_t *iter_offsets = &offsets[grp->num_layers]; + + /* Space for layer-specific data, guaranteeing alignment */ + size_t total_sess_data_size = 0; + size_t total_iter_data_size = 0; + for (size_t i = 0; i < grp->num_layers; i++) { + const struct protolayer_globals *g = &protolayer_globals[grp->layers[i]]; + + sess_offsets[i] = g->sess_size ? total_sess_data_size : -1; + total_sess_data_size += ALIGN_TO(g->sess_size, CPU_STRUCT_ALIGN); + + iter_offsets[i] = g->iter_size ? total_iter_data_size : -1; + total_iter_data_size += ALIGN_TO(g->iter_size, CPU_STRUCT_ALIGN); + + size_t wire_buf_overhead = (g->wire_buf_overhead_cb) + ? g->wire_buf_overhead_cb(outgoing) + : g->wire_buf_overhead; + wire_buf_length += wire_buf_overhead; + } + session_size += total_sess_data_size; + iter_ctx_size += total_iter_data_size; + + struct session2 *s = malloc(session_size); kr_require(s); *s = (struct session2) { @@ -853,24 +825,35 @@ struct session2 *session2_new(enum session2_transport_type transport_type, .log_id = next_log_id++, .outgoing = outgoing, .tasks = trie_create(NULL), - }; - struct protolayer_manager *layers = protolayer_manager_new(s, layer_grp, - layer_param, layer_param_count); - if (!layers) { - free(s); - return NULL; - } - s->layers = layers; + .proto = proto, + .iter_ctx_size = iter_ctx_size, + }; - mm_ctx_mempool(&s->pool, CPU_PAGE_SIZE); + memcpy(&s->layer_data, offsets, sizeof(offsets)); queue_init(s->waiting); + int ret = wire_buf_init(&s->wire_buf, wire_buf_length); + kr_require(!ret); - int ret = uv_timer_init(uv_default_loop(), &s->timer); + ret = uv_timer_init(uv_default_loop(), &s->timer); kr_require(!ret); s->timer.data = s; s->uv_count++; /* Session owns the timer */ + /* Initialize the layer's session data */ + for (size_t i = 0; i < grp->num_layers; i++) { + struct protolayer_globals *globals = &protolayer_globals[grp->layers[i]]; + struct protolayer_data *sess_data = protolayer_sess_data_get(s, i); + if (sess_data) { + memset(sess_data, 0, globals->sess_size); + sess_data->session = s; + } + + void *param = get_init_param(grp->layers[i], layer_param, layer_param_count); + if (globals->sess_init) + globals->sess_init(s, sess_data, param); + } + session2_touch(s); return s; @@ -880,8 +863,16 @@ struct session2 *session2_new(enum session2_transport_type transport_type, * and timer are already closed, otherwise may leak resources. */ static void session2_free(struct session2 *s) { - protolayer_manager_free(s->layers); - mm_ctx_delete(&s->pool); + const struct protolayer_grp *grp = &protolayer_grps[s->proto]; + for (size_t i = 0; i < grp->num_layers; i++) { + struct protolayer_globals *globals = &protolayer_globals[grp->layers[i]]; + if (globals->sess_deinit) { + struct protolayer_data *sess_data = protolayer_sess_data_get(s, i); + globals->sess_deinit(s, sess_data); + } + } + + wire_buf_deinit(&s->wire_buf); trie_free(s->tasks); queue_deinit(s->waiting); free(s); @@ -1206,53 +1197,52 @@ int session2_unwrap(struct session2 *s, struct protolayer_payload payload, const struct comm_info *comm, protolayer_finished_cb cb, void *baton) { - return protolayer_manager_submit(s, PROTOLAYER_UNWRAP, 0, - payload, comm, cb, baton); + return session2_submit(s, PROTOLAYER_UNWRAP, + 0, payload, comm, cb, baton); } -int session2_unwrap_after(struct session2 *s, enum protolayer_protocol protocol, +int session2_unwrap_after(struct session2 *s, enum protolayer_type protocol, struct protolayer_payload payload, const struct comm_info *comm, protolayer_finished_cb cb, void *baton) { - ssize_t layer_ix = protolayer_manager_get_protocol(s->layers, protocol) + 1; + ssize_t layer_ix = session2_get_protocol(s, protocol) + 1; if (layer_ix < 0) return layer_ix; - return protolayer_manager_submit(s, PROTOLAYER_UNWRAP, layer_ix, - payload, comm, cb, baton); + return session2_submit(s, PROTOLAYER_UNWRAP, + layer_ix, payload, comm, cb, baton); } int session2_wrap(struct session2 *s, struct protolayer_payload payload, const struct comm_info *comm, protolayer_finished_cb cb, void *baton) { - return protolayer_manager_submit(s, PROTOLAYER_WRAP, - s->layers->num_layers - 1, + return session2_submit(s, PROTOLAYER_WRAP, + protolayer_grps[s->proto].num_layers - 1, payload, comm, cb, baton); } -int session2_wrap_after(struct session2 *s, enum protolayer_protocol protocol, +int session2_wrap_after(struct session2 *s, enum protolayer_type protocol, struct protolayer_payload payload, const struct comm_info *comm, protolayer_finished_cb cb, void *baton) { - ssize_t layer_ix = protolayer_manager_get_protocol(s->layers, protocol) - 1; + ssize_t layer_ix = session2_get_protocol(s, protocol) - 1; if (layer_ix < 0) return layer_ix; - return protolayer_manager_submit(s, PROTOLAYER_WRAP, layer_ix, + return session2_submit(s, PROTOLAYER_WRAP, layer_ix, payload, comm, cb, baton); } static void session2_event_wrap(struct session2 *s, enum protolayer_event_type event, void *baton) { bool cont; - struct protolayer_manager *m = s->layers; - for (ssize_t i = m->num_layers - 1; i >= 0; i--) { - enum protolayer_protocol p = protolayer_grps[s->layers->grp][i]; - struct protolayer_globals *globals = &protolayer_globals[p]; + const struct protolayer_grp *grp = &protolayer_grps[s->proto]; + for (ssize_t i = grp->num_layers - 1; i >= 0; i--) { + struct protolayer_globals *globals = &protolayer_globals[grp->layers[i]]; if (globals->event_wrap) { - struct protolayer_data *sess_data = protolayer_sess_data_get(m, i); - cont = globals->event_wrap(event, &baton, m, sess_data); + struct protolayer_data *sess_data = protolayer_sess_data_get(s, i); + cont = globals->event_wrap(event, &baton, s, sess_data); } else { cont = true; } @@ -1267,13 +1257,12 @@ static void session2_event_wrap(struct session2 *s, enum protolayer_event_type e void session2_event_unwrap(struct session2 *s, ssize_t start_ix, enum protolayer_event_type event, void *baton) { bool cont; - struct protolayer_manager *m = s->layers; - for (ssize_t i = start_ix; i < m->num_layers; i++) { - enum protolayer_protocol p = protolayer_grps[s->layers->grp][i]; - struct protolayer_globals *globals = &protolayer_globals[p]; + const struct protolayer_grp *grp = &protolayer_grps[s->proto]; + for (ssize_t i = start_ix; i < grp->num_layers; i++) { + struct protolayer_globals *globals = &protolayer_globals[grp->layers[i]]; if (globals->event_unwrap) { - struct protolayer_data *sess_data = protolayer_sess_data_get(m, i); - cont = globals->event_unwrap(event, &baton, m, sess_data); + struct protolayer_data *sess_data = protolayer_sess_data_get(s, i); + cont = globals->event_unwrap(event, &baton, s, sess_data); } else { cont = true; } @@ -1296,10 +1285,10 @@ void session2_event(struct session2 *s, enum protolayer_event_type event, void * session2_event_unwrap(s, 0, event, baton); } -void session2_event_after(struct session2 *s, enum protolayer_protocol protocol, +void session2_event_after(struct session2 *s, enum protolayer_type protocol, enum protolayer_event_type event, void *baton) { - ssize_t start_ix = protolayer_manager_get_protocol(s->layers, protocol); + ssize_t start_ix = session2_get_protocol(s, protocol); if (kr_fails_assert(start_ix >= 0)) return; session2_event_unwrap(s, start_ix + 1, event, baton); @@ -1307,13 +1296,12 @@ void session2_event_after(struct session2 *s, enum protolayer_protocol protocol, void session2_init_request(struct session2 *s, struct kr_request *req) { - struct protolayer_manager *m = s->layers; - for (ssize_t i = 0; i < m->num_layers; i++) { - enum protolayer_protocol p = protolayer_grps[s->layers->grp][i]; - struct protolayer_globals *globals = &protolayer_globals[p]; + const struct protolayer_grp *grp = &protolayer_grps[s->proto]; + for (ssize_t i = 0; i < grp->num_layers; i++) { + struct protolayer_globals *globals = &protolayer_globals[grp->layers[i]]; if (globals->request_init) { - struct protolayer_data *sess_data = protolayer_sess_data_get(m, i); - globals->request_init(m, req, sess_data); + struct protolayer_data *sess_data = protolayer_sess_data_get(s, i); + globals->request_init(s, req, sess_data); } } } @@ -1429,27 +1417,30 @@ static int session2_transport_pushv(struct session2 *s, .baton = baton, .comm = comm }; + int err_ret = kr_ok(); switch (s->transport.type) { case SESSION2_TRANSPORT_IO:; uv_handle_t *handle = s->transport.io.handle; if (kr_fails_assert(handle)) { - if (cb) - cb(kr_error(EINVAL), s, comm, baton); - free(ctx); - return kr_error(EINVAL); + err_ret = kr_error(EINVAL); + goto exit_err; } if (handle->type == UV_UDP) { if (ENABLE_SENDMMSG && !s->outgoing) { int fd; int ret = uv_fileno(handle, &fd); - if (kr_fails_assert(!ret)) - return kr_error(EIO); + if (kr_fails_assert(!ret)) { + err_ret = kr_error(EIO); + goto exit_err; + } /* TODO: support multiple iovecs properly? */ - if (kr_fails_assert(iovcnt == 1)) - return kr_error(EINVAL); + if (kr_fails_assert(iovcnt == 1)) { + err_ret = kr_error(EINVAL); + goto exit_err; + } session2_transport_pushv_ensure_long_lived( &iov, &iovcnt, iov_short_lived, @@ -1496,12 +1487,16 @@ static int session2_transport_pushv(struct session2 *s, #if ENABLE_XDP } else if (handle->type == UV_POLL) { xdp_handle_data_t *xhd = handle->data; - if (kr_fails_assert(xhd && xhd->socket)) - return kr_error(EIO); + if (kr_fails_assert(xhd && xhd->socket)) { + err_ret = kr_error(EIO); + goto exit_err; + } /* TODO: support multiple iovecs properly? */ - if (kr_fails_assert(iovcnt == 1)) - return kr_error(EINVAL); + if (kr_fails_assert(iovcnt == 1)) { + err_ret = kr_error(EINVAL); + goto exit_err; + } session2_transport_pushv_ensure_long_lived( &iov, &iovcnt, iov_short_lived, @@ -1534,29 +1529,31 @@ static int session2_transport_pushv(struct session2 *s, #endif } else { kr_assert(false && "Unsupported handle"); - if (cb) - cb(kr_error(EINVAL), s, comm, baton); - free(ctx); - return kr_error(EINVAL); + err_ret = kr_error(EINVAL); + goto exit_err; } case SESSION2_TRANSPORT_PARENT:; struct session2 *parent = s->transport.parent; if (kr_fails_assert(parent)) { - free(ctx); - return kr_error(EINVAL); + err_ret = kr_error(EINVAL); + goto exit_err; } int ret = session2_wrap(parent, - protolayer_iovec(iov, iovcnt, iov_short_lived), + protolayer_payload_iovec(iov, iovcnt, iov_short_lived), comm, session2_transport_parent_pushv_finished, ctx); return (ret < 0) ? ret : kr_ok(); default: kr_assert(false && "Invalid transport"); - free(ctx); - return kr_error(EINVAL); + err_ret = kr_error(EINVAL); + goto exit_err; } + +exit_err: + session2_transport_pushv_finished(err_ret, ctx); + return err_ret; } struct push_ctx { diff --git a/daemon/session2.h b/daemon/session2.h index 426f9b1f..dc602cca 100644 --- a/daemon/session2.h +++ b/daemon/session2.h @@ -2,8 +2,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -/* HINT: If you are looking to implement a new protocol, start with the doc - * comment of the `PROTOLAYER_PROTOCOL_MAP` macro and continue from there. */ +/* HINT: If you are looking to implement support for a new transport protocol, + * start with the doc comment of the `PROTOLAYER_TYPE_MAP` macro and + * continue from there. */ /* GLOSSARY: * @@ -21,6 +22,13 @@ * processing, it is also the lifetime of `struct protolayer_iter_ctx` and * layer-specific data contained therein. * + * Layer sequence return function: + * - One of `protolayer_break()`, `protolayer_continue()`, or + * `protolayer_async()` - a function that a protolayer's `_wrap` or `_unwrap` + * callback should call to get its return value. They may either be called + * synchronously directly in the callback to end/pause the processing, or, if + * the processing went asynchronous, called to resume the iteration of layers. + * * Payload: * - Data processed by protocol layers in a particular sequence. In the wrap * direction, this data generally starts as a DNS packet, which is then @@ -29,25 +37,26 @@ * is retrieved. * * Protocol layer: - * - An implementation of a particular protocol. A layer transforms payloads - * to conform to a particular protocol, e.g. UDP, TCP, TLS, HTTP, QUIC, etc. - * While transforming a payload, a layer may also modify metadata - e.g. the - * UDP and TCP layers in the Unwrap direction implement the PROXYv2 protocol, - * using which they retrieve the IP address of the actual originating client - * and store it in the appropriate struct. + * - Not to be confused with `struct kr_layer_api`. An implementation of a + * particular protocol. A protocol layer transforms payloads to conform to a + * particular protocol, e.g. UDP, TCP, TLS, HTTP, QUIC, etc. While + * transforming a payload, a layer may also modify metadata - e.g. the UDP and + * TCP layers in the Unwrap direction implement the PROXYv2 protocol, using + * which they retrieve the IP address of the actual originating client and + * store it in the appropriate struct. * * Protolayer: - * - Same as 'Protocol layer'. + * - Short for 'protocol layer'. * * Unwrap: - * - The direction of data transformation, starting with the transport (e.g. - * data that came from the network), ending with an internal subsystem (e.g. - * DNS query resolution). + * - The direction of data transformation, which starts with the transport + * (e.g. bytes that came from the network) and ends with an internal subsystem + * (e.g. DNS query resolution). * * Wrap: - * - The direction of data transformation, starting with an internal - * subsystem (e.g. an answer to a resolved DNS query), ending with the - * transport (e.g. data that is going to be sent to the client). */ + * - The direction of data transformation, which starts with an internal + * subsystem (e.g. an answer to a resolved DNS query) and ends with the + * transport (e.g. bytes that are going to be sent to the client). */ #pragma once @@ -59,6 +68,7 @@ #include "contrib/mempattern.h" #include "lib/generic/queue.h" #include "lib/generic/trie.h" +#include "lib/proto.h" #include "lib/utils.h" /* Forward declarations */ @@ -102,17 +112,26 @@ struct comm_info { bool xdp:1; }; +/** Just a simple struct able to hold three IPv6 or IPv4 addresses, so that we + * can hold them somewhere. */ +struct comm_addr_storage { + union kr_sockaddr src_addr; + union kr_sockaddr comm_addr; + union kr_sockaddr dst_addr; +}; + -/** A buffer, with indices marking the chunk containing as of yet unprocessed - * data - this chunk is called "valid". The contents may be manipulated using - * `wire_buf_` functions, which ensure the struct's validity. +/** A buffer control struct, with indices marking a chunk containing received + * but as of yet unprocessed data - the data in this chunk is called "valid + * data". The struct may be manipulated using `wire_buf_` functions, which + * contain bounds checks to ensure correct behaviour. * - * The struct may be used to retrieve data piecewise, e.g. from a stream-based - * transport like TCP, by writing data to the buffer's free space, then + * The struct may be used to retrieve data piecewise (e.g. from a stream-based + * transport like TCP) by writing data to the buffer's free space, then * "consuming" that space with `wire_buf_consume`. It can also be handy for * processing message headers, then trimming the beginning of the buffer (using - * `wire_buf_trim`) so that the next part of the data may be processed by a - * next part of a common pipeline. + * `wire_buf_trim`) so that the next part of the data may be processed by + * another part of a pipeline. * * May be initialized in two possible ways: * - via `wire_buf_init` @@ -155,7 +174,7 @@ int wire_buf_trim(struct wire_buf *wb, size_t length); /** Moves the valid bytes of the buffer to the buffer's beginning. */ int wire_buf_movestart(struct wire_buf *wb); -/** Resets the valid bytes of the buffer to zero. */ +/** Marks the wire buffer as empty. */ int wire_buf_reset(struct wire_buf *wb); /** Gets a pointer to the data marked as valid in the wire buffer. */ @@ -188,7 +207,7 @@ static inline size_t wire_buf_free_space_length(const struct wire_buf *wb) /** Protocol layer types map - an enumeration of individual protocol layer * implementations * - * This macro is used to generate `enum protolayer_protocol` as well as other + * This macro is used to generate `enum protolayer_type` as well as other * additional data on protocols, e.g. name string constants. * * To define a new protocol, add a new identifier to this macro, and, within @@ -200,14 +219,18 @@ static inline size_t wire_buf_free_space_length(const struct wire_buf *wb) * * To use protocols within sessions, protocol layer groups also need to be * defined, to indicate the order in which individual protocols are to be - * processed. See `PROTOLAYER_GRP_MAP` below for more details. */ -#define PROTOLAYER_PROTOCOL_MAP(XX) \ + * processed. See `KR_PROTO_MAP` below for more details. */ +#define PROTOLAYER_TYPE_MAP(XX) \ /* General transport protocols */\ XX(UDP)\ XX(TCP)\ XX(TLS)\ XX(HTTP)\ \ + /* PROXYv2 */\ + XX(PROXYV2_DGRAM)\ + XX(PROXYV2_STREAM)\ + \ /* DNS (`worker`) */\ XX(DNS_DGRAM) /**< Packets WITHOUT prepended size, one per (un)wrap, * limited to UDP sizes, multiple sources (single @@ -220,54 +243,16 @@ static inline size_t wire_buf_free_space_length(const struct wire_buf *wb) * stream (may span multiple (un)wraps). */ /** The identifiers of protocol layer types. */ -enum protolayer_protocol { - PROTOLAYER_PROTOCOL_NULL = 0, -#define XX(cid) PROTOLAYER_PROTOCOL_ ## cid, - PROTOLAYER_PROTOCOL_MAP(XX) +enum protolayer_type { + PROTOLAYER_TYPE_NULL = 0, +#define XX(cid) PROTOLAYER_TYPE_ ## cid, + PROTOLAYER_TYPE_MAP(XX) #undef XX - PROTOLAYER_PROTOCOL_COUNT /* must be the last! */ + PROTOLAYER_TYPE_COUNT /* must be the last! */ }; /** Gets the constant string name of the specified protocol. */ -const char *protolayer_protocol_name(enum protolayer_protocol p); - -/** Protocol layer group map - * - * This macro is used to generate `enum protolayer_grp` as well as other - * additional data on protocol layer groups, e.g. name string constants. - * - * Each group represents a sequence of layers in the unwrap direction (wrap - * direction being the opposite). The sequence dictates the order in which - * individual layers are processed. This macro is used to generate global data - * about groups. - * - * For defining new groups, see the docs of `protolayer_grps[]` in - * `daemon/session2.h`. - * - * TODO: probably unify enum protolayer_grp with enum kr_proto. - * - * Parameters for XX are: - * 1. Constant name (for e.g. PROTOLAYER_GRP_* enum value identifiers) - * 2. Variable name (for e.g. protolayer_grp_* array identifiers - defined in - * `session2.c`) - * 3. Human-readable name for logging */ -#define PROTOLAYER_GRP_MAP(XX) \ - XX(DOUDP, doudp, "DNS UDP") \ - XX(DOTCP, dotcp, "DNS TCP") \ - XX(DOTLS, dot, "DNS-over-TLS") \ - XX(DOHTTPS, doh, "DNS-over-HTTPS") - -/** The identifiers of pre-defined protocol layer sequences. */ -enum protolayer_grp { - PROTOLAYER_GRP_NULL = 0, -#define XX(cid, vid, name) PROTOLAYER_GRP_ ## cid, - PROTOLAYER_GRP_MAP(XX) -#undef XX - PROTOLAYER_GRP_COUNT -}; - -/** Gets the constant string name of the specified protocol layer group. */ -const char *protolayer_grp_name(enum protolayer_grp g); +const char *protolayer_layer_name(enum protolayer_type p); /** Flow control indicators for protocol layer `wrap` and `unwrap` callbacks. * Use via `protolayer_continue`, `protolayer_break`, and `protolayer_push` @@ -372,7 +357,7 @@ const char *protolayer_event_name(enum protolayer_event_type e); * valid. */ enum protolayer_payload_type { PROTOLAYER_PAYLOAD_NULL = 0, -#define XX(cid, name) PROTOLAYER_PAYLOAD_ ## cid, +#define XX(cid, name) PROTOLAYER_PAYLOAD_##cid, PROTOLAYER_PAYLOAD_MAP(XX) #undef XX PROTOLAYER_PAYLOAD_COUNT @@ -391,9 +376,17 @@ struct protolayer_payload { /** Time-to-live hint (e.g. for HTTP Cache-Control) */ unsigned int ttl; - /** If `true`, the payload's memory may be freed early as kresd does not - * completely control its lifetime. When going asynchronous, it needs to - * be copied. */ + /** If `true`, signifies that the memory this payload points to may + * become invalid when we return from one of the functions in the + * current stack. That is fine as long as all the protocol layer + * processing for this payload takes place in a single `session2_wrap()` + * or `session2_unwrap()` call, but may become a problem, when a layer + * goes asynchronous (via `protolayer_async()`). + * + * Setting this to `true` will ensure that the payload will get copied + * into a separate memory buffer if and only if a layer goes + * asynchronous. It makes sure that if all processing for the payload is + * synchronous, no copies or reallocations for the payload are done. */ bool short_lived; union { @@ -418,28 +411,43 @@ struct protolayer_payload { * layer-specific data, and internal information for the protocol layer * manager. */ struct protolayer_iter_ctx { -/* read-write: */ +/* read-write for layers: */ /** The payload */ struct protolayer_payload payload; - /** Communication information. Typically written into by one of the - * first layers facilitating transport protocol processing. */ - struct comm_info comm; + /** Pointer to communication information. For TCP, this will generally + * point to the storage in the session. For UDP, this will generally + * point to the storage in this context. */ + struct comm_info *comm; + /** Communication information storage. This will generally be set by one + * of the first layers in the sequence, if used, e.g. UDP PROXYv2. */ + struct comm_info comm_storage; + struct comm_addr_storage comm_addr_storage; + /** Per-iter memory pool. Has no `free` procedure, gets freed as a whole + * when the context is being destroyed. Initialized and destroyed + * automatically - layers may use it to allocate memory. */ + knot_mm_t pool; -/* callback for when the layer iteration has ended - read-only: */ +/* callback for when the layer iteration has ended - read-only for layers: */ protolayer_finished_cb finished_cb; void *finished_cb_baton; -/* internal information for the manager - private: */ +/* internal information for the manager - should only be used by the protolayer + * system, never by layers: */ enum protolayer_direction direction; + /** If `true`, the processing of the layer sequence has been paused and + * is waiting to be resumed (`protolayer_continue()`) or cancelled + * (`protolayer_break()`). */ bool async_mode; + /** The index of the layer that is currently being (or has just been) + * processed. */ unsigned int layer_ix; - struct protolayer_manager *manager; + struct session2 *session; + /** Status passed to the finish callback. */ int status; enum protolayer_iter_action action; - void *async_buffer; /** Contains a sequence of variably-sized CPU-aligned layer-specific - * structs. See `struct protolayer_manager::data`. */ + * structs. See `struct session2::layer_data` for details. */ alignas(CPU_STRUCT_ALIGN) char data[]; }; @@ -455,8 +463,8 @@ size_t protolayer_payload_copy(void *dest, size_t max_len); /** Convenience function to get a buffer-type payload. */ -static inline struct protolayer_payload protolayer_buffer(void *buf, size_t len, - bool short_lived) +static inline struct protolayer_payload protolayer_payload_buffer( + void *buf, size_t len, bool short_lived) { return (struct protolayer_payload){ .type = PROTOLAYER_PAYLOAD_BUFFER, @@ -469,7 +477,7 @@ static inline struct protolayer_payload protolayer_buffer(void *buf, size_t len, } /** Convenience function to get an iovec-type payload. */ -static inline struct protolayer_payload protolayer_iovec( +static inline struct protolayer_payload protolayer_payload_iovec( struct iovec *iov, int iovcnt, bool short_lived) { return (struct protolayer_payload){ @@ -483,7 +491,7 @@ static inline struct protolayer_payload protolayer_iovec( } /** Convenience function to get a wire-buf-type payload. */ -static inline struct protolayer_payload protolayer_wire_buf( +static inline struct protolayer_payload protolayer_payload_wire_buf( struct wire_buf *wire_buf, bool short_lived) { return (struct protolayer_payload){ @@ -500,7 +508,8 @@ static inline struct protolayer_payload protolayer_wire_buf( * If the input payload is `_WIRE_BUF`, the pointed-to wire buffer is reset to * indicate that all of its contents have been used up, and the buffer is ready * to be reused. */ -struct protolayer_payload protolayer_as_buffer(const struct protolayer_payload *payload); +struct protolayer_payload protolayer_payload_as_buffer( + const struct protolayer_payload *payload); /** A predefined queue type for iteration context. */ typedef queue_t(struct protolayer_iter_ctx *) protolayer_iter_ctx_queue_t; @@ -521,8 +530,8 @@ struct protolayer_data { }; /** Return value of `protolayer_iter_cb` callbacks. To be returned by *layer - * sequence return functions* as a sanity check. Not to be used directly by - * user code. */ + * sequence return functions* (see glossary) as a sanity check. Not to be used + * directly by user code. */ enum protolayer_iter_cb_result { PROTOLAYER_ITER_CB_RESULT_MAGIC = 0x364F392E, }; @@ -534,9 +543,9 @@ enum protolayer_iter_cb_result { * * The function (or another function, that the pointed-to function causes to be * called, directly or through an asynchronous operation), must call one of the - * *layer sequence return functions* (e.g. `protolayer_continue()`, - * `protolayer_async()`, ...) to advance (or end) the layer sequence. The - * function must return the result of such a return function. */ + * *layer sequence return functions* (see glossary) to advance (or end) the + * layer sequence. The function must return the result of such a return + * function. */ typedef enum protolayer_iter_cb_result (*protolayer_iter_cb)( void *sess_data, void *iter_data, @@ -561,7 +570,7 @@ enum protolayer_event_cb_result { * stops. */ typedef enum protolayer_event_cb_result (*protolayer_event_cb)( enum protolayer_event_type event, void **baton, - struct protolayer_manager *manager, void *sess_data); + struct session2 *session, void *sess_data); /** Function type for initialization callbacks of layer session data. * @@ -573,9 +582,8 @@ typedef enum protolayer_event_cb_result (*protolayer_event_cb)( * * Returning 0 means success, other return values mean error and halt the * initialization. */ -typedef int (*protolayer_data_sess_init_cb)(struct protolayer_manager *manager, - void *data, - void *param); +typedef int (*protolayer_data_sess_init_cb)(struct session2 *session, + void *data, void *param); /** Function type for determining the size of a layer's wire buffer overhead. */ typedef size_t (*protolayer_wire_buf_overhead_cb)(bool outgoing); @@ -588,8 +596,7 @@ typedef size_t (*protolayer_wire_buf_overhead_cb)(bool outgoing); * * Returning 0 means success, other return values mean error and halt the * initialization. */ -typedef int (*protolayer_iter_data_cb)(struct protolayer_manager *manager, - struct protolayer_iter_ctx *ctx, +typedef int (*protolayer_iter_data_cb)(struct protolayer_iter_ctx *ctx, void *data); /** Function type for (de)initialization callbacks of layers. @@ -598,57 +605,19 @@ typedef int (*protolayer_iter_data_cb)(struct protolayer_manager *manager, * * Returning 0 means success, other return values mean error and halt the * initialization. */ -typedef int (*protolayer_data_cb)(struct protolayer_manager *manager, - void *data); +typedef int (*protolayer_data_cb)(struct session2 *session, void *data); /** Function type for (de)initialization callbacks of DNS requests. * * `req` points to the request for initialization. * `sess_data` points to layer-specific session data struct. */ -typedef void (*protolayer_request_cb)(struct protolayer_manager *manager, +typedef void (*protolayer_request_cb)(struct session2 *session, struct kr_request *req, void *sess_data); -/** A collection of protocol layers and their layer-specific data, tied to a - * session. The manager contains a sequence of protocol layers (determined by - * `grp`), which define how the data processed by the session is to be - * interpreted. */ -struct protolayer_manager { - enum protolayer_grp grp; - struct wire_buf wire_buf; - size_t wire_buf_max_length; - struct session2 *session; - size_t num_layers; - size_t cb_ctx_size; /**< Size of a single callback context, including - * layer-specific per-iteration data. */ - - /** The following flexible array has basically this structure: - * - * struct { - * size_t sess_offsets[num_layers]; - * size_t iter_offsets[num_layers]; - * variably-sized-data sess_data[num_layers]; - * } - * - * It is done this way, because different layer groups will have - * different numbers of layers and differently-sized layer-specific - * data. C does not have a convenient way to define this in structs, so - * we do it via this flexible array. - * - * `sess_data` is a sequence of variably-sized CPU-aligned - * layer-specific structs. - * - * `sess_offsets` determines data offsets in `sess_data` for pointer - * retrieval. - * - * `iter_offsets` determines data offsets in `struct - * protolayer_iter_ctx::data` for pointer retrieval. */ - alignas(CPU_STRUCT_ALIGN) char data[]; -}; - /** Initialization parameters for protocol layer session data. */ struct protolayer_data_param { - enum protolayer_protocol protocol; /**< Which protocol these parameters + enum protolayer_type protocol; /**< Which protocol these parameters * are meant for. */ void *param; /**< Pointer to protolayer-related initialization * parameters. Only needs to be valid during session @@ -740,30 +709,30 @@ struct protolayer_globals { protolayer_request_cb request_init; }; -/** Global data about layered protocols. Mapped by `enum protolayer_protocol`. +/** Global data about layered protocols. Mapped by `enum protolayer_type`. * Individual protocols are to be initialized during resolver startup. */ -extern struct protolayer_globals protolayer_globals[PROTOLAYER_PROTOCOL_COUNT]; +extern struct protolayer_globals protolayer_globals[PROTOLAYER_TYPE_COUNT]; -/** *Layer sequence return function* - signalizes the protolayer manager to - * continue processing the next layer. */ +/** *Layer sequence return function* (see glossary) - signalizes the protolayer + * manager to continue processing the next layer. */ enum protolayer_iter_cb_result protolayer_continue(struct protolayer_iter_ctx *ctx); -/** *Layer sequence return function* - signalizes that the layer wants to stop - * processing of the buffer and clean up, possibly due to an error (indicated - * by a non-zero `status`). */ +/** *Layer sequence return function* (see glossary) - signalizes that the layer + * wants to stop processing of the buffer and clean up, possibly due to an error + * (indicated by a non-zero `status`). */ enum protolayer_iter_cb_result protolayer_break(struct protolayer_iter_ctx *ctx, int status); -/** *Layer sequence return function* - signalizes that the current sequence - * will continue in an asynchronous manner. The layer should store the context - * and call another sequence return function at another point. This may be used - * in layers that work through libraries whose operation is asynchronous, like - * GnuTLS. +/** *Layer sequence return function* (see glossary) - signalizes that the + * current sequence will continue in an asynchronous manner. The layer should + * store the context and call another sequence return function at another point. + * This may be used in layers that work through libraries whose operation is + * asynchronous, like GnuTLS. * - * Note that this return function is just a readability hint - another return - * function may be called in another stack frame before it (generally during a - * call to an external library function, e.g. GnuTLS or nghttp2) and the - * sequence will continue correctly. */ + * Note that this one is basically just a readability hint - another return + * function may be actually called before it (generally during a call to an + * external library function, e.g. GnuTLS or nghttp2). This is completely legal + * and the sequence will continue correctly. */ static inline enum protolayer_iter_cb_result protolayer_async(void) { return PROTOLAYER_ITER_CB_RESULT_MAGIC; @@ -780,21 +749,22 @@ enum session2_transport_type { /** A data unit for a single sequential data source. The data may be organized * as a stream or a sequence of datagrams - this is up to the actual individual - * protocols used by the session, as defined by the `layers` member - see - * `struct protolayer_manager` and the types of its members for more info. + * protocols used by the session - see `enum kr_proto` and + * `protolayer_`-prefixed types and functions for more information. * * A session processes data in two directions: * * - `_UNWRAP` deals with raw data received from the session's transport. It - * strips the ceremony of individual protocols from the buffers. The last - * (bottommost) layer is generally responsible for submitting the unwrapped - * data to be processed by an internal system, e.g. to be resolved as a DNS - * query. + * strips the ceremony of individual protocols from the buffers, retaining any + * required metadata in an iteration context (`struct protolayer_iter_ctx`). + * The last layer (as defined by a `protolayer_grp_*` array in `session2.c`) in + * a sequence is generally responsible for submitting the unwrapped data to be + * processed by an internal system, e.g. to be resolved as a DNS query. * * - `_WRAP` deals with data generated by an internal system. It adds the - * required protocol ceremony to it (e.g. encryption). The first (topmost) - * layer is responsible for preparing the data to be sent through the - * session's transport. */ + * required protocol ceremony to it (e.g. encryption). The first layer (as + * defined by a `protolayer_grp_*` array in `session2.c`) is responsible for + * preparing the data to be sent through the session's transport. */ struct session2 { /** Data for sending data out in the `wrap` direction and receiving new * data in the `unwrap` direction. */ @@ -814,14 +784,12 @@ struct session2 { }; } transport; - struct protolayer_manager *layers; /**< Protocol layers of this session. */ - knot_mm_t pool; uv_timer_t timer; /**< For session-wide timeout events. */ enum protolayer_event_type timer_event; /**< The event fired on timeout. */ trie_t *tasks; /**< List of tasks associated with given session. */ queue_t(struct qr_task *) waiting; /**< List of tasks waiting for * sending to upstream. */ - + struct wire_buf wire_buf; uint32_t log_id; /**< Session ID for logging. */ int uv_count; /**< Number of unclosed libUV handles owned by this @@ -830,7 +798,7 @@ struct session2 { /** Communication information. Typically written into by one of the * first layers facilitating transport protocol processing. * Zero-initialized by default. */ - struct comm_info comm; + struct comm_info comm_storage; /** Time of last IO activity (if any occurs). Otherwise session * creation time. */ @@ -866,6 +834,37 @@ struct session2 { /** If true, session is being rate-limited. One of the protocol layers * is going to be the writer for this flag. */ bool throttled : 1; + + /* Protocol layers */ + + /** The set of protocol layers used by this session. */ + enum kr_proto proto; + /** The size of a single iteration context + * (`struct protolayer_iter_ctx`), including layer-specific data. */ + size_t iter_ctx_size; + + /** The following flexible array has basically this structure: + * + * struct { + * size_t sess_offsets[num_layers]; + * size_t iter_offsets[num_layers]; + * variably-sized-data sess_data[num_layers]; + * } + * + * It is done this way, because different layer groups will have + * different numbers of layers and differently-sized layer-specific + * data. C does not have a convenient way to define this in structs, so + * we do it via this flexible array. + * + * `sess_data` is a sequence of variably-sized CPU-aligned + * layer-specific structs. + * + * `sess_offsets` determines data offsets in `sess_data` for pointer + * retrieval. + * + * `iter_offsets` determines data offsets in `struct + * protolayer_iter_ctx::data` for pointer retrieval. */ + alignas(CPU_STRUCT_ALIGN) char layer_data[]; }; /** Allocates and initializes a new session with the specified protocol layer @@ -877,7 +876,7 @@ struct session2 { * individual layer implementations to determine the lifetime of the data * pointed to by the parameters. */ struct session2 *session2_new(enum session2_transport_type transport_type, - enum protolayer_grp layer_grp, + enum kr_proto proto, struct protolayer_data_param *layer_param, size_t layer_param_count, bool outgoing); @@ -885,7 +884,7 @@ struct session2 *session2_new(enum session2_transport_type transport_type, /** Allocates and initializes a new session with the specified protocol layer * group, using a *libuv handle* as its transport. */ static inline struct session2 *session2_new_io(uv_handle_t *handle, - enum protolayer_grp layer_grp, + enum kr_proto layer_grp, struct protolayer_data_param *layer_param, size_t layer_param_count, bool outgoing) @@ -901,7 +900,7 @@ static inline struct session2 *session2_new_io(uv_handle_t *handle, /** Allocates and initializes a new session with the specified protocol layer * group, using a *parent session* as its transport. */ static inline struct session2 *session2_new_child(struct session2 *parent, - enum protolayer_grp layer_grp, + enum kr_proto layer_grp, struct protolayer_data_param *layer_param, size_t layer_param_count, bool outgoing) @@ -1019,7 +1018,7 @@ void session2_penalize(struct session2 *session); * indicating an error. */ int session2_unwrap(struct session2 *s, struct protolayer_payload payload, const struct comm_info *comm, protolayer_finished_cb cb, - void *baton); + void *baton); /** Same as `session2_unwrap`, but looks up the specified `protocol` in the * session's assigned protocol group and sends the `payload` to the layer that @@ -1027,7 +1026,7 @@ int session2_unwrap(struct session2 *s, struct protolayer_payload payload, * * Layers may use this to generate their own data to send in the sequence, e.g. * for protocol-specific ceremony. */ -int session2_unwrap_after(struct session2 *s, enum protolayer_protocol protocol, +int session2_unwrap_after(struct session2 *s, enum protolayer_type protocol, struct protolayer_payload payload, const struct comm_info *comm, protolayer_finished_cb cb, void *baton); @@ -1056,7 +1055,7 @@ int session2_wrap(struct session2 *s, struct protolayer_payload payload, * * Layers may use this to generate their own data to send in the sequence, e.g. * for protocol-specific ceremony. */ -int session2_wrap_after(struct session2 *s, enum protolayer_protocol protocol, +int session2_wrap_after(struct session2 *s, enum protolayer_type protocol, struct protolayer_payload payload, const struct comm_info *comm, protolayer_finished_cb cb, void *baton); @@ -1074,7 +1073,7 @@ void session2_event(struct session2 *s, enum protolayer_event_type event, void * * NOTE: The bounced iteration does not exclude any layers - the layer * specified by `protocol` and those before it are only skipped in the * `_UNWRAP` direction! */ -void session2_event_after(struct session2 *s, enum protolayer_protocol protocol, +void session2_event_after(struct session2 *s, enum protolayer_type protocol, enum protolayer_event_type event, void *baton); /** Sends a `PROTOLAYER_EVENT_CLOSE` event to be processed by the protocol diff --git a/daemon/tls.c b/daemon/tls.c index e8dff76c..a5169ae3 100644 --- a/daemon/tls.c +++ b/daemon/tls.c @@ -24,10 +24,10 @@ #include "daemon/worker.h" #include "daemon/session2.h" -#define EPHEMERAL_CERT_EXPIRATION_SECONDS_RENEW_BEFORE (60*60*24*7) +#define EPHEMERAL_CERT_EXPIRATION_SECONDS_RENEW_BEFORE ((time_t)60*60*24*7) #define GNUTLS_PIN_MIN_VERSION 0x030400 #define UNWRAP_BUF_SIZE 131072 -#define TLS_CHUNK_SIZE (16 * 1024) +#define TLS_CHUNK_SIZE ((size_t)16 * 1024) #define VERBOSE_MSG(cl_side, ...)\ if (cl_side) \ @@ -35,9 +35,9 @@ else \ kr_log_debug(TLS, __VA_ARGS__); -static const gnutls_datum_t tls_grp_alpn[PROTOLAYER_GRP_COUNT] = { - [PROTOLAYER_GRP_DOTLS] = { (uint8_t *)"dot", 3 }, - [PROTOLAYER_GRP_DOHTTPS] = { (uint8_t *)"h2", 2 }, +static const gnutls_datum_t tls_grp_alpn[KR_PROTO_COUNT] = { + [KR_PROTO_DOT] = { (uint8_t *)"dot", 3 }, + [KR_PROTO_DOH] = { (uint8_t *)"h2", 2 }, }; typedef enum tls_client_hs_state { @@ -218,6 +218,11 @@ static ssize_t kres_gnutls_vec_push(gnutls_transport_ptr_t h, const giovec_t * i return 0; } + if (kr_fails_assert(iovcnt > 0)) { + errno = EINVAL; + return -1; + } + size_t total_len = 0; for (int i = 0; i < iovcnt; i++) total_len += iov[i].iov_len; @@ -228,9 +233,9 @@ static ssize_t kres_gnutls_vec_push(gnutls_transport_ptr_t h, const giovec_t * i push_ctx->sess_data = tls; memcpy(push_ctx->iov, iov, sizeof(struct iovec[iovcnt])); - session2_wrap_after(tls->h.session, PROTOLAYER_PROTOCOL_TLS, - protolayer_iovec(push_ctx->iov, iovcnt, true), NULL, - kres_gnutls_push_finished, push_ctx); + session2_wrap_after(tls->h.session, PROTOLAYER_TYPE_TLS, + protolayer_payload_iovec(push_ctx->iov, iovcnt, true), + NULL, kres_gnutls_push_finished, push_ctx); return total_len; } @@ -260,7 +265,7 @@ static void tls_handshake_success(struct pl_tls_sess_data *tls, } } if (!tls->first_handshake_done) { - session2_event_after(session, PROTOLAYER_PROTOCOL_TLS, + session2_event_after(session, PROTOLAYER_TYPE_TLS, PROTOLAYER_EVENT_CONNECT, NULL); tls->first_handshake_done = true; } @@ -442,7 +447,7 @@ static int str_replace(char **where_ptr, const char *with) return kr_ok(); } -static time_t _get_end_entity_expiration(gnutls_certificate_credentials_t creds) +static time_t get_end_entity_expiration(gnutls_certificate_credentials_t creds) { gnutls_datum_t data; gnutls_x509_crt_t cert = NULL; @@ -514,7 +519,7 @@ int tls_certificate_set(const char *tls_cert, const char *tls_key) return kr_error(EINVAL); } /* record the expiration date: */ - tls_credentials->valid_until = _get_end_entity_expiration(tls_credentials->credentials); + tls_credentials->valid_until = get_end_entity_expiration(tls_credentials->credentials); /* Exchange the x509 credentials */ struct tls_credentials *old_credentials = the_network->tls_credentials; @@ -889,7 +894,7 @@ static int pl_tls_sess_data_deinit(struct pl_tls_sess_data *tls) return kr_ok(); } -static int pl_tls_sess_server_init(struct protolayer_manager *manager, +static int pl_tls_sess_server_init(struct session2 *session, struct pl_tls_sess_data *tls) { if (kr_fails_assert(the_worker && the_engine)) @@ -967,7 +972,7 @@ static int pl_tls_sess_server_init(struct protolayer_manager *manager, tls->tls_session); } - const gnutls_datum_t *alpn = &tls_grp_alpn[manager->grp]; + const gnutls_datum_t *alpn = &tls_grp_alpn[session->proto]; if (alpn->size) { /* ALPN is a non-empty string */ flags = 0; #if GNUTLS_VERSION_NUMBER >= 0x030500 @@ -987,7 +992,7 @@ static int pl_tls_sess_server_init(struct protolayer_manager *manager, return kr_ok(); } -static int pl_tls_sess_client_init(struct protolayer_manager *manager, +static int pl_tls_sess_client_init(struct session2 *session, struct pl_tls_sess_data *tls, tls_client_param_t *param) { @@ -1042,21 +1047,21 @@ static int pl_tls_sess_client_init(struct protolayer_manager *manager, return kr_ok(); } -static int pl_tls_sess_init(struct protolayer_manager *manager, +static int pl_tls_sess_init(struct session2 *session, void *sess_data, void *param) { struct pl_tls_sess_data *tls = sess_data; - manager->session->secure = true; + session->secure = true; queue_init(tls->unwrap_queue); queue_init(tls->wrap_queue); - if (manager->session->outgoing) - return pl_tls_sess_client_init(manager, tls, param); + if (session->outgoing) + return pl_tls_sess_client_init(session, tls, param); else - return pl_tls_sess_server_init(manager, tls); + return pl_tls_sess_server_init(session, tls); } -static int pl_tls_sess_deinit(struct protolayer_manager *manager, +static int pl_tls_sess_deinit(struct session2 *session, void *sess_data) { return pl_tls_sess_data_deinit(sess_data); @@ -1067,7 +1072,7 @@ static enum protolayer_iter_cb_result pl_tls_unwrap(void *sess_data, void *iter_ { int brstatus = kr_ok(); struct pl_tls_sess_data *tls = sess_data; - struct session2 *s = ctx->manager->session; + struct session2 *s = ctx->session; queue_push(tls->unwrap_queue, ctx); @@ -1160,7 +1165,7 @@ static enum protolayer_iter_cb_result pl_tls_unwrap(void *sess_data, void *iter_ struct protolayer_iter_ctx *ctx_head = queue_head(tls->unwrap_queue); if (!kr_fails_assert(ctx == ctx_head)) queue_pop(tls->unwrap_queue); - ctx->payload = protolayer_wire_buf(&tls->unwrap_buf, false); + ctx->payload = protolayer_payload_wire_buf(&tls->unwrap_buf, false); return protolayer_continue(ctx); exit_break: @@ -1174,7 +1179,7 @@ static ssize_t pl_tls_submit(gnutls_session_t tls_session, struct protolayer_payload payload) { if (payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF) - payload = protolayer_as_buffer(&payload); + payload = protolayer_payload_as_buffer(&payload); if (payload.type == PROTOLAYER_PAYLOAD_BUFFER) { ssize_t count = gnutls_record_send(tls_session, @@ -1278,9 +1283,8 @@ static enum protolayer_event_cb_result pl_tls_client_connect_start( static enum protolayer_event_cb_result pl_tls_event_unwrap( enum protolayer_event_type event, void **baton, - struct protolayer_manager *manager, void *sess_data) + struct session2 *s, void *sess_data) { - struct session2 *s = manager->session; struct pl_tls_sess_data *tls = sess_data; if (event == PROTOLAYER_EVENT_CLOSE) { @@ -1308,7 +1312,7 @@ static enum protolayer_event_cb_result pl_tls_event_unwrap( static enum protolayer_event_cb_result pl_tls_event_wrap( enum protolayer_event_type event, void **baton, - struct protolayer_manager *manager, void *sess_data) + struct session2 *session, void *sess_data) { if (event == PROTOLAYER_EVENT_STATS_SEND_ERR) { the_worker->stats.err_tls += 1; @@ -1321,7 +1325,7 @@ static enum protolayer_event_cb_result pl_tls_event_wrap( return PROTOLAYER_EVENT_PROPAGATE; } -static void pl_tls_request_init(struct protolayer_manager *manager, +static void pl_tls_request_init(struct session2 *session, struct kr_request *req, void *sess_data) { @@ -1330,7 +1334,7 @@ static void pl_tls_request_init(struct protolayer_manager *manager, void tls_protolayers_init(void) { - protolayer_globals[PROTOLAYER_PROTOCOL_TLS] = (struct protolayer_globals){ + protolayer_globals[PROTOLAYER_TYPE_TLS] = (struct protolayer_globals){ .sess_size = sizeof(struct pl_tls_sess_data), .sess_deinit = pl_tls_sess_deinit, .wire_buf_overhead = TLS_CHUNK_SIZE, diff --git a/daemon/tls.h b/daemon/tls.h index b8cf7af6..9fd45fb6 100644 --- a/daemon/tls.h +++ b/daemon/tls.h @@ -30,7 +30,7 @@ * So it takes 2 RTT. * As we use session tickets, there are additional messages, add one RTT mode. */ -#define TLS_MAX_HANDSHAKE_TIME (KR_CONN_RTT_MAX * 3) +#define TLS_MAX_HANDSHAKE_TIME (KR_CONN_RTT_MAX * (uint64_t)3) /** Transport session (opaque). */ struct session2; diff --git a/daemon/tls_ephemeral_credentials.c b/daemon/tls_ephemeral_credentials.c index a27dcd2d..712e355b 100644 --- a/daemon/tls_ephemeral_credentials.c +++ b/daemon/tls_ephemeral_credentials.c @@ -17,19 +17,19 @@ #define EPHEMERAL_PRIVKEY_FILENAME "ephemeral_key.pem" #define INVALID_HOSTNAME "dns-over-tls.invalid" -#define EPHEMERAL_CERT_EXPIRATION_SECONDS (60*60*24*90) +#define EPHEMERAL_CERT_EXPIRATION_SECONDS ((time_t)60*60*24*90) /* This is an attempt to grab an exclusive, advisory, non-blocking * lock based on a filename. At the moment it's POSIX-only, but it * should be abstract enough of an interface to make an implementation * for non-posix systems if anyone cares. */ typedef int lock_t; -static bool _lock_is_invalid(lock_t lock) +static bool lock_is_invalid(lock_t lock) { return lock == -1; } /* a blocking lock on a given filename */ -static lock_t _lock_filename(const char *fname) +static lock_t lock_filename(const char *fname) { lock_t lockfd = open(fname, O_RDONLY|O_CREAT, 0400); if (lockfd == -1) @@ -41,9 +41,9 @@ static lock_t _lock_filename(const char *fname) } return lockfd; /* for cleanup later */ } -static void _lock_unlock(lock_t *lock, const char *fname) +static void lock_unlock(lock_t *lock, const char *fname) { - if (lock && !_lock_is_invalid(*lock)) { + if (lock && !lock_is_invalid(*lock)) { flock(*lock, LOCK_UN); close(*lock); *lock = -1; @@ -61,8 +61,8 @@ static gnutls_x509_privkey_t get_ephemeral_privkey (void) /* Take a lock to ensure that two daemons started concurrently * with a shared cache don't both create the same privkey: */ - lock = _lock_filename(EPHEMERAL_PRIVKEY_FILENAME ".lock"); - if (_lock_is_invalid(lock)) { + lock = lock_filename(EPHEMERAL_PRIVKEY_FILENAME ".lock"); + if (lock_is_invalid(lock)) { kr_log_error(TLS, "unable to lock lockfile " EPHEMERAL_PRIVKEY_FILENAME ".lock\n"); goto done; } @@ -91,7 +91,7 @@ static gnutls_x509_privkey_t get_ephemeral_privkey (void) } data.size = stat.st_size; bytes_read = read(datafd, data.data, stat.st_size); - if (bytes_read != stat.st_size) { + if (bytes_read < 0 || bytes_read != stat.st_size) { kr_log_error(TLS, "unable to read ephemeral private key\n"); goto bad_data; } @@ -141,7 +141,7 @@ static gnutls_x509_privkey_t get_ephemeral_privkey (void) } } done: - _lock_unlock(&lock, EPHEMERAL_PRIVKEY_FILENAME ".lock"); + lock_unlock(&lock, EPHEMERAL_PRIVKEY_FILENAME ".lock"); if (datafd != -1) { close(datafd); } @@ -220,7 +220,7 @@ struct tls_credentials * tls_get_ephemeral_credentials(void) if ((privkey = get_ephemeral_privkey()) == NULL) { goto failure; } - if ((cert = get_ephemeral_cert(privkey, creds->ephemeral_servicename, now - 60*15, creds->valid_until)) == NULL) { + if ((cert = get_ephemeral_cert(privkey, creds->ephemeral_servicename, now - ((time_t)60 * 15), creds->valid_until)) == NULL) { goto failure; } if ((err = gnutls_certificate_set_x509_key(creds->credentials, &cert, 1, privkey)) < 0) { diff --git a/daemon/tls_session_ticket-srv.c b/daemon/tls_session_ticket-srv.c index b1989030..26d41862 100644 --- a/daemon/tls_session_ticket-srv.c +++ b/daemon/tls_session_ticket-srv.c @@ -188,7 +188,7 @@ static void tst_key_check(uv_timer_t *timer, bool force_update) const uint64_t remain_ms = (tv_sec_next - now.tv_sec - 1) * (uint64_t)1000 + ms_until_second + 1; /* ^ +1 because we don't want to wake up half a millisecond before the epoch! */ - if (kr_fails_assert(remain_ms < (TST_KEY_LIFETIME + 1 /*rounding tolerance*/) * 1000)) + if (kr_fails_assert(remain_ms < ((uint64_t)TST_KEY_LIFETIME + 1 /*rounding tolerance*/) * 1000)) return; kr_log_debug(TLS, "session ticket: epoch %"PRIu64 ", scheduling rotation check in %"PRIu64" ms\n", diff --git a/daemon/udp_queue.c b/daemon/udp_queue.c index a03af8d7..68d67ec6 100644 --- a/daemon/udp_queue.c +++ b/daemon/udp_queue.c @@ -112,11 +112,11 @@ void udp_queue_push(int fd, const struct sockaddr *sa, char *buf, size_t buf_len /* Get a valid correct queue. */ if (fd >= state.udp_queues_len) { const int new_len = fd + 1; - state.udp_queues = realloc(state.udp_queues, - sizeof(state.udp_queues[0]) * new_len); + state.udp_queues = realloc(state.udp_queues, // NOLINT(bugprone-suspicious-realloc-usage): we just abort() below, so it's fine + sizeof(state.udp_queues[0]) * new_len); // NOLINT(bugprone-sizeof-expression): false-positive if (!state.udp_queues) abort(); memset(state.udp_queues + state.udp_queues_len, 0, - sizeof(state.udp_queues[0]) * (new_len - state.udp_queues_len)); + sizeof(state.udp_queues[0]) * (new_len - state.udp_queues_len)); // NOLINT(bugprone-sizeof-expression): false-positive state.udp_queues_len = new_len; } if (unlikely(state.udp_queues[fd] == NULL)) diff --git a/daemon/worker.c b/daemon/worker.c index 0445d027..e81255b2 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -129,7 +129,7 @@ struct worker_ctx *the_worker = NULL; /*! @internal Create a UDP/TCP handle for an outgoing AF_INET* connection. * socktype is SOCK_* */ static struct session2 *ioreq_spawn(int socktype, sa_family_t family, - enum protolayer_grp grp, + enum kr_proto grp, struct protolayer_data_param *layer_param, size_t layer_param_count) { @@ -195,7 +195,7 @@ static inline struct mempool *pool_borrow(void) { /* The implementation used to have extra caching layer, * but it didn't work well. Now it's very simple. */ - return mp_new(16 * 1024); + return mp_new((size_t)16 * 1024); } /** Return a mempool. */ static inline void pool_release(struct mempool *mp) @@ -635,9 +635,6 @@ static int qr_task_send(struct qr_task *task, struct session2 *session, int ret = 0; - if (comm == NULL) - comm = &session->comm; - if (pkt == NULL) pkt = worker_task_get_pktbuf(task); @@ -664,7 +661,7 @@ static int qr_task_send(struct qr_task *task, struct session2 *session, /* Pending '_finished' callback on current task */ qr_task_ref(task); - struct protolayer_payload payload = protolayer_buffer( + struct protolayer_payload payload = protolayer_payload_buffer( (char *)pkt->wire, pkt->size, false); payload.ttl = packet_ttl(pkt); ret = session2_wrap(session, payload, comm, qr_task_wrap_finished, task); @@ -834,7 +831,7 @@ static int transmit(struct qr_task *task) return ret; struct session2 *session = ioreq_spawn(SOCK_DGRAM, choice->sin6_family, - PROTOLAYER_GRP_DOUDP, NULL, 0); + KR_PROTO_UDP53, NULL, 0); if (!session) return kr_error(EINVAL); @@ -1090,14 +1087,14 @@ static int tcp_task_make_connection(struct qr_task *task, const struct sockaddr bool has_tls = tls_entry; if (has_tls) { struct protolayer_data_param param = { - .protocol = PROTOLAYER_PROTOCOL_TLS, + .protocol = PROTOLAYER_TYPE_TLS, .param = tls_entry }; session = ioreq_spawn(SOCK_STREAM, addr->sa_family, - PROTOLAYER_GRP_DOTLS, ¶m, 1); + KR_PROTO_DOT, ¶m, 1); } else { session = ioreq_spawn(SOCK_STREAM, addr->sa_family, - PROTOLAYER_GRP_DOTCP, NULL, 0); + KR_PROTO_TCP53, NULL, 0); } if (!session) { free(conn); @@ -1715,12 +1712,11 @@ static inline knot_pkt_t *produce_packet(uint8_t *buf, size_t buf_len) static enum protolayer_event_cb_result pl_dns_dgram_event_unwrap( enum protolayer_event_type event, void **baton, - struct protolayer_manager *manager, void *sess_data) + struct session2 *session, void *sess_data) { if (event != PROTOLAYER_EVENT_GENERAL_TIMEOUT) return PROTOLAYER_EVENT_PROPAGATE; - struct session2 *session = manager->session; if (session2_tasklist_get_len(session) != 1 || !session2_waitinglist_is_empty(session)) return PROTOLAYER_EVENT_PROPAGATE; @@ -1758,7 +1754,7 @@ static size_t pl_dns_dgram_wire_buf_overhead(bool outgoing) static enum protolayer_iter_cb_result pl_dns_dgram_unwrap( void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx) { - struct session2 *session = ctx->manager->session; + struct session2 *session = ctx->session; if (ctx->payload.type == PROTOLAYER_PAYLOAD_IOVEC) { int ret = kr_ok(); @@ -1777,7 +1773,7 @@ static enum protolayer_iter_cb_result pl_dns_dgram_unwrap( break; } - ret = worker_submit(session, &ctx->comm, pkt); + ret = worker_submit(session, ctx->comm, pkt); if (ret) break; } @@ -1795,7 +1791,7 @@ static enum protolayer_iter_cb_result pl_dns_dgram_unwrap( if (!pkt) return protolayer_break(ctx, KNOT_EMALF); - int ret = worker_submit(session, &ctx->comm, pkt); + int ret = worker_submit(session, ctx->comm, pkt); mp_flush(the_worker->pkt_pool.ctx); return protolayer_break(ctx, ret); } else if (ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF) { @@ -1811,7 +1807,7 @@ static enum protolayer_iter_cb_result pl_dns_dgram_unwrap( if (!pkt) return protolayer_break(ctx, KNOT_EMALF); - int ret = worker_submit(session, &ctx->comm, pkt); + int ret = worker_submit(session, ctx->comm, pkt); wire_buf_reset(ctx->payload.wire_buf); mp_flush(the_worker->pkt_pool.ctx); return protolayer_break(ctx, ret); @@ -1828,41 +1824,24 @@ struct pl_dns_stream_sess_data { bool connected : 1; /**< True: The stream is connected */ }; -struct pl_dns_stream_iter_data { - struct protolayer_data h; - struct { - knot_mm_t *pool; - void *mem; - } sent; -}; - -static int pl_dns_stream_sess_init(struct protolayer_manager *manager, - void *sess_data, void *param) +static int pl_dns_stream_sess_init(struct session2 *session, + void *sess_data, void *param) { /* _UNSIZED_STREAM and _MULTI_STREAM - don't forget to split if needed * at some point */ - manager->session->stream = true; + session->stream = true; return kr_ok(); } -static int pl_dns_single_stream_sess_init(struct protolayer_manager *manager, +static int pl_dns_single_stream_sess_init(struct session2 *session, void *sess_data, void *param) { - manager->session->stream = true; + session->stream = true; struct pl_dns_stream_sess_data *stream = sess_data; stream->single = true; return kr_ok(); } -static int pl_dns_stream_iter_deinit(struct protolayer_manager *manager, - struct protolayer_iter_ctx *ctx, - void *iter_data) -{ - struct pl_dns_stream_iter_data *stream = iter_data; - mm_free(stream->sent.pool, stream->sent.mem); - return kr_ok(); -} - static enum protolayer_event_cb_result pl_dns_stream_resolution_timeout( struct session2 *s) { @@ -2051,9 +2030,8 @@ static enum protolayer_event_cb_result pl_dns_stream_disconnected( static enum protolayer_event_cb_result pl_dns_stream_event_unwrap( enum protolayer_event_type event, void **baton, - struct protolayer_manager *manager, void *sess_data) + struct session2 *session, void *sess_data) { - struct session2 *session = manager->session; if (session->closing) return PROTOLAYER_EVENT_PROPAGATE; @@ -2061,10 +2039,10 @@ static enum protolayer_event_cb_result pl_dns_stream_event_unwrap( switch (event) { case PROTOLAYER_EVENT_GENERAL_TIMEOUT: - return pl_dns_stream_resolution_timeout(manager->session); + return pl_dns_stream_resolution_timeout(session); case PROTOLAYER_EVENT_CONNECT_TIMEOUT: - return pl_dns_stream_connection_fail(manager->session, + return pl_dns_stream_connection_fail(session, KR_SELECTION_TCP_CONNECT_TIMEOUT); case PROTOLAYER_EVENT_CONNECT: @@ -2074,7 +2052,7 @@ static enum protolayer_event_cb_result pl_dns_stream_event_unwrap( enum kr_selection_error err = (*baton) ? *(enum kr_selection_error *)baton : KR_SELECTION_TCP_CONNECT_FAILED; - return pl_dns_stream_connection_fail(manager->session, err); + return pl_dns_stream_connection_fail(session, err); case PROTOLAYER_EVENT_DISCONNECT: case PROTOLAYER_EVENT_CLOSE: @@ -2176,7 +2154,7 @@ static enum protolayer_iter_cb_result pl_dns_stream_unwrap( } int status = kr_ok(); - struct session2 *session = ctx->manager->session; + struct session2 *session = ctx->session; struct pl_dns_stream_sess_data *stream_sess = sess_data; struct wire_buf *wb = ctx->payload.wire_buf; @@ -2197,14 +2175,14 @@ static enum protolayer_iter_cb_result pl_dns_stream_unwrap( if (stream_sess->single && stream_sess->produced) { if (kr_log_is_debug(WORKER, NULL)) { kr_log_debug(WORKER, "Unexpected extra data from %s\n", - kr_straddr(ctx->comm.src_addr)); + kr_straddr(ctx->comm->src_addr)); } status = KNOT_EMALF; goto exit; } stream_sess->produced = true; - int ret = worker_submit(session, &ctx->comm, pkt); + int ret = worker_submit(session, ctx->comm, pkt); /* Errors from worker_submit() are intentionally *not* handled * in order to ensure the entire wire buffer is processed. */ @@ -2240,18 +2218,12 @@ struct sized_iovs { static enum protolayer_iter_cb_result pl_dns_stream_wrap( void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx) { - struct pl_dns_stream_iter_data *stream = iter_data; - struct session2 *s = ctx->manager->session; - - if (kr_fails_assert(!stream->sent.mem)) - return protolayer_break(ctx, kr_error(EINVAL)); - if (ctx->payload.type == PROTOLAYER_PAYLOAD_BUFFER) { if (kr_fails_assert(ctx->payload.buffer.len <= UINT16_MAX)) return protolayer_break(ctx, kr_error(EMSGSIZE)); const int iovcnt = 2; - struct sized_iovs *siov = mm_alloc(&s->pool, + struct sized_iovs *siov = mm_alloc(&ctx->pool, sizeof(*siov) + iovcnt * sizeof(struct iovec)); kr_require(siov); knot_wire_write_u16(siov->nlen, ctx->payload.buffer.len); @@ -2264,14 +2236,11 @@ static enum protolayer_iter_cb_result pl_dns_stream_wrap( .iov_len = ctx->payload.buffer.len }; - stream->sent.mem = siov; - stream->sent.pool = &s->pool; - - ctx->payload = protolayer_iovec(siov->iovs, iovcnt, false); + ctx->payload = protolayer_payload_iovec(siov->iovs, iovcnt, false); return protolayer_continue(ctx); } else if (ctx->payload.type == PROTOLAYER_PAYLOAD_IOVEC) { const int iovcnt = 1 + ctx->payload.iovec.cnt; - struct sized_iovs *siov = mm_alloc(&s->pool, + struct sized_iovs *siov = mm_alloc(&ctx->pool, sizeof(*siov) + iovcnt * sizeof(struct iovec)); kr_require(siov); @@ -2290,10 +2259,7 @@ static enum protolayer_iter_cb_result pl_dns_stream_wrap( .iov_len = sizeof(siov->nlen) }; - stream->sent.mem = siov; - stream->sent.pool = &s->pool; - - ctx->payload = protolayer_iovec(siov->iovs, iovcnt, false); + ctx->payload = protolayer_payload_iovec(siov->iovs, iovcnt, false); return protolayer_continue(ctx); } else { kr_assert(false && "Invalid payload"); @@ -2301,7 +2267,7 @@ static enum protolayer_iter_cb_result pl_dns_stream_wrap( } } -static void pl_dns_stream_request_init(struct protolayer_manager *manager, +static void pl_dns_stream_request_init(struct session2 *session, struct kr_request *req, void *sess_data) { @@ -2315,13 +2281,13 @@ int worker_init(void) kr_bindings_register(the_engine->L); // TODO move /* DNS protocol layers */ - protolayer_globals[PROTOLAYER_PROTOCOL_DNS_DGRAM] = (struct protolayer_globals){ + protolayer_globals[PROTOLAYER_TYPE_DNS_DGRAM] = (struct protolayer_globals){ .wire_buf_overhead_cb = pl_dns_dgram_wire_buf_overhead, .wire_buf_max_overhead = KNOT_WIRE_MAX_PKTSIZE, .unwrap = pl_dns_dgram_unwrap, .event_unwrap = pl_dns_dgram_event_unwrap }; - protolayer_globals[PROTOLAYER_PROTOCOL_DNS_UNSIZED_STREAM] = (struct protolayer_globals){ + protolayer_globals[PROTOLAYER_TYPE_DNS_UNSIZED_STREAM] = (struct protolayer_globals){ .sess_size = sizeof(struct pl_dns_stream_sess_data), .wire_buf_overhead = KNOT_WIRE_MAX_PKTSIZE, .sess_init = pl_dns_stream_sess_init, @@ -2331,19 +2297,17 @@ int worker_init(void) }; const struct protolayer_globals stream_common = { .sess_size = sizeof(struct pl_dns_stream_sess_data), - .iter_size = sizeof(struct pl_dns_stream_iter_data), .wire_buf_overhead = KNOT_WIRE_MAX_PKTSIZE, .sess_init = NULL, /* replaced in specific layers below */ - .iter_deinit = pl_dns_stream_iter_deinit, .unwrap = pl_dns_stream_unwrap, .wrap = pl_dns_stream_wrap, .event_unwrap = pl_dns_stream_event_unwrap, .request_init = pl_dns_stream_request_init }; - protolayer_globals[PROTOLAYER_PROTOCOL_DNS_MULTI_STREAM] = stream_common; - protolayer_globals[PROTOLAYER_PROTOCOL_DNS_MULTI_STREAM].sess_init = pl_dns_stream_sess_init; - protolayer_globals[PROTOLAYER_PROTOCOL_DNS_SINGLE_STREAM] = stream_common; - protolayer_globals[PROTOLAYER_PROTOCOL_DNS_SINGLE_STREAM].sess_init = pl_dns_single_stream_sess_init; + protolayer_globals[PROTOLAYER_TYPE_DNS_MULTI_STREAM] = stream_common; + protolayer_globals[PROTOLAYER_TYPE_DNS_MULTI_STREAM].sess_init = pl_dns_stream_sess_init; + protolayer_globals[PROTOLAYER_TYPE_DNS_SINGLE_STREAM] = stream_common; + protolayer_globals[PROTOLAYER_TYPE_DNS_SINGLE_STREAM].sess_init = pl_dns_single_stream_sess_init; /* Create main worker. */ the_worker = &the_worker_value; diff --git a/daemon/zimport.c b/daemon/zimport.c index 8d395270..30edcaec 100644 --- a/daemon/zimport.c +++ b/daemon/zimport.c @@ -98,7 +98,7 @@ static int key_get(char buf[KEY_LEN], const knot_dname_t *name, char *lf = (char *)knot_dname_lf(name, (uint8_t *)buf); if (kr_fails_assert(lf && key_p)) return kr_error(EINVAL); - int len = lf[0]; + int len = (unsigned char)lf[0]; lf++; // point to start of data *key_p = lf; // Check that LF is right-aligned to KNOT_DNAME_MAXLEN in buf. @@ -282,7 +282,7 @@ do_digest: // hexdump the hash for logging char hash_str[digs[i].size * 2 + 1]; for (ssize_t j = 0; j < digs[i].size; ++j) - sprintf(hash_str + 2*j, "%02x", digs[i].data[j]); + (void)sprintf(hash_str + 2*j, "%02x", digs[i].data[j]); if (!z_import->digests[i].expected) { kr_log_error(PREFILL, "no ZONEMD found; computed hash: %s\n", @@ -560,7 +560,7 @@ int zi_zone_import(const zi_config_t config) if (kr_fails_assert(c && c->zone_file)) return kr_error(EINVAL); - knot_mm_t *pool = mm_ctx_mempool2(1024 * 1024); + knot_mm_t *pool = mm_ctx_mempool2((size_t)1024 * 1024); zone_import_ctx_t *z_import = mm_calloc(pool, 1, sizeof(*z_import)); if (!z_import) return kr_error(ENOMEM); z_import->pool = pool; diff --git a/distro/config/apkg.toml b/distro/config/apkg.toml index dcf5e5e1..19d4d8be 100644 --- a/distro/config/apkg.toml +++ b/distro/config/apkg.toml @@ -10,4 +10,4 @@ signature_url = "https://secure.nic.cz/files/knot-resolver/knot-resolver-{{ vers version_script = "scripts/upstream-version.sh" [apkg] -compat = 2 +compat = 4 diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control index 55ab26b6..19d95834 100644 --- a/distro/pkg/deb/control +++ b/distro/pkg/deb/control @@ -28,47 +28,33 @@ Build-Depends: libssl-dev, Homepage: https://www.knot-resolver.cz/ -Package: knot-resolver-manager +Package: knot-resolver6 Architecture: any -Provides: - knot-resolver6, Depends: - knot-resolver-core (= ${binary:Version}), + adduser, + dns-root-data, python3-aiohttp, python3-jinja2, python3-yaml, supervisor, - ${misc:Depends}, - ${python3:Depends}, -Recommends: - python3-prometheus-client, -Section: python -Description: caching, DNSSEC-validating DNS resolver - config manager - Knot Resolver is a caching full resolver implementation written in C and - LuaJIT, including both a resolver library and a daemon. - . - This package contains Knot Resolver Manager - a configuration tool for Knot - Resolver. The Manager hides the complexity of running several independent - resolver processes while ensuring zero-downtime reconfiguration with YAML/JSON - declarative configuration and an optional HTTP API for dynamic changes. - -Package: knot-resolver-core -Architecture: any -Depends: - adduser, - dns-root-data, systemd, ${misc:Depends}, ${shlibs:Depends}, + ${python3:Depends}, Breaks: knot-resolver (<< 6), + knot-resolver-core (<< 6.0.8), + knot-resolver-manager (<< 6.0.8), Replaces: knot-resolver (<< 6), + knot-resolver-core (<< 6.0.8), + knot-resolver-manager (<< 6.0.8), Recommends: lua-basexx, lua-cqueues, lua-http, lua-psl, + python3-prometheus-client, Suggests: knot-resolver-module-http, Description: caching, DNSSEC-validating DNS resolver - core binaries @@ -88,12 +74,24 @@ Description: caching, DNSSEC-validating DNS resolver - core binaries MVCC cache that may be shared). You can start and stop additional nodes depending on the contention without downtime. . - This package contains the core resolver binaries. + Knot Resolver Manager hides the complexity of running several independent + resolver processes while ensuring zero-downtime reconfiguration with YAML/JSON + declarative configuration and an optional HTTP API for dynamic changes. -Package: knot-resolver-core-dbg +Package: knot-resolver6-dev Architecture: any Depends: - knot-resolver-core (= ${binary:Version}), + knot-resolver6 (= ${binary:Version}), + ${misc:Depends} + ${shlibs:Depends}, +Section: libdevel +Description: Knot Resolver development files + This package provides the development headers for Knot Resolver. + +Package: knot-resolver6-dbg +Architecture: any +Depends: + knot-resolver6 (= ${binary:Version}), ${misc:Depends} Recommends: gdb Section: debug @@ -102,11 +100,11 @@ Description: Debug symbols for Knot Resolver This package provides the debug symbols for Knot Resolver needed for properly debugging errors in Knot Resolver with gdb. -Package: knot-resolver-module-dnstap +Package: knot-resolver6-module-dnstap Architecture: any Multi-Arch: same Depends: - knot-resolver-core (= ${binary:Version}), + knot-resolver6 (= ${binary:Version}), libfstrm0, libprotobuf-c1, ${misc:Depends}, @@ -122,10 +120,10 @@ Description: dnstap module for Knot Resolver This package contains dnstap module for logging DNS responses to a unix socket in dnstap format. -Package: knot-resolver-module-http +Package: knot-resolver6-module-http Architecture: all Depends: - knot-resolver-core (= ${binary:Version}), + knot-resolver6 (= ${binary:Version}), libjs-bootstrap, libjs-d3, libjs-jquery, @@ -135,8 +133,6 @@ Depends: systemd, ${misc:Depends}, ${shlibs:Depends}, -Breaks: - knot-resolver-module-tinyweb (<< 1.1.0~git20160713-1~), Description: HTTP module for Knot Resolver The Knot Resolver is a caching full resolver implementation written in C and LuaJIT, including both a resolver library and a diff --git a/distro/pkg/deb/knot-resolver-core.manpages b/distro/pkg/deb/knot-resolver-core.manpages deleted file mode 100644 index f9ca908f..00000000 --- a/distro/pkg/deb/knot-resolver-core.manpages +++ /dev/null @@ -1 +0,0 @@ -debian/tmp/usr/share/man/man8/kresd.8* diff --git a/distro/pkg/deb/knot-resolver-manager.install b/distro/pkg/deb/knot-resolver-manager.install deleted file mode 100644 index 91068501..00000000 --- a/distro/pkg/deb/knot-resolver-manager.install +++ /dev/null @@ -1,4 +0,0 @@ -etc/knot-resolver/config.yaml -usr/lib/systemd/system/knot-resolver.service -usr/share/bash-completion/completions/kresctl -usr/share/fish/completions/kresctl.fish diff --git a/distro/pkg/deb/knot-resolver6-dev.install b/distro/pkg/deb/knot-resolver6-dev.install new file mode 100644 index 00000000..d565b386 --- /dev/null +++ b/distro/pkg/deb/knot-resolver6-dev.install @@ -0,0 +1,3 @@ +usr/include/libkres/*.h +usr/lib/*.so +usr/lib/pkgconfig/libkres.pc diff --git a/distro/pkg/deb/knot-resolver-module-dnstap.install b/distro/pkg/deb/knot-resolver6-module-dnstap.install index ae5404e0..ae5404e0 100644 --- a/distro/pkg/deb/knot-resolver-module-dnstap.install +++ b/distro/pkg/deb/knot-resolver6-module-dnstap.install diff --git a/distro/pkg/deb/knot-resolver-module-http.install b/distro/pkg/deb/knot-resolver6-module-http.install index ffa04d01..ffa04d01 100644 --- a/distro/pkg/deb/knot-resolver-module-http.install +++ b/distro/pkg/deb/knot-resolver6-module-http.install diff --git a/distro/pkg/deb/knot-resolver-module-http.links b/distro/pkg/deb/knot-resolver6-module-http.links index 4963c5cb..4963c5cb 100644 --- a/distro/pkg/deb/knot-resolver-module-http.links +++ b/distro/pkg/deb/knot-resolver6-module-http.links diff --git a/distro/pkg/deb/knot-resolver-core.dirs b/distro/pkg/deb/knot-resolver6.dirs index f8981d8d..f8981d8d 100644 --- a/distro/pkg/deb/knot-resolver-core.dirs +++ b/distro/pkg/deb/knot-resolver6.dirs diff --git a/distro/pkg/deb/knot-resolver-core.docs b/distro/pkg/deb/knot-resolver6.docs index 8e919d0c..8e919d0c 100644 --- a/distro/pkg/deb/knot-resolver-core.docs +++ b/distro/pkg/deb/knot-resolver6.docs diff --git a/distro/pkg/deb/knot-resolver-core.install b/distro/pkg/deb/knot-resolver6.install index 1e57ac9b..29d23032 100644 --- a/distro/pkg/deb/knot-resolver-core.install +++ b/distro/pkg/deb/knot-resolver6.install @@ -1,34 +1,38 @@ +etc/knot-resolver/config.yaml usr/lib/*.so.* -usr/lib/tmpfiles.d/knot-resolver.conf -usr/lib/knot-resolver/*.so usr/lib/knot-resolver/*.lua +usr/lib/knot-resolver/*.so usr/lib/knot-resolver/kres_modules/bogus_log.so -usr/lib/knot-resolver/kres_modules/edns_keepalive.so -usr/lib/knot-resolver/kres_modules/extended_error.so -usr/lib/knot-resolver/kres_modules/hints.so -usr/lib/knot-resolver/kres_modules/nsid.so -usr/lib/knot-resolver/kres_modules/refuse_nord.so -usr/lib/knot-resolver/kres_modules/stats.so usr/lib/knot-resolver/kres_modules/daf.lua usr/lib/knot-resolver/kres_modules/daf/* usr/lib/knot-resolver/kres_modules/detect_time_jump.lua usr/lib/knot-resolver/kres_modules/detect_time_skew.lua usr/lib/knot-resolver/kres_modules/dns64.lua +usr/lib/knot-resolver/kres_modules/edns_keepalive.so usr/lib/knot-resolver/kres_modules/experimental_dot_auth.lua +usr/lib/knot-resolver/kres_modules/extended_error.so usr/lib/knot-resolver/kres_modules/graphite.lua +usr/lib/knot-resolver/kres_modules/hints.so +usr/lib/knot-resolver/kres_modules/nsid.so usr/lib/knot-resolver/kres_modules/policy.lua usr/lib/knot-resolver/kres_modules/predict.lua usr/lib/knot-resolver/kres_modules/prefetch.lua usr/lib/knot-resolver/kres_modules/prefill.lua usr/lib/knot-resolver/kres_modules/priming.lua usr/lib/knot-resolver/kres_modules/rebinding.lua +usr/lib/knot-resolver/kres_modules/refuse_nord.so usr/lib/knot-resolver/kres_modules/renumber.lua usr/lib/knot-resolver/kres_modules/serve_stale.lua +usr/lib/knot-resolver/kres_modules/stats.so usr/lib/knot-resolver/kres_modules/ta_sentinel.lua usr/lib/knot-resolver/kres_modules/ta_signal_query.lua usr/lib/knot-resolver/kres_modules/ta_update.lua usr/lib/knot-resolver/kres_modules/view.lua usr/lib/knot-resolver/kres_modules/watchdog.lua usr/lib/knot-resolver/kres_modules/workarounds.lua -usr/sbin/kresd +usr/lib/systemd/system/knot-resolver.service +usr/lib/tmpfiles.d/knot-resolver.conf usr/sbin/kres-cache-gc +usr/sbin/kresd +usr/share/bash-completion/completions/kresctl +usr/share/fish/completions/kresctl.fish diff --git a/distro/pkg/deb/knot-resolver-manager.links b/distro/pkg/deb/knot-resolver6.links index c5467e84..c5467e84 100644 --- a/distro/pkg/deb/knot-resolver-manager.links +++ b/distro/pkg/deb/knot-resolver6.links diff --git a/distro/pkg/deb/knot-resolver-manager.manpages b/distro/pkg/deb/knot-resolver6.manpages index a453f7e9..aa59efff 100644 --- a/distro/pkg/deb/knot-resolver-manager.manpages +++ b/distro/pkg/deb/knot-resolver6.manpages @@ -1 +1,2 @@ debian/tmp/usr/share/man/man8/kresctl.8* +debian/tmp/usr/share/man/man8/kresd.8* diff --git a/distro/pkg/deb/knot-resolver-core.postinst b/distro/pkg/deb/knot-resolver6.postinst index e9852987..e9852987 100644 --- a/distro/pkg/deb/knot-resolver-core.postinst +++ b/distro/pkg/deb/knot-resolver6.postinst diff --git a/distro/pkg/deb/knot-resolver-core.postrm b/distro/pkg/deb/knot-resolver6.postrm index e5814954..e5814954 100644 --- a/distro/pkg/deb/knot-resolver-core.postrm +++ b/distro/pkg/deb/knot-resolver6.postrm diff --git a/distro/pkg/deb/not-installed b/distro/pkg/deb/not-installed index ceb8f20d..e653609e 100644 --- a/distro/pkg/deb/not-installed +++ b/distro/pkg/deb/not-installed @@ -1,6 +1,3 @@ usr/lib/knot-resolver/kres_modules/http/LICENSE usr/lib/knot-resolver/kres_modules/etcd.lua -usr/include/libkres/*.h -usr/lib/*.so -usr/lib/pkgconfig/libkres.pc usr/lib/sysusers.d/knot-resolver.conf diff --git a/distro/pkg/deb/rules b/distro/pkg/deb/rules index cc925bf0..dff9df6f 100755 --- a/distro/pkg/deb/rules +++ b/distro/pkg/deb/rules @@ -10,17 +10,13 @@ export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic -fno-omit-frame-pointer # package maintainers to append LDFLAGS export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed -export PYKRES_NAME=knot_resolver -export PYKRES_DEST=debian/knot-resolver-core/ -export KRES_MANAGER_NAME=knot_resolver_manager -export KRES_MANAGER_DEST=debian/knot-resolver-manager/ +# include python modules in main package +export PYKRES_DESTDIR=debian/knot-resolver6/ # see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/* DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/default.mk -export ARCH=$(DEB_HOST_GNU_CPU) - %: dh $@ --with python3 @@ -39,16 +35,16 @@ override_dh_auto_build: -Dc_args="$${CFLAGS}" \ -Dc_link_args="$${LDFLAGS}" ninja -v -C build_deb - PYBUILD_NAME="$${PYKRES_NAME}" PYBUILD_DESTDIR="$${PYKRES_DEST}" \ + PYBUILD_NAME=knot_resolver PYBUILD_DESTDIR="$${PYKRES_DESTDIR}" \ dh_auto_build --buildsystem=pybuild --sourcedirectory build_deb/python - PYBUILD_NAME="$${KRES_MANAGER_NAME}" PYBUILD_DESTDIR="$${KRES_MANAGER_DEST}" \ + PYBUILD_NAME=knot_resoolver_manager PYBUILD_DESTDIR="$${PYKRES_DESTDIR}" \ dh_auto_build --buildsystem=pybuild --sourcedirectory manager override_dh_auto_install: DESTDIR="${PWD}/debian/tmp" ninja -v -C build_deb install - PYBUILD_NAME="$${PYKRES_NAME}" PYBUILD_DESTDIR="$${PYKRES_DEST}" \ + PYBUILD_NAME=knot_resolver PYBUILD_DESTDIR="$${PYKRES_DESTDIR}" \ dh_auto_install --buildsystem=pybuild --sourcedirectory build_deb/python - PYBUILD_NAME="$${KRES_MANAGER_NAME}" PYBUILD_DESTDIR="$${KRES_MANAGER_DEST}" \ + PYBUILD_NAME=knot_resolver_manager PYBUILD_DESTDIR="$${PYKRES_DESTDIR}" \ dh_auto_install --buildsystem=pybuild --sourcedirectory manager install -m 644 -D manager/etc/knot-resolver/config.yaml debian/tmp/etc/knot-resolver/config.yaml install -m 644 -D manager/shell-completion/client.bash debian/tmp/usr/share/bash-completion/completions/kresctl @@ -57,8 +53,5 @@ override_dh_auto_install: override_dh_auto_test: meson test -C build_deb -override_dh_missing: - dh_missing --fail-missing - override_dh_strip: - dh_strip --dbg-package=knot-resolver-core-dbg + dh_strip --dbg-package=knot-resolver6-dbg diff --git a/distro/pkg/rpm/knot-resolver.spec b/distro/pkg/rpm/knot-resolver.spec index 5fd08f8d..8b30059e 100644 --- a/distro/pkg/rpm/knot-resolver.spec +++ b/distro/pkg/rpm/knot-resolver.spec @@ -25,16 +25,11 @@ Source100: kresd-keyblock.asc BuildRequires: gnupg2 %endif -%description -The Knot Resolver is a DNSSEC-enabled caching full resolver implementation -written in C and LuaJIT, including both a resolver library and a daemon. -Modular architecture of the library keeps the core tiny and efficient, and -provides a state-machine like API for extensions. - +Provides: knot-resolver6 = %{version}-%{release} -%package core -Summary: Caching full DNS Resolver - core binaries -Conflicts: knot-resolver < 6 +# alpha packaging compat, can be removed around 6.2 +Conflicts: knot-resolver-core +Conflicts: knot-resolver-manager # LuaJIT only on these arches ExclusiveArch: %{arm} aarch64 %{ix86} x86_64 @@ -59,6 +54,19 @@ BuildRequires: python3-devel Requires: systemd Requires(post): systemd +# manager dependencies +Requires: python3 +Requires: python3-aiohttp +Requires: supervisor +%if 0%{?suse_version} +Requires: python3-PyYAML +Requires: python3-typing_extensions +%else +Requires: python3-pyyaml +Requires: python3-typing-extensions +%endif +Recommends: python3-prometheus_client + # dnstap module dependencies # SUSE is missing protoc-c protobuf compiler %if "x%{?suse_version}" == "x" @@ -96,15 +104,21 @@ BuildRequires: python3-setuptools Requires(pre): shadow %endif -%description core +%description The Knot Resolver is a DNSSEC-enabled caching full resolver implementation written in C and LuaJIT, including both a resolver library and a daemon. Modular architecture of the library keeps the core tiny and efficient, and provides a state-machine like API for extensions. +Knot Resolver Manager is a configuration tool for Knot Resolver. The Manager +hides the complexity of running several independent resolver processes while +ensuring zero-downtime reconfiguration with YAML/JSON declarative +configuration and an optional HTTP API for dynamic changes. + + %package devel Summary: Development headers for Knot Resolver -Requires: %{name}-core%{?_isa} = %{version}-%{release} +Requires: %{name}%{?_isa} = %{version}-%{release} %description devel The package contains development headers for Knot Resolver. @@ -112,7 +126,7 @@ The package contains development headers for Knot Resolver. %if "x%{?suse_version}" == "x" %package module-dnstap Summary: dnstap module for Knot Resolver -Requires: %{name}-core = %{version}-%{release} +Requires: %{name} = %{version}-%{release} %description module-dnstap dnstap module for Knot Resolver supports logging DNS responses to a unix socket @@ -123,7 +137,7 @@ need effectively log all DNS traffic. %if "x%{?suse_version}" == "x" %package module-http Summary: HTTP module for Knot Resolver -Requires: %{name}-core = %{version}-%{release} +Requires: %{name} = %{version}-%{release} %if 0%{?fedora} || 0%{?rhel} > 7 Requires: lua5.1-http Requires: lua5.1-mmdb @@ -139,28 +153,6 @@ queries. It can also serve DNS-over-HTTPS, but it is deprecated in favor of native C implementation, which doesn't require this package. %endif -%package -n knot-resolver-manager -Summary: Configuration tool for Knot Resolver -Provides: knot-resolver6 = %{version}-%{release} -Requires: %{name}-core = %{version}-%{release} -Requires: python3 -Requires: python3-aiohttp -Requires: supervisor -%if 0%{?suse_version} -Requires: python3-PyYAML -Requires: python3-typing_extensions -%else -Requires: python3-pyyaml -Requires: python3-typing-extensions -%endif -Recommends: python3-prometheus_client - -%description -n knot-resolver-manager -Knot Resolver Manager is a configuration tool for Knot Resolver. The Manager -hides the complexity of running several independent resolver processes while -ensuring zero-downtime reconfiguration with YAML/JSON declarative -configuration and an optional HTTP API for dynamic changes. - %prep %if 0%{GPG_CHECK} export GNUPGHOME=./gpg-keyring @@ -237,44 +229,48 @@ install -m 644 -D shell-completion/client.fish %{buildroot}%{_datarootdir}/fish/ popd -%pre core +%pre getent group knot-resolver >/dev/null || groupadd -r knot-resolver getent passwd knot-resolver >/dev/null || useradd -r -g knot-resolver -d %{_sysconfdir}/knot-resolver -s /sbin/nologin -c "Knot Resolver" knot-resolver -%post core +%post # systemd_post macro is not needed for anything (calls systemctl preset) %tmpfiles_create %{_tmpfilesdir}/knot-resolver.conf %if "x%{?fedora}" == "x" /sbin/ldconfig %endif -%preun manager +%preun %systemd_preun knot-resolver.service -%postun manager +%postun %systemd_postun_with_restart knot-resolver.service %if "x%{?fedora}" == "x" /sbin/ldconfig %endif -%files core +%files %dir %{_pkgdocdir} %license %{_pkgdocdir}/COPYING %doc %{_pkgdocdir}/AUTHORS %doc %{_pkgdocdir}/NEWS %doc %{_pkgdocdir}/examples %dir %{_sysconfdir}/knot-resolver +%config(noreplace) %{_sysconfdir}/knot-resolver/config.yaml %config(noreplace) %{_sysconfdir}/knot-resolver/root.hints %{_sysconfdir}/knot-resolver/icann-ca.pem %attr(750,knot-resolver,knot-resolver) %dir %{_sharedstatedir}/knot-resolver %attr(640,knot-resolver,knot-resolver) %{_sharedstatedir}/knot-resolver/root.keys %dir %{_unitdir}/multi-user.target.wants +%{_unitdir}/knot-resolver.service %{_unitdir}/multi-user.target.wants/knot-resolver.service %{_tmpfilesdir}/knot-resolver.conf %ghost /run/%{name} %ghost %{_localstatedir}/cache/%{name} %attr(750,knot-resolver,knot-resolver) %dir %{_libdir}/%{name} +%{_bindir}/kresctl +%{_bindir}/knot-resolver %{_sbindir}/kresd %{_sbindir}/kres-cache-gc %{_libdir}/libkres.so.* @@ -313,6 +309,7 @@ getent passwd knot-resolver >/dev/null || useradd -r -g knot-resolver -d %{_sysc %{_libdir}/knot-resolver/kres_modules/workarounds.lua %{python3_sitelib}/knot_resolver.py %{python3_sitelib}/knot_resolver-* +%{python3_sitearch}/knot_resolver_manager* %if 0%{?suse_version} %pycache_only %{python3_sitelib}/__pycache__/knot_resolver.* %else @@ -320,6 +317,8 @@ getent passwd knot-resolver >/dev/null || useradd -r -g knot-resolver -d %{_sysc %endif %{_mandir}/man8/kresd.8.gz %{_mandir}/man8/kresctl.8.gz +%{_datarootdir}/bash-completion/completions/kresctl +%{_datarootdir}/fish/completions/kresctl.fish %files devel %{_includedir}/libkres @@ -339,15 +338,6 @@ getent passwd knot-resolver >/dev/null || useradd -r -g knot-resolver -d %{_sysc %{_libdir}/knot-resolver/kres_modules/prometheus.lua %endif -%files -n knot-resolver-manager -%{python3_sitearch}/knot_resolver_manager* -%config(noreplace) %{_sysconfdir}/knot-resolver/config.yaml -%{_unitdir}/knot-resolver.service -%{_bindir}/kresctl -%{_bindir}/knot-resolver -%{_datarootdir}/bash-completion/completions/kresctl -%{_datarootdir}/fish/completions/kresctl.fish - %changelog * {{ now }} Jakub Ružička <jakub.ruzicka@nic.cz> - {{ version }}-{{ release }} - upstream package diff --git a/doc/dev/build.rst b/doc/dev/build.rst index b6bfd9f4..2e8e7d17 100644 --- a/doc/dev/build.rst +++ b/doc/dev/build.rst @@ -92,20 +92,6 @@ The following dependencies are needed to build and run Knot Resolver with core f "lmdb", "Memory-mapped database for cache" "GnuTLS", "TLS" -Additional dependencies are needed to build and run Knot Resolver with ``manager``: -All dependencies are also listed in `pyproject.toml <https://gitlab.nic.cz/knot/knot-resolver/-/blob/manager/manager/pyproject.toml>`_ which is our authoritative source. - -.. csv-table:: - :header: "Requirement", "Notes" - - "python3_ >=3.7", "Python language interpreter" - "Jinja2_", "Template engine for Python" - "PyYAML_", "YAML framework for Python" - "aiohttp_", "HTTP Client/Server for Python." - "prometheus-client_", "Prometheus client for Python" - "typing-extensions_", "Compatibility module for Python" - - There are also *optional* packages that enable specific functionality in Knot Resolver: @@ -168,7 +154,7 @@ You can also configure some :ref:`build-options`, in this case enable ``manager` .. code-block:: bash - $ meson build_dir --prefix=/tmp/kr --default-library=static -Dmanager=enabled + $ meson build_dir --prefix=/tmp/kr After that it is possible to build and install Knot Resolver. @@ -275,7 +261,6 @@ Recommended build options for packagers: * ``-Dsystemd_files=enabled`` for systemd unit files * ``-Ddoc=enabled`` for offline documentation (see :ref:`build-html-doc`) * ``-Dinstall_kresd_conf=enabled`` to install default config file -* ``-Dmanager=enabled`` to force build of the manager and its features * ``-Dunit_tests=enabled`` to force build of unit tests Systemd @@ -308,6 +293,36 @@ In case you want to have automatically managed DNSSEC trust anchors instead, set ``-Dmanaged_ta=enabled`` and make sure both ``keyfile_default`` file and its parent directories are writable by kresd process (after package installation!). +********************************** +Installing the manager from source +********************************** + +Additional dependencies are needed to run Knot Resolver with the ``manager``. +All dependencies are also listed in `pyproject.toml <https://gitlab.nic.cz/knot/knot-resolver/-/blob/master/manager/pyproject.toml>`_ which is our authoritative source. + +.. csv-table:: + :header: "Requirement", "Notes" + + "python3_ >=3.8", "Python language interpreter" + "Jinja2_", "Template engine for Python" + "PyYAML_", "YAML framework for Python" + "aiohttp_", "HTTP Client/Server for Python." + "typing-extensions_", "Compatibility module for Python" + "prometheus-client_", "Prometheus client for Python (optional)" + + + You can install the ``manager`` using generated ``setup.py``. + +.. code-block:: bash + + cd manager + python3 setup.py install + +.. tip:: + + For development, it is recommended to run the manager using the procedure described in `manager/README.md <https://gitlab.nic.cz/knot/knot-resolver/-/blob/master/manager/README.md>`_. + + ************ Docker image ************ diff --git a/lib/cache/api.c b/lib/cache/api.c index 2143ceef..490f3d1c 100644 --- a/lib/cache/api.c +++ b/lib/cache/api.c @@ -529,7 +529,7 @@ static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry, goto return_needs_pkt; const knot_dname_t *encloser = rr->owner; /**< the closest encloser name */ for (int i = 0; i < wild_labels; ++i) { - encloser = knot_wire_next_label(encloser, NULL); + encloser = knot_dname_next_label(encloser); } /* Construct the key under which RRs will be stored, diff --git a/lib/cache/peek.c b/lib/cache/peek.c index 4b8e4acc..d12031fc 100644 --- a/lib/cache/peek.c +++ b/lib/cache/peek.c @@ -174,6 +174,7 @@ int peek_nosync(kr_layer_t *ctx, knot_pkt_t *pkt) knot_db_val_bound(v), new_ttl); return ret == kr_ok() ? KR_STATE_DONE : ctx->state; } + default:; // Continue below } /* We have to try proving from NSEC*. */ @@ -359,7 +360,7 @@ static int peek_encloser( /** Name of the closest (provable) encloser. */ const knot_dname_t *clencl_name = qry->sname; for (int l = sname_labels; l > clencl_labels; --l) - clencl_name = knot_wire_next_label(clencl_name, NULL); + clencl_name = knot_dname_next_label(clencl_name); /**** 3. source of synthesis checks, in case the next closer name was covered. **** 3a. We want to query for NSEC* of source of synthesis (SS) or its diff --git a/lib/dnssec.c b/lib/dnssec.c index 9f43bb83..77cec796 100644 --- a/lib/dnssec.c +++ b/lib/dnssec.c @@ -362,7 +362,7 @@ static int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx, const int covered_labels = knot_dname_labels(covered->owner, NULL) - knot_dname_is_wildcard(covered->owner); - for (uint16_t i = 0; i < vctx->rrs->len; ++i) { + for (size_t i = 0; i < vctx->rrs->len; ++i) { /* Consider every RRSIG that matches and comes from the same query. */ const knot_rrset_t *rrsig = vctx->rrs->at[i]->rr; const bool ok = vctx->rrs->at[i]->qry_uid == vctx->qry_uid diff --git a/lib/dnssec/nsec.c b/lib/dnssec/nsec.c index d798e3cf..be34d92d 100644 --- a/lib/dnssec/nsec.c +++ b/lib/dnssec/nsec.c @@ -81,15 +81,13 @@ static int dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2) dname_reverse(d1, d1_len, d1_rev_arr); dname_reverse(d2, d2_len, d2_rev_arr); - int res = 0; - while (res == 0 && d1_rev != NULL) { - res = lf_cmp(d1_rev, d2_rev); - d1_rev = knot_wire_next_label(d1_rev, NULL); - d2_rev = knot_wire_next_label(d2_rev, NULL); - } - - kr_require(res != 0 || d2_rev == NULL); - return res; + do { + int res = lf_cmp(d1_rev, d2_rev); + if (res != 0 || d1_rev[0] == '\0') + return res; + d1_rev = knot_dname_next_label(d1_rev); + d2_rev = knot_dname_next_label(d2_rev); + } while (true); } @@ -251,7 +249,7 @@ int kr_nsec_negative(const ranked_rr_array_t *rrrs, uint32_t qry_uid, ssynth[1] = '*'; const knot_dname_t *clencl = sname; for (int l = sname_labels; l > clencl_labels; --l) - clencl = knot_wire_next_label(clencl, NULL); + clencl = knot_dname_next_label(clencl); (void)!!knot_dname_store(&ssynth[2], clencl); // Try to (dis)prove the source of synthesis by a covering or matching NSEC. diff --git a/lib/dnssec/nsec3.c b/lib/dnssec/nsec3.c index 4199f25f..4ff27500 100644 --- a/lib/dnssec/nsec3.c +++ b/lib/dnssec/nsec3.c @@ -143,7 +143,7 @@ static int closest_encloser_match(int *flags, const knot_rrset_t *nsec3, goto fail; } - const knot_dname_t *encloser = knot_wire_next_label(name, NULL); + const knot_dname_t *encloser = knot_dname_next_label(name); *skipped = 1; /* Avoid doing too much work on SHA1, mitigating: @@ -154,7 +154,7 @@ static int closest_encloser_match(int *flags, const knot_rrset_t *nsec3, const int max_labels = knot_dname_labels(nsec3->owner, NULL) - 1 + kr_nsec3_max_depth(¶ms); for (int l = knot_dname_labels(encloser, NULL); l > max_labels; --l) { - encloser = knot_wire_next_label(encloser, NULL); + encloser = knot_dname_next_label(encloser); ++(*skipped); } @@ -174,7 +174,7 @@ static int closest_encloser_match(int *flags, const knot_rrset_t *nsec3, if (!encloser[0]) break; - encloser = knot_wire_next_label(encloser, NULL); + encloser = knot_dname_next_label(encloser); ++(*skipped); } @@ -404,7 +404,7 @@ static int closest_encloser_proof(const knot_pkt_t *pkt, for (unsigned j = 0; j < skipped; ++j) { if (kr_fails_assert(next_closer[0])) return kr_error(EINVAL); - next_closer = knot_wire_next_label(next_closer, NULL); + next_closer = knot_dname_next_label(next_closer); } for (unsigned j = 0; j < sec->count; ++j) { const knot_rrset_t *rrset_j = knot_pkt_rr(sec, j); @@ -425,7 +425,7 @@ static int closest_encloser_proof(const knot_pkt_t *pkt, if ((flags & FLG_CLOSEST_PROVABLE_ENCLOSER) && (flags & FLG_NAME_COVERED) && next_closer) { if (encloser_name && next_closer[0]) - *encloser_name = knot_wire_next_label(next_closer, NULL); + *encloser_name = knot_dname_next_label(next_closer); if (matching_encloser_nsec3) *matching_encloser_nsec3 = matching; if (covering_next_nsec3) @@ -569,7 +569,7 @@ int kr_nsec3_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_ for (int i = 0; i < trim_to_next; ++i) { if (kr_fails_assert(sname[0])) return kr_error(EINVAL); - sname = knot_wire_next_label(sname, NULL); + sname = knot_dname_next_label(sname); } int flags = 0; diff --git a/lib/dnssec/signature.c b/lib/dnssec/signature.c index f80337fe..6e443cf9 100644 --- a/lib/dnssec/signature.c +++ b/lib/dnssec/signature.c @@ -224,7 +224,7 @@ static int sign_ctx_add_records(dnssec_sign_ctx_t *ctx, const knot_rrset_t *cove for (int j = 0; j < trim_labels; ++j) { if (kr_fails_assert(beginp[0])) return kr_error(EINVAL); - beginp = (uint8_t *) knot_wire_next_label(beginp, NULL); + beginp = (uint8_t *) knot_dname_next_label(beginp); if (kr_fails_assert(beginp)) return kr_error(EFAULT); } diff --git a/lib/dnssec/ta.c b/lib/dnssec/ta.c index 67f0a206..6593b2f3 100644 --- a/lib/dnssec/ta.c +++ b/lib/dnssec/ta.c @@ -28,9 +28,9 @@ const knot_dname_t * kr_ta_closest(const struct kr_context *ctx, const knot_dnam kr_require(ctx && name); if (type == KNOT_RRTYPE_DS && name[0] != '\0') { /* DS is parent-side record, so the parent name needs to be covered. */ - name = knot_wire_next_label(name, NULL); + name = knot_dname_next_label(name); } - while (name) { + do { struct kr_context *ctx_nc = (struct kr_context *)/*const-cast*/ctx; if (kr_ta_get(ctx_nc->trust_anchors, name)) { return name; @@ -38,9 +38,12 @@ const knot_dname_t * kr_ta_closest(const struct kr_context *ctx, const knot_dnam if (kr_ta_get(ctx_nc->negative_anchors, name)) { return NULL; } - name = knot_wire_next_label(name, NULL); - } - return NULL; + if (name[0] == '\0') { + return NULL; + } else { + name = knot_dname_next_label(name); + } + } while (true); } /* @internal Create DS from DNSKEY, caller MUST free dst if successful. */ diff --git a/lib/generic/array.h b/lib/generic/array.h index 9f351189..9bea546b 100644 --- a/lib/generic/array.h +++ b/lib/generic/array.h @@ -113,7 +113,7 @@ static inline void array_std_free(void *baton, void *p) * Mempool usage: pass kr_memreserve and a knot_mm_t* . * @return 0 if success, <0 on failure */ #define array_reserve_mm(array, n, reserve, baton) \ - (reserve)((baton), (void **) &(array).at, sizeof((array).at[0]), (n), &(array).cap) + (reserve)((baton), (void **) &(array).at, array_member_size((array)), (n), &(array).cap) /** * Push value at the end of the array, resize it if necessary. @@ -122,9 +122,9 @@ static inline void array_std_free(void *baton, void *p) * @return element index on success, <0 on failure */ #define array_push_mm(array, val, reserve, baton) \ - (int)((array).len < (array).cap ? ((array).at[(array).len] = val, (array).len++) \ + (int)((array).len < (array).cap ? ((array).at[(array).len] = (val), (array).len++) \ : (array_reserve_mm(array, ((array).cap + 1), reserve, baton) < 0 ? -1 \ - : ((array).at[(array).len] = val, (array).len++))) + : ((array).at[(array).len] = (val), (array).len++))) /** * Push value at the end of the array, resize it if necessary (plain malloc/free). @@ -152,6 +152,12 @@ static inline void array_std_free(void *baton, void *p) * @warning Undefined if the array is empty. */ #define array_tail(array) \ - (array).at[(array).len - 1] + (array).at[(array).len - 1] + +/** + * Return the size of a singular member in the array. + */ +#define array_member_size(array) \ + (sizeof((array).at[0])) // NOLINT(bugprone-sizeof-expression): usually a false-positive /** @} */ diff --git a/lib/generic/lru.h b/lib/generic/lru.h index 448c1b92..1c1dd81a 100644 --- a/lib/generic/lru.h +++ b/lib/generic/lru.h @@ -130,7 +130,10 @@ #define lru_get_new(table, key_, len_, is_new) \ (__typeof__((table)->pdata_t)) \ lru_get_impl(&(table)->lru, (key_), (len_), \ - sizeof(*(table)->pdata_t), true, is_new) + lru_member_size((table)), true, is_new) + +#define lru_member_size(table) \ + (sizeof(*(table)->pdata_t)) // NOLINT(bugprone-sizeof-expression): usually a false-positive /** * @brief Apply a function to every item in LRU. diff --git a/lib/generic/queue.c b/lib/generic/queue.c index 5bed153e..29609dd2 100644 --- a/lib/generic/queue.c +++ b/lib/generic/queue.c @@ -62,7 +62,7 @@ void * queue_push_impl(struct queue *q) if (t->begin * 2 >= t->cap) { /* Utilization is below 50%, so let's shift (no overlap). * (size_t cast is to avoid unintended sign-extension) */ - memcpy(t->data, t->data + t->begin * q->item_size, + memcpy(t->data, t->data + t->begin * (size_t)q->item_size, (size_t) (t->end - t->begin) * (size_t) q->item_size); t->end -= t->begin; t->begin = 0; @@ -76,7 +76,7 @@ void * queue_push_impl(struct queue *q) kr_require(t->end < t->cap); ++(q->len); ++(t->end); - return t->data + q->item_size * (t->end - 1); + return t->data + (size_t)q->item_size * (t->end - 1); } /* Return pointer to the space for the new element. */ @@ -98,8 +98,8 @@ void * queue_push_head_impl(struct queue *q) * Computations here are simplified due to h->begin == 0. * (size_t cast is to avoid unintended sign-extension) */ const int cnt = h->end; - memcpy(h->data + (h->cap - cnt) * q->item_size, h->data, - (size_t) cnt * (size_t) q->item_size); + memcpy(h->data + ((size_t)h->cap - cnt) * q->item_size, h->data, + (size_t)cnt * (size_t)q->item_size); h->begin = h->cap - cnt; h->end = h->cap; } else { @@ -113,7 +113,7 @@ void * queue_push_head_impl(struct queue *q) kr_require(h->begin > 0); --(h->begin); ++(q->len); - return h->data + q->item_size * h->begin; + return h->data + (size_t)q->item_size * h->begin; } void queue_pop_impl(struct queue *q) diff --git a/lib/generic/queue.h b/lib/generic/queue.h index 3fa52cea..fc2a86f3 100644 --- a/lib/generic/queue.h +++ b/lib/generic/queue.h @@ -71,7 +71,7 @@ /** @brief Initialize a queue. You can malloc() it the usual way. */ #define queue_init(q) do { \ (void)(((__typeof__(((q).pdata_t)))0) == (void *)0); /* typecheck queue_t */ \ - queue_init_impl(&(q).queue, sizeof(*(q).pdata_t)); \ + queue_init_impl(&(q).queue, queue_member_size((q))); \ } while (false) /** @brief De-initialize a queue: make it invalid and free any inner allocations. */ @@ -105,6 +105,10 @@ #define queue_len(q) \ ((const size_t)(q).queue.len) +/** @brief Return the size of a single element in the queue. */ +#define queue_member_size(q) \ + (sizeof(*(q).pdata_t)) // NOLINT(bugprone-sizeof-expression): usually a false-positive + /** @brief Type for queue iterator, parametrized by value type. * It's a simple structure that owns no other resources. diff --git a/lib/generic/trie.c b/lib/generic/trie.c index f9aceda7..21254eb4 100644 --- a/lib/generic/trie.c +++ b/lib/generic/trie.c @@ -470,6 +470,10 @@ static int ns_longer_alloc(nstack_t *ns) memcpy(st, ns->stack, ns->len * sizeof(node_t *)); } else { st = realloc(ns->stack, new_size); + if (st == NULL) { + free(ns->stack); // left behind by realloc, callers bail out + ns->stack = NULL; + } } if (st == NULL) return KNOT_ENOMEM; diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c index 4eacf86f..6f312ca7 100644 --- a/lib/layer/iterate.c +++ b/lib/layer/iterate.c @@ -51,7 +51,7 @@ static const knot_dname_t *minimized_qname(struct kr_query *query, uint16_t *qty int cut_labels = knot_dname_labels(query->zone_cut.name, NULL); int qname_labels = knot_dname_labels(qname, NULL); while(qname[0] && qname_labels > cut_labels + 1) { - qname = knot_wire_next_label(qname, NULL); + qname = knot_dname_next_label(qname); qname_labels -= 1; } diff --git a/lib/layer/validate.c b/lib/layer/validate.c index 3bdb205c..af20b2e4 100644 --- a/lib/layer/validate.c +++ b/lib/layer/validate.c @@ -709,7 +709,7 @@ static int check_validation_result(kr_layer_t *ctx, const knot_pkt_t *pkt, ranke invalid_entry = entry; break; } else if (kr_rank_test(entry->rank, KR_RANK_MISSING) && - !invalid_entry) { + !invalid_entry) { // NOLINT(bugprone-branch-clone) invalid_entry = entry; } else if (kr_rank_test(entry->rank, KR_RANK_OMIT)) { continue; @@ -126,7 +126,7 @@ void kr_log_fmt(enum kr_log_group group, kr_log_level_t level, const char *file, } va_start(args, fmt); - vfprintf(stream, fmt, args); + (void)vfprintf(stream, fmt, args); va_end(args); } } diff --git a/lib/meson.build b/lib/meson.build index d8cbf1fa..60988f02 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -23,6 +23,7 @@ libkres_src = files([ 'layer/iterate.c', 'layer/validate.c', 'log.c', + 'proto.c', 'rules/api.c', 'rules/defaults.c', 'rules/forward.c', @@ -60,6 +61,7 @@ libkres_headers = files([ 'layer/iterate.h', 'log.h', 'module.h', + 'proto.h', 'resolve.h', 'resolve-impl.h', 'rplan.h', diff --git a/lib/proto.c b/lib/proto.c new file mode 100644 index 00000000..cf12e94e --- /dev/null +++ b/lib/proto.c @@ -0,0 +1,19 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz> + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "lib/proto.h" + +const char *kr_proto_name(enum kr_proto p) +{ + switch (p) { + case KR_PROTO_INTERNAL: + return "INTERNAL"; +#define XX(cid, vid, name) case KR_PROTO_##cid: \ + return (name); + KR_PROTO_MAP(XX) +#undef XX + default: + return "(default)"; + } +} diff --git a/lib/proto.h b/lib/proto.h new file mode 100644 index 00000000..875fe8e3 --- /dev/null +++ b/lib/proto.h @@ -0,0 +1,53 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz> + * SPDX-License-Identifier: GPL-3.0-or-later + */ +#pragma once + +#include <stdint.h> + +#include "lib/defines.h" + +/** DNS transport protocol map + * + * This macro is used to generate `enum kr_proto` as well as other additional + * data on protocols, like name string constants. + * + * It defines DNS transport protocols for use by `session2` (to define sequences + * of protocol layers) and `rules` (to filter requests based on them). To find + * out more, see the individual usages. + * + * Parameters for XX are: + * 1. Constant name (for e.g. KR_PROTO_* enum value identifiers) + * 2. Variable name (for e.g. kr_proto_* array identifiers, like those defined + * in `session2.c`) + * 3. Human-readable name for logging */ +#define KR_PROTO_MAP(XX) \ + XX(UDP53, udp53, "DNS UDP") \ + XX(TCP53, tcp53, "DNS TCP") \ + XX(DOT, dot, "DNS-over-TLS") \ + XX(DOH, doh, "DNS-over-HTTPS") \ + XX(DOQ, doq, "DNS-over-QUIC") /* unused for now */ \ + // + +/** DNS protocol set - mutually exclusive options, contrary to + * kr_request_qsource_flags + * + * The XDP flag is not discerned here, as it could apply to any protocol. (Not + * right now, but libknot does support it for TCP, so that would complete + * everything) + */ +enum kr_proto { + KR_PROTO_INTERNAL = 0, /// no protocol, e.g. useful to mark internal requests +#define XX(cid, vid, name) KR_PROTO_ ## cid, + KR_PROTO_MAP(XX) +#undef XX + KR_PROTO_COUNT, +}; + +/** Gets the constant string name of the specified transport protocol. */ +KR_EXPORT +const char *kr_proto_name(enum kr_proto p); + +/** Bitmap of enum kr_proto options. */ +typedef uint8_t kr_proto_set; +static_assert(sizeof(kr_proto_set) * 8 >= KR_PROTO_COUNT, "bad combination of type sizes"); diff --git a/lib/resolve-produce.c b/lib/resolve-produce.c index d9bec433..563a2ca2 100644 --- a/lib/resolve-produce.c +++ b/lib/resolve-produce.c @@ -72,7 +72,7 @@ static void check_empty_nonterms(struct kr_query *qry, knot_pkt_t *pkt, struct k * otherwise this would risk leaking information to parent if the NODATA TTD > zone cut TTD. */ int labels = knot_dname_labels(target, NULL) - knot_dname_labels(cut_name, NULL); while (target[0] && labels > 2) { - target = knot_wire_next_label(target, NULL); + target = knot_dname_next_label(target); --labels; } for (int i = 0; i < labels; ++i) { @@ -84,7 +84,7 @@ static void check_empty_nonterms(struct kr_query *qry, knot_pkt_t *pkt, struct k break; } kr_assert(target[0]); - target = knot_wire_next_label(target, NULL); + target = knot_dname_next_label(target); } kr_cache_commit(cache); #endif @@ -277,7 +277,7 @@ static int forward_trust_chain_check(struct kr_request *request, struct kr_query int cut_labels = knot_dname_labels(qry->zone_cut.name, NULL); int wanted_name_labels = knot_dname_labels(wanted_name, NULL); while (wanted_name[0] && wanted_name_labels > cut_labels + name_offset) { - wanted_name = knot_wire_next_label(wanted_name, NULL); + wanted_name = knot_dname_next_label(wanted_name); wanted_name_labels -= 1; } minimized = (wanted_name != qry->sname); @@ -508,11 +508,11 @@ static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot const knot_dname_t *parent = qry->parent->zone_cut.name; if (parent[0] != '\0' && knot_dname_in_bailiwick(qry->sname, parent) >= 0) { - requested_name = knot_wire_next_label(parent, NULL); + requested_name = knot_dname_next_label(parent); } - } else if ((qry->stype == KNOT_RRTYPE_DS) && (qry->sname[0] != '\0')) { + } else if ((qry->stype == KNOT_RRTYPE_DS) && (requested_name[0] != '\0')) { /* If this is explicit DS query, start from encloser too. */ - requested_name = knot_wire_next_label(requested_name, NULL); + requested_name = knot_dname_next_label(requested_name); } int state = KR_STATE_FAIL; @@ -521,7 +521,8 @@ static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot if (state == KR_STATE_DONE || (state & KR_STATE_FAIL)) { return state; } else if (state == KR_STATE_CONSUME) { - requested_name = knot_wire_next_label(requested_name, NULL); + kr_require(requested_name[0] != '\0'); + requested_name = knot_dname_next_label(requested_name); } } while (state == KR_STATE_CONSUME); diff --git a/lib/resolve.c b/lib/resolve.c index e8a63489..ec00b215 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -715,6 +715,8 @@ int kr_resolve_consume(struct kr_request *request, struct kr_transport **transpo if (transport && !qry->flags.CACHED) { if (!(request->state & KR_STATE_FAIL)) { /* Do not complete NS address resolution on soft-fail. */ + if (kr_fails_assert(packet->wire)) + return KR_STATE_FAIL; const int rcode = knot_wire_get_rcode(packet->wire); if (rcode != KNOT_RCODE_SERVFAIL && rcode != KNOT_RCODE_REFUSED) { qry->flags.AWAIT_IPV6 = false; @@ -748,7 +750,7 @@ int kr_resolve_consume(struct kr_request *request, struct kr_transport **transpo } /* Pop query if resolved. */ - if (request->state == KR_STATE_YIELD) { + if (request->state == KR_STATE_YIELD) { // NOLINT(bugprone-branch-clone) return KR_STATE_PRODUCE; /* Requery */ } else if (qry->flags.RESOLVED) { kr_rplan_pop(rplan, qry); @@ -931,6 +933,7 @@ int kr_resolve_finish(struct kr_request *request, int state) knot_wire_clear_ad(wire); knot_wire_clear_aa(wire); knot_wire_set_rcode(wire, KNOT_RCODE_SERVFAIL); + default:; // Do nothing } } } diff --git a/lib/rules/api.c b/lib/rules/api.c index ca026879..8e908a7a 100644 --- a/lib/rules/api.c +++ b/lib/rules/api.c @@ -91,7 +91,7 @@ int kr_rule_tag_add(const char *tag, kr_rule_tags_t *tagset) kr_log_error(RULES, "ERROR: invalid length: %d\n", (int)val.len); return kr_error(EILSEQ); } - *tagset |= (1 << *tindex_p); + *tagset |= ((kr_rule_tags_t)1 << *tindex_p); return kr_ok(); } else if (ret != kr_error(ENOENT)) { return ret; @@ -114,7 +114,7 @@ int kr_rule_tag_add(const char *tag, kr_rule_tags_t *tagset) int ix = ffsll(~bmp) - 1; if (ix < 0 || ix >= 8 * sizeof(bmp)) return kr_error(E2BIG); - const kr_rule_tags_t tag_new = 1 << ix; + const kr_rule_tags_t tag_new = (kr_rule_tags_t)1 << ix; kr_require((tag_new & bmp) == 0); // Update the bitmap. ATM ruledb does not overwrite, so we `remove` before `write`. @@ -158,7 +158,7 @@ int kr_rules_init(const char *path, size_t maxsize) // Later we might improve it to auto-resize in case of running out of space. // Caveat: mdb_env_set_mapsize() can only be called without transactions open. .maxsize = maxsize ? maxsize : - (sizeof(size_t) > 4 ? 2048 : 500) * 1024*(size_t)1024, + (size_t)(sizeof(size_t) > 4 ? 2048 : 500) * 1024*1024, }; int ret = the_rules->api->open(&the_rules->db, &the_rules->stats, &opts, NULL); /* No persistence - we always refill from config for now. @@ -848,8 +848,8 @@ static int subnet_encode(const struct sockaddr *addr, int sub_len, uint8_t buf[3 uint16_t x = a[i] * 85; // interleave by zero bits uint8_t sub_mask = 255 >> (8 - MIN(sub_len, 8)); uint16_t r = x | (sub_mask * 85 * 2); - buf[2*i] = r / 256; - buf[2*i + 1] = r % 256; + buf[(ssize_t)2*i] = r / 256; + buf[(ssize_t)2*i + 1] = r % 256; } return i * 2; } @@ -870,9 +870,9 @@ bool subnet_is_prefix(uint8_t a, uint8_t b) } #define KEY_PREPEND(key, arr) do { \ - key.data -= sizeof(arr); \ - key.len += sizeof(arr); \ - memcpy(key.data, arr, sizeof(arr)); \ + (key).data -= sizeof(arr); \ + (key).len += sizeof(arr); \ + memcpy((key).data, arr, sizeof(arr)); \ } while (false) int kr_view_insert_action(const char *subnet, const char *dst_subnet, diff --git a/lib/rules/api.h b/lib/rules/api.h index bf51e4d5..1069ef4d 100644 --- a/lib/rules/api.h +++ b/lib/rules/api.h @@ -4,6 +4,7 @@ #pragma once #include "lib/defines.h" +#include "lib/proto.h" struct kr_query; struct kr_request; struct knot_pkt; @@ -16,27 +17,6 @@ typedef uint64_t kr_rule_tags_t; /// Tags "capacity", i.e. numbered from 0 to _CAP - 1. #define KR_RULE_TAGS_CAP (sizeof(kr_rule_tags_t) * 8) -/** DNS protocol set - mutually exclusive options, contrary to kr_request_qsource_flags - * - * The XDP flag is not discerned here, as it could apply to any protocol. - * (not right now, but libknot does support it for TCP, so that would complete everything) - * - * TODO: probably unify with enum protolayer_grp. - */ -enum kr_proto { - KR_PROTO_INTERNAL = 0, /// no protocol, e.g. useful to mark internal requests - KR_PROTO_UDP53, - KR_PROTO_TCP53, - KR_PROTO_DOT, - KR_PROTO_DOH, - KR_PROTO_DOQ, /// unused for now - KR_PROTO_COUNT, -}; -/** Bitmap of enum kr_proto options. */ -typedef uint8_t kr_proto_set; -static_assert(sizeof(kr_proto_set) * 8 >= KR_PROTO_COUNT, "bad combination of type sizes"); - - /** Open the rule DB. * * You can call this to override the path or size (NULL/0 -> default). diff --git a/lib/rules/forward.c b/lib/rules/forward.c index 12ad14d5..ef2cf9da 100644 --- a/lib/rules/forward.c +++ b/lib/rules/forward.c @@ -95,7 +95,7 @@ int kr_rule_data_src_check(struct kr_query *qry, struct knot_pkt *pkt) const knot_dname_t *apex = qry->sname; for (int labels = knot_dname_labels(apex, NULL); labels > qry->data_src.rule_depth; - --labels, apex = knot_wire_next_label(apex, NULL)); + --labels, apex = knot_dname_next_label(apex)); kr_zonecut_set(&qry->zone_cut, apex); qry->zone_cut.avoid_resolving = true; knot_db_val_t targets = qry->data_src.targets_ptr; diff --git a/lib/rules/local-addr.c b/lib/rules/local-addr.c index 787639df..cd5d456b 100644 --- a/lib/rules/local-addr.c +++ b/lib/rules/local-addr.c @@ -67,7 +67,7 @@ static const knot_dname_t * raw_addr2reverse(const uint8_t *raw_addr, int family #undef REV_MAXLEN if (family == AF_INET) { - snprintf(reverse_addr, sizeof(reverse_addr), + (void)snprintf(reverse_addr, sizeof(reverse_addr), "%d.%d.%d.%d.in-addr.arpa.", raw_addr[3], raw_addr[2], raw_addr[1], raw_addr[0]); } else if (family == AF_INET6) { diff --git a/lib/rules/zonefile.c b/lib/rules/zonefile.c index cfd2bc27..d308f375 100644 --- a/lib/rules/zonefile.c +++ b/lib/rules/zonefile.c @@ -50,7 +50,8 @@ static void rr_scan2trie(zs_scanner_t *s) knot_rrset_init(rr, NULL, s->r_type, KNOT_CLASS_IN, s->r_ttl); // we don't ^^ need owner so save allocation } - knot_rrset_add_rdata(rr, s->r_data, s->r_data_length, s_data->pool); + int ret = knot_rrset_add_rdata(rr, s->r_data, s->r_data_length, s_data->pool); + kr_assert(!ret); } /// Process an RRset of other types into a rule static int rr_trie2rule(const char *key_data, uint32_t key_len, trie_val_t *rr_p, void *config) @@ -202,6 +203,7 @@ static void process_record(zs_scanner_t *s) KR_RRTYPE_GET_STR(type_str, s->r_type); kr_log_warning(RULES, "skipping unsupported RR type %s\n", type_str); return; + default:; // Continue below } if (knot_rrtype_is_metatype(s->r_type)) goto unsupported_type; @@ -244,7 +246,7 @@ int kr_rule_zonefile(const struct kr_rule_zonefile_config *c) s_data_t s_data = { 0 }; s_data.c = c; - s_data.pool = mm_ctx_mempool2(64 * 1024); + s_data.pool = mm_ctx_mempool2((size_t)64 * 1024); s_data.rrs = trie_create(s_data.pool); ret = zs_set_processing(s, process_record, NULL, &s_data); if (kr_fails_assert(ret == 0)) diff --git a/lib/selection.c b/lib/selection.c index ea3a85ae..9cdd1a60 100644 --- a/lib/selection.c +++ b/lib/selection.c @@ -149,7 +149,7 @@ struct rtt_state get_rtt_state(const uint8_t *ip, size_t len, knot_db_val_t key = cache_key(ip, len); - if (cache->api->read(db, stats, &key, &value, 1)) { + if (cache->api->read(db, stats, &key, &value, 1)) { // NOLINT(bugprone-branch-clone) state = default_rtt_state; } else if (kr_fails_assert(value.len == sizeof(struct rtt_state))) { // shouldn't happen but let's be more robust diff --git a/lib/utils.c b/lib/utils.c index 6d215760..04b1bcb9 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -955,9 +955,8 @@ int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr, static int rdata_p_cmp(const void *rp1, const void *rp2) { /* Just correct types of the parameters and pass them dereferenced. */ - const knot_rdata_t - *const *r1 = rp1, - *const *r2 = rp2; + const knot_rdata_t *const *r1 = (const knot_rdata_t *const *)rp1; + const knot_rdata_t *const *r2 = (const knot_rdata_t *const *)rp2; return knot_rdata_cmp(*r1, *r2); } int kr_ranked_rrarray_finalize(ranked_rr_array_t *array, uint32_t qry_uid, knot_mm_t *pool) @@ -982,7 +981,7 @@ int kr_ranked_rrarray_finalize(ranked_rr_array_t *array, uint32_t qry_uid, knot_ } else { /* Multiple RRs; first: sort the array. */ stashed->rr->additional = NULL; - qsort(ra->at, ra->len, sizeof(ra->at[0]), rdata_p_cmp); + qsort((void *)ra->at, ra->len, array_member_size(*ra), rdata_p_cmp); /* Prune duplicates: NULL all except the last instance. */ int dup_count = 0; for (int i = 0; i + 1 < ra->len; ++i) { diff --git a/lib/utils.h b/lib/utils.h index 8f84fc46..9fdc2d48 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -618,4 +618,10 @@ static inline size_t kr_dname_prefixlen(const uint8_t *name, unsigned nlabels) #endif ); } +#if KNOT_VERSION_HEX < 0x030400 +static inline const knot_dname_t * knot_dname_next_label(const knot_dname_t *dname) +{ + return knot_wire_next_label(dname, NULL); +} +#endif diff --git a/lib/zonecut.c b/lib/zonecut.c index 2bbd26fc..aea38e46 100644 --- a/lib/zonecut.c +++ b/lib/zonecut.c @@ -580,7 +580,7 @@ int kr_zonecut_find_cached(struct kr_context *ctx, struct kr_zonecut *cut, trie_clear(cut->nsset); /* Subtract label from QNAME. */ if (!is_root) { - label = knot_wire_next_label(label, NULL); + label = knot_dname_next_label(label); } else { ret = kr_error(ENOENT); break; diff --git a/manager/README.md b/manager/README.md index 805a563e..7f323337 100644 --- a/manager/README.md +++ b/manager/README.md @@ -32,9 +32,10 @@ Please note that Python's development files are also required, since the Manager ### Common tasks and interactions with the project -After setting up the environment, you should be able to interract with the project by using `./poe` script. Common actions are: +After setting up the environment, you should be able to interact with the project by using the `./poe` script. Common actions are: -* `poe run` - runs the manager from the source +* `poe configure` - configures Knot Resolver daemon for use with the manager +* `poe run` - runs the manager from the source (requires the daemon to be configured) * `poe docs` - creates HTML documentation * `poe test` - unit tests * `poe tox` - unit tests in all supported Python versions (must not be run outside of virtualenv, otherwise it fails to find multiple versions of Python) diff --git a/manager/knot_resolver_manager/datamodel/templates/dnssec.lua.j2 b/manager/knot_resolver_manager/datamodel/templates/dnssec.lua.j2 index 31a29bea..05d1fa68 100644 --- a/manager/knot_resolver_manager/datamodel/templates/dnssec.lua.j2 +++ b/manager/knot_resolver_manager/datamodel/templates/dnssec.lua.j2 @@ -26,8 +26,10 @@ modules.load('detect_time_skew') modules.unload('detect_time_skew') {% endif %} +{% if cfg.dnssec.keep_removed %} -- dnssec.keep-removed trust_anchors.keep_removed = {{ cfg.dnssec.keep_removed }} +{% endif %} {% if cfg.dnssec.refresh_time %} -- dnssec.refresh-time diff --git a/manager/pyproject.toml b/manager/pyproject.toml index 3b63350f..1da53a48 100644 --- a/manager/pyproject.toml +++ b/manager/pyproject.toml @@ -58,6 +58,7 @@ kresctl = 'knot_resolver_manager.cli.main:main' knot-resolver = 'knot_resolver_manager.__main__:run' [tool.poe.tasks] +configure = { cmd = "scripts/meson-configure", help = "Configure Knot Resolver daemon" } run = { cmd = "scripts/run", help = "Run the manager" } run-debug = { cmd = "scripts/run-debug", help = "Run the manager under debugger" } docs = { cmd = "scripts/docs", help = "Create HTML documentation" } diff --git a/manager/scripts/_env.sh b/manager/scripts/_env.sh index ddbbcf55..cabc0025 100644 --- a/manager/scripts/_env.sh +++ b/manager/scripts/_env.sh @@ -36,17 +36,23 @@ set -o nounset function build_kresd { - echo - echo Building Knot Resolver - echo ---------------------- - echo -e "${blue}In case of an compilation error, run this command to try to fix it:${reset}" - echo -e "\t${blue}rm -r $(realpath .install_kresd) $(realpath .build_kresd)${reset}" - echo pushd .. - mkdir -p manager/.build_kresd manager/.install_kresd - meson manager/.build_kresd --prefix=$(realpath manager/.install_kresd) --default-library=static --buildtype=debug - ninja -C manager/.build_kresd - ninja install -C manager/.build_kresd - export PYTHONPATH="$(realpath manager/.build_kresd/python):${PYTHONPATH:-}" + if [ -d manager/.build_kresd ]; then + echo + echo Building Knot Resolver + echo ---------------------- + echo -e "${blue}In case of an compilation error, run this command to try to fix it:${reset}" + echo -e "\t${blue}rm -r $(realpath .install_kresd) $(realpath .build_kresd)${reset}" + echo + ninja -C manager/.build_kresd + ninja install -C manager/.build_kresd + export PYTHONPATH="$(realpath manager/.build_kresd/python):${PYTHONPATH:-}" + else + echo + echo Knot Resolver daemon is not configured. + echo "Please run './poe configure' (optionally with additional Meson arguments)" + echo + exit 2 + fi popd } diff --git a/manager/scripts/meson-configure b/manager/scripts/meson-configure new file mode 100755 index 00000000..c99ddea7 --- /dev/null +++ b/manager/scripts/meson-configure @@ -0,0 +1,11 @@ +#!/bin/bash + +# ensure consistent behaviour +src_dir="$(dirname "$(realpath "$0")")" +source $src_dir/_env.sh + +pushd .. +meson setup manager/.build_kresd --reconfigure --prefix=$(realpath manager/.install_kresd) "$@" +popd + +build_kresd diff --git a/meson.build b/meson.build index 77ca8138..b5158fb1 100644 --- a/meson.build +++ b/meson.build @@ -181,6 +181,13 @@ add_project_arguments( language: 'c', ) +if meson.get_compiler('c').get_id() == 'gcc' + add_project_arguments( + '-Wno-nonnull-compare', # reasonable to do in assertions + language: 'c', + ) +endif + # Files for clang-tidy lint c_src_lint = files() @@ -255,7 +262,6 @@ subdir('lib') ## Remaining code subdir('daemon') subdir('modules') -subdir('manager') subdir('python') subdir('utils') if get_option('bench') == 'enabled' @@ -341,7 +347,6 @@ run_target( # https://github.com/mesonbuild/meson/issues/2404 s_managed_ta = managed_ta ? 'enabled' : 'disabled' s_install_root_keys = install_root_keys ? 'enabled' : 'disabled' -s_build_manager = build_manager ? 'enabled' : 'disabled' s_build_utils = build_utils ? 'enabled' : 'disabled' s_build_dnstap = build_dnstap ? 'enabled' : 'disabled' s_build_unit_tests = build_unit_tests ? 'enabled' : 'disabled' @@ -376,7 +381,6 @@ message(''' cache_dir: @0@'''.format(systemd_cache_dir) + ''' optional components - manager: @0@'''.format(s_build_manager) + ''' utils: @0@'''.format(s_build_utils) + ''' dnstap: @0@'''.format(s_build_dnstap) + ''' unit_tests: @0@'''.format(s_build_unit_tests) + ''' diff --git a/meson_options.txt b/meson_options.txt index 428d5aaa..0f404850 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -126,18 +126,6 @@ option( ) option( - 'manager', - type: 'combo', - choices: [ - 'auto', - 'enabled', - 'disabled', - ], - value: 'disabled', - description: 'build manager and its features', -) - -option( 'utils', type: 'combo', choices: [ diff --git a/modules/dnstap/dnstap.c b/modules/dnstap/dnstap.c index ab52bca3..6fcc192c 100644 --- a/modules/dnstap/dnstap.c +++ b/modules/dnstap/dnstap.c @@ -193,6 +193,7 @@ static int dnstap_log(kr_layer_t *ctx, enum dnstap_log_phase phase) { m.socket_family = DNSTAP__SOCKET_FAMILY__INET6; m.has_socket_family = true; break; + default:; } } diff --git a/modules/hints/meson.build b/modules/hints/meson.build index d5046cb4..7e681f11 100644 --- a/modules/hints/meson.build +++ b/modules/hints/meson.build @@ -18,5 +18,5 @@ hints_mod = shared_module( ) config_tests += [ - ['hints', files('tests/hints.test.lua'), ['skip_asan']], + ['hints', files('tests/hints.test.lua')], ] diff --git a/modules/http/meson.build b/modules/http/meson.build index 9d20c929..7d892159 100644 --- a/modules/http/meson.build +++ b/modules/http/meson.build @@ -21,7 +21,7 @@ lua_mod_src += [ config_tests += [ ['http', files('http.test.lua')], ['http.doh', files('http_doh.test.lua')], - ['http.tls', files('test_tls/tls.test.lua')], + ['http.tls', files('test_tls/tls.test.lua'), ['skip_asan']], ] # install static files diff --git a/modules/stats/stats.c b/modules/stats/stats.c index a8a29de2..deed9c94 100644 --- a/modules/stats/stats.c +++ b/modules/stats/stats.c @@ -46,8 +46,9 @@ X(answer,aa) X(answer,tc) X(answer,rd) X(answer,ra) X(answer, ad) X(answer,cd) \ X(answer,edns0) X(answer,do) \ X(query,edns) X(query,dnssec) \ - X(request,total) X(request,udp) X(request,tcp) X(request,xdp) \ - X(request,dot) X(request,doh) X(request,internal) \ + X(request,total) X(request,total4) X(request,total6) X(request,internal) \ + X(request,udp4) X(request,tcp4) X(request,xdp4) X(request,dot4) X(request,doh4) \ + X(request,udp6) X(request,tcp6) X(request,xdp6) X(request,dot6) X(request,doh6) \ X(const,end) enum const_metric { @@ -72,6 +73,29 @@ static struct const_metric_elm const_metrics[] = { CONST_METRICS(X) #undef X }; + +/// These metrics are read-only views, each simply summing a pair of const_metrics items. +struct sum_metric { + const char *sub_key; + const size_t *val1, *val2; +}; +static const struct sum_metric sum_metrics[] = { + // We're using this to aggregate v4 + v6 pairs. + #define DEF(proto) { \ + .sub_key = #proto, \ + .val1 = &const_metrics[metric_request_ ## proto ## 4].val, \ + .val2 = &const_metrics[metric_request_ ## proto ## 6].val, \ + } + DEF(udp), + DEF(tcp), + DEF(xdp), + DEF(dot), + DEF(doh), + #undef DEF +}; +static const size_t sum_metrics_len = sizeof(sum_metrics) / sizeof(sum_metrics[0]); +#define SUM_METRICS_SUP_KEY "request" + /** @endcond */ /** @internal LRU hash of most frequent names. */ @@ -125,7 +149,7 @@ static inline int collect_key(char *key, const knot_dname_t *name, uint16_t type if (key_len < 0) { return kr_error(key_len); } - return key_len + sizeof(type); + return key_len + (int)sizeof(type); } static void collect_sample(struct stat_data *data, struct kr_rplan *rplan) @@ -193,19 +217,26 @@ static int collect_transport(kr_layer_t *ctx) } /** - * Count each transport only once, + * Apart from the "total" stats, count each transport only once, * i.e. DoT does not count as TCP and XDP does not count as UDP. + * We have two counts for each - IPv6 and IPv4 separately. */ + const bool isIPv6 = req->qsource.addr->sa_family == AF_INET6; + #define INC_PROTO(proto) \ + stat_const_add(data, isIPv6 ? metric_request_ ## proto ## 6 \ + : metric_request_ ## proto ## 4, 1) + INC_PROTO(total); if (req->qsource.flags.http) - stat_const_add(data, metric_request_doh, 1); + INC_PROTO(doh); else if (req->qsource.flags.tls) - stat_const_add(data, metric_request_dot, 1); + INC_PROTO(dot); else if (req->qsource.flags.tcp) - stat_const_add(data, metric_request_tcp, 1); + INC_PROTO(tcp); else if (req->qsource.flags.xdp) - stat_const_add(data, metric_request_xdp, 1); + INC_PROTO(xdp); else - stat_const_add(data, metric_request_udp, 1); + INC_PROTO(udp); + #undef INC_PROTO return ctx->state; } @@ -282,6 +313,7 @@ static int collect(kr_layer_t *ctx) * Set nominal value of a key. * * Input: { key, val } + * Aggregate metrics can't be set. * */ static char* stats_set(void *env, struct kr_module *module, const char *args) @@ -323,26 +355,36 @@ static char* stats_get(void *env, struct kr_module *module, const char *args) struct stat_data *data = module->data; /* Expecting CHAR_BIT to be 8, this is a safe bet */ - char *ret = malloc(3 * sizeof(size_t) + 2); - if (!ret) { - return NULL; - } + char *str_value = NULL; + int ret = 0; /* Check if it exists in const map. */ for (unsigned i = 0; i < metric_const_end; ++i) { if (strcmp(const_metrics[i].key, args) == 0) { - sprintf(ret, "%zu", const_metrics[i].val); - return ret; + ret = asprintf(&str_value, "%zu", const_metrics[i].val); + if (ret < 0) + return NULL; + return str_value; + } + } + /* Check if it exists in aggregate metrics. */ + for (int i = 0; i < sum_metrics_len; ++i) { + const struct sum_metric *smi = &sum_metrics[i]; + if (strcmp(smi->sub_key, args) == 0) { + ret = asprintf(&str_value, "%zu", *smi->val1 + *smi->val2); + if (ret < 0) + return NULL; + return str_value; } } /* Check in variable map */ trie_val_t *val = trie_get_try(data->trie, args, strlen(args)); - if (!val) { - free(ret); + if (!val) return NULL; - } - sprintf(ret, "%zu", (size_t) *val); - return ret; + ret = asprintf(&str_value, "%zu", (size_t) *val); + if (ret < 0) + return NULL; + return str_value; } /** Checks whether: @@ -360,13 +402,31 @@ struct list_entry_context { size_t key_prefix_len; /**< Prefix length. Prefix is a wildcard if zero. */ }; +/** Ensures that the `root` node contains an object (and only an object - not a + * number, array, or anything else) with the specified `key`. If this cannot be + * ensured, fails on an assertion, or returns `NULL` if asserts are disabled. */ +static JsonNode *ensure_object(JsonNode *root, const char *key) +{ + JsonNode *node = json_find_member(root, key); + if (node) { + if (kr_fails_assert(node->tag == JSON_OBJECT)) + return NULL; + } else { + node = json_mkobject(); + if (kr_fails_assert(node)) + return NULL; + json_append_member(root, key, node); + } + return node; +} + /** Inserts the entry with a matching key into the JSON object. */ static int list_entry(const char *key, uint32_t key_len, trie_val_t *val, void *baton) { struct list_entry_context *ctx = baton; if (!key_matches_prefix(key, key_len, ctx->key_prefix, ctx->key_prefix_len)) return 0; - size_t number = (size_t) *val; + size_t number = (size_t)*val; uint32_t dot_index = 0; for (uint32_t i = 0; i < key_len; i++) { @@ -380,17 +440,13 @@ static int list_entry(const char *key, uint32_t key_len, trie_val_t *val, void * if (dot_index) { auto_free char *sup_key_nt = strndup(key, dot_index); auto_free char *sub_key_nt = strndup(key + dot_index + 1, key_len - dot_index - 1); - JsonNode *sup = json_find_member(ctx->root, sup_key_nt); - if (!sup) { - sup = json_mkobject(); - json_append_member(ctx->root, sup_key_nt, sup); - } - if (kr_fails_assert(sup)) + JsonNode *sup = ensure_object(ctx->root, sup_key_nt); + if (!sup) return 0; - json_append_member(sup, sub_key_nt, json_mknumber(number)); + json_append_member(sup, sub_key_nt, json_mknumber((double)number)); } else { auto_free char *key_nt = strndup(key, key_len); - json_append_member(ctx->root, key_nt, json_mknumber(number)); + json_append_member(ctx->root, key_nt, json_mknumber((double)number)); } return 0; } @@ -402,22 +458,36 @@ static int list_entry(const char *key, uint32_t key_len, trie_val_t *val, void * */ static char* stats_list(void *env, struct kr_module *module, const char *args) { + char *ret; JsonNode *root = json_mkobject(); /* Walk const metrics map */ size_t args_len = args ? strlen(args) : 0; for (unsigned i = 0; i < metric_const_end; ++i) { struct const_metric_elm *elm = &const_metrics[i]; if (!args || strcmp(elm->sup_key, args) == 0) { - JsonNode *sup = json_find_member(root, elm->sup_key); + JsonNode *sup = ensure_object(root, elm->sup_key); if (!sup) { - sup = json_mkobject(); - json_append_member(root, elm->sup_key, sup); + ret = strdup("\"ERROR\""); + goto exit; } - if (kr_fails_assert(sup)) - break; - json_append_member(sup, elm->sub_key, json_mknumber(elm->val)); + json_append_member(sup, elm->sub_key, json_mknumber((double)elm->val)); } } + + /* Walk sum metrics map */ + JsonNode *sum_sup = ensure_object(root, SUM_METRICS_SUP_KEY); + if (!sum_sup) { + ret = strdup("\"ERROR\""); + goto exit; + } + for (int i = 0; i < sum_metrics_len; ++i) { + const struct sum_metric *elm = &sum_metrics[i]; + if (!args || strncmp(elm->sub_key, args, args_len) == 0) { + size_t val = *elm->val1 + *elm->val2; + json_append_member(sum_sup, elm->sub_key, json_mknumber(val)); + } + } + struct list_entry_context ctx = { .root = root, .key_prefix = args, @@ -425,7 +495,8 @@ static char* stats_list(void *env, struct kr_module *module, const char *args) }; struct stat_data *data = module->data; trie_apply_with_key(data->trie, list_entry, &ctx); - char *ret = json_encode(root); + ret = json_encode(root); +exit: json_delete(root); return ret; } diff --git a/tests/config/meson.build b/tests/config/meson.build index a739222d..dc345a88 100644 --- a/tests/config/meson.build +++ b/tests/config/meson.build @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later config_tests += [ - ['basic', files('basic.test.lua'), ['skip_asan']], - ['cache', files('cache.test.lua'), ['skip_asan']], + ['basic', files('basic.test.lua')], + ['cache', files('cache.test.lua')], ['net', files('net.test.lua'), ['config_net']], ['doh2', files('doh2.test.lua')], ['lru', files('lru.test.lua')], diff --git a/tests/dnstap/src/dnstap-test/go.mod b/tests/dnstap/src/dnstap-test/go.mod index 6b650889..2eb72879 100644 --- a/tests/dnstap/src/dnstap-test/go.mod +++ b/tests/dnstap/src/dnstap-test/go.mod @@ -1,6 +1,6 @@ module gitlab.nic.cz/knot/knot-resolver/tests/dnstap-test -go 1.17 +go 1.15 require ( github.com/cloudflare/dns v0.0.0-20151007113418-e20ffa3da443 diff --git a/tests/dnstap/src/dnstap-test/go.sum b/tests/dnstap/src/dnstap-test/go.sum deleted file mode 100644 index 1860f9ef..00000000 --- a/tests/dnstap/src/dnstap-test/go.sum +++ /dev/null @@ -1,44 +0,0 @@ -github.com/cloudflare/dns v0.0.0-20151007113418-e20ffa3da443 h1:dYR6/V5rx/uaHsy4m1JuWfKYZO0r+G89BLD+XN7s9AI= -github.com/cloudflare/dns v0.0.0-20151007113418-e20ffa3da443/go.mod h1:pa4p3oKOxzbXjrV5AGD1v5xjL7skv9BvO4J0Llo3P+s= -github.com/dnstap/golang-dnstap v0.4.0 h1:KRHBoURygdGtBjDI2w4HifJfMAhhOqDuktAokaSa234= -github.com/dnstap/golang-dnstap v0.4.0/go.mod h1:FqsSdH58NAmkAvKcpyxht7i4FoBjKu8E4JUPt8ipSUs= -github.com/farsightsec/golang-framestream v0.3.0 h1:/spFQHucTle/ZIPkYqrfshQqPe2VQEzesH243TjIwqA= -github.com/farsightsec/golang-framestream v0.3.0/go.mod h1:eNde4IQyEiA5br02AouhEHCu3p3UzrCdFR4LuQHklMI= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= -github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/tests/dnstap/src/dnstap-test/run.sh b/tests/dnstap/src/dnstap-test/run.sh index 37822b75..70d82254 100755 --- a/tests/dnstap/src/dnstap-test/run.sh +++ b/tests/dnstap/src/dnstap-test/run.sh @@ -8,16 +8,13 @@ echo "$GOPATH" cd "$(dirname $0)" DNSTAP_TEST=dnstap-test -if [ -z "$GITLAB_CI" ]; then - type -P go >/dev/null || exit 77 - echo "Building the dnstap test and its dependencies..." - # some packages may be missing on the system right now - go get . -else - # In CI we've prebuilt dependencies into the default GOPATH. - # We're in a scratch container, so we just add the dnstap test inside. - export GOPATH=/root/go -fi +go mod tidy + +type -P go >/dev/null || exit 77 +echo "Building the dnstap test and its dependencies..." +# some packages may be missing on the system right now +go get . + DTAP_DIR="$GOPATH/src" DTAP="$DTAP_DIR/$DNSTAP_TEST" mkdir -p "$DTAP_DIR" diff --git a/tests/pytests/conftest.py b/tests/pytests/conftest.py index 4c711f84..fcf4b05f 100644 --- a/tests/pytests/conftest.py +++ b/tests/pytests/conftest.py @@ -86,7 +86,7 @@ def query_before(request): # whether to send an initial query return request.param -@pytest.mark.optionalhook +@pytest.hookimpl(optionalhook=True) def pytest_metadata(metadata): # filter potentially sensitive data from GitLab CI keys_to_delete = [] for key in metadata.keys(): diff --git a/tests/pytests/kresd.py b/tests/pytests/kresd.py index ca15e0d7..21d96455 100644 --- a/tests/pytests/kresd.py +++ b/tests/pytests/kresd.py @@ -312,5 +312,7 @@ KRESD_LOG_IO_CLOSE = re.compile(r'^\[io \].*closed by peer.*') @contextmanager def make_kresd(workdir, certname=None, ip='127.0.0.1', ip6='::1', **kwargs): with Kresd(workdir, ip=ip, ip6=ip6, certname=certname, **kwargs) as kresd: - yield kresd - print(kresd.partial_log()) + try: + yield kresd + finally: + print(kresd.partial_log()) diff --git a/tests/pytests/test_tls.py b/tests/pytests/test_tls.py index 3e1328ab..2187efbc 100644 --- a/tests/pytests/test_tls.py +++ b/tests/pytests/test_tls.py @@ -1,15 +1,8 @@ # SPDX-License-Identifier: GPL-3.0-or-later """TLS-specific tests""" -import itertools -import os -from socket import AF_INET, AF_INET6 import ssl -import sys - import pytest - -from kresd import make_kresd import utils @@ -41,43 +34,3 @@ def test_tls_cert_hostname_mismatch(kresd_tt, sock_family): with pytest.raises(ssl.CertificateError): ssock.connect(dest) - - -@pytest.mark.skipif(sys.version_info < (3, 6), - reason="requires python3.6 or higher") -@pytest.mark.parametrize('sf1, sf2, sf3', itertools.product( - [AF_INET, AF_INET6], [AF_INET, AF_INET6], [AF_INET, AF_INET6])) -def test_tls_session_resumption(tmpdir, sf1, sf2, sf3): - """Attempt TLS session resumption against the same kresd instance and a different one.""" - # TODO ensure that session can't be resumed after session ticket key regeneration - # at the first kresd instance - - # NOTE TLS 1.3 is intentionally disabled for session resumption tests, - # because python's SSLSocket.session isn't compatible with TLS 1.3 - # https://docs.python.org/3/library/ssl.html?highlight=ssl%20ticket#tls-1-3 - - def connect(kresd, ctx, sf, session=None): - sock, dest = kresd.stream_socket(sf, tls=True) - ssock = ctx.wrap_socket( - sock, server_hostname='transport-test-server.com', session=session) - ssock.connect(dest) - new_session = ssock.session - assert new_session.has_ticket - assert ssock.session_reused == (session is not None) - utils.ping_alive(ssock) - ssock.close() - return new_session - - workdir = os.path.join(str(tmpdir), 'kresd') - os.makedirs(workdir) - - with make_kresd(workdir, 'tt') as kresd: - ctx = utils.make_ssl_context( - verify_location=kresd.tls_cert_path, extra_options=[ssl.OP_NO_TLSv1_3]) - session = connect(kresd, ctx, sf1) # initial conn - connect(kresd, ctx, sf2, session) # resume session on the same instance - - workdir2 = os.path.join(str(tmpdir), 'kresd2') - os.makedirs(workdir2) - with make_kresd(workdir2, 'tt') as kresd2: - connect(kresd2, ctx, sf3, session) # resume session on a different instance diff --git a/tests/pytests/utils.py b/tests/pytests/utils.py index 4b995d4b..8af71aad 100644 --- a/tests/pytests/utils.py +++ b/tests/pytests/utils.py @@ -99,7 +99,7 @@ def ping_alive(sock, msgid=None): @contextmanager def expect_kresd_close(rst_ok=False): - with pytest.raises(BrokenPipeError): + with pytest.raises((BrokenPipeError, ssl.SSLEOFError)): try: time.sleep(0.2) # give kresd time to close connection with TCP FIN yield @@ -110,17 +110,12 @@ def expect_kresd_close(rst_ok=False): pytest.fail("kresd didn't close the connection") -def make_ssl_context(insecure=False, verify_location=None, extra_options=None): - # set TLS v1.2+ - context = ssl.SSLContext(ssl.PROTOCOL_TLS) - context.options |= ssl.OP_NO_SSLv2 - context.options |= ssl.OP_NO_SSLv3 - context.options |= ssl.OP_NO_TLSv1 - context.options |= ssl.OP_NO_TLSv1_1 - - if extra_options is not None: - for option in extra_options: - context.options |= option +def make_ssl_context(insecure=False, verify_location=None, + minimum_tls=ssl.TLSVersion.TLSv1_2, + maximum_tls=ssl.TLSVersion.MAXIMUM_SUPPORTED): + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.minimum_version = minimum_tls + context.maximum_version = maximum_tls if insecure: # turn off certificate verification diff --git a/utils/cache_gc/categories.c b/utils/cache_gc/categories.c index 19dec45c..aaa1ae53 100644 --- a/utils/cache_gc/categories.c +++ b/utils/cache_gc/categories.c @@ -18,7 +18,7 @@ static bool rrtype_is_infrastructure(uint16_t r) } } -static int get_random(int to) +static unsigned int get_random(int to) { // We don't need these to be really unpredictable, // but this should be cheap enough not to be noticeable. diff --git a/utils/cache_gc/db.c b/utils/cache_gc/db.c index 76a2b5fa..0e8f90c1 100644 --- a/utils/cache_gc/db.c +++ b/utils/cache_gc/db.c @@ -9,11 +9,13 @@ #include <time.h> #include <sys/stat.h> +#define MDB_FILE "/data.mdb" + int kr_gc_cache_open(const char *cache_path, struct kr_cache *kres_db, knot_db_t ** libknot_db) { - char cache_data[strlen(cache_path) + 10]; - snprintf(cache_data, sizeof(cache_data), "%s/data.mdb", cache_path); + char cache_data[strlen(cache_path) + sizeof(MDB_FILE)]; + (void)snprintf(cache_data, sizeof(cache_data), "%s" MDB_FILE, cache_path); struct stat st = { 0 }; if (stat(cache_path, &st) || !(st.st_mode & S_IFDIR) diff --git a/utils/cache_gc/kr_cache_gc.c b/utils/cache_gc/kr_cache_gc.c index 5978345c..4097c802 100644 --- a/utils/cache_gc/kr_cache_gc.c +++ b/utils/cache_gc/kr_cache_gc.c @@ -8,20 +8,12 @@ // libknot includes #include <libknot/libknot.h> -// dynarray is inside libknot since 3.1, but it's differently named -#ifdef knot_dynarray_declare - #define dynarray_declare knot_dynarray_declare - #define dynarray_define knot_dynarray_define - #define dynarray_foreach knot_dynarray_foreach -#else - #include <contrib/dynarray.h> -#endif - // resolver includes #include <lib/cache/api.h> #include <lib/cache/impl.h> #include <lib/defines.h> #include "lib/cache/cdb_lmdb.h" +#include "lib/generic/array.h" #include "lib/utils.h" #include "kr_cache_gc.h" @@ -43,41 +35,40 @@ static knot_db_val_t *dbval_copy(const knot_db_val_t * from) } // section: rrtype list +typedef array_t(uint16_t) rrtype_array_t; -dynarray_declare(rrtype, uint16_t, DYNARRAY_VISIBILITY_STATIC, 64) - dynarray_define(rrtype, uint16_t, DYNARRAY_VISIBILITY_STATIC) -static void rrtypelist_add(rrtype_dynarray_t * arr, uint16_t add_type) +static void rrtypelist_add(rrtype_array_t *arr, uint16_t add_type) { bool already_present = false; - dynarray_foreach(rrtype, uint16_t, i, *arr) { - if (*i == add_type) { + for (size_t i = 0; i < arr->len; i++) { + if (arr->at[i] == add_type) { already_present = true; break; } } if (!already_present) { - rrtype_dynarray_add(arr, &add_type); + kr_require(array_push(*arr, add_type) >= 0); } } -static void rrtypelist_print(rrtype_dynarray_t * arr) +static void rrtypelist_print(rrtype_array_t *arr) { char type_s[32] = { 0 }; - dynarray_foreach(rrtype, uint16_t, i, *arr) { - knot_rrtype_to_string(*i, type_s, sizeof(type_s)); + for (size_t i = 0; i < arr->len; i++) { + knot_rrtype_to_string(arr->at[i], type_s, sizeof(type_s)); printf(" %s", type_s); } printf("\n"); } -dynarray_declare(entry, knot_db_val_t *, DYNARRAY_VISIBILITY_STATIC, 256) - dynarray_define(entry, knot_db_val_t *, DYNARRAY_VISIBILITY_STATIC) -static void entry_dynarray_deep_free(entry_dynarray_t * d) +typedef array_t(knot_db_val_t *) entry_array_t; + +static void entry_array_deep_free(entry_array_t *d) { - dynarray_foreach(entry, knot_db_val_t *, i, *d) { - free(*i); + for (size_t i = 0; i < d->len; i++) { + free(d->at[i]); } - entry_dynarray_free(d); + array_clear(*d); } typedef struct { @@ -98,7 +89,7 @@ int cb_compute_categories(const knot_db_val_t * key, gc_record_info_t * info, typedef struct { category_t limit_category; - entry_dynarray_t to_delete; + entry_array_t to_delete; size_t cfg_temp_keys_space; size_t used_space; size_t oversize_records; @@ -117,7 +108,7 @@ int cb_delete_categories(const knot_db_val_t * key, gc_record_info_t * info, ctx->oversize_records++; free(todelete); } else { - entry_dynarray_add(&ctx->to_delete, &todelete); + kr_require(array_push(ctx->to_delete, todelete) >= 0); ctx->used_space = used; } } @@ -194,12 +185,12 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state) // Mixing ^^ page usage and entry sizes (key+value lengths) didn't work // too well, probably due to internal fragmentation after some GC cycles. // Therefore let's scale this by the ratio of these two sums. - ssize_t cats_sumsize = 0; + size_t cats_sumsize = 0; for (int i = 0; i < CATEGORIES; ++i) { cats_sumsize += cats.categories_sizes[i]; } /* use less precise variant to avoid 32-bit overflow */ - ssize_t amount_tofree = cats_sumsize / 100 * cfg->cache_to_be_freed; + size_t amount_tofree = cats_sumsize / 100 * cfg->cache_to_be_freed; kr_log_debug(CACHE, "tofree: %zd / %zd\n", amount_tofree, cats_sumsize); if (VERBOSE_STATUS) { @@ -212,8 +203,11 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state) } category_t limit_category = CATEGORIES; - while (limit_category > 0 && amount_tofree > 0) { - amount_tofree -= cats.categories_sizes[--limit_category]; + while (limit_category > 0) { + size_t cat_size = cats.categories_sizes[--limit_category]; + if (cat_size > amount_tofree) + break; + amount_tofree -= cat_size; } printf("Cache analyzed in %.0lf msecs, %zu records, limit category is %d.\n", @@ -226,13 +220,13 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state) to_del.limit_category = limit_category; ret = kr_gc_cache_iter(db, cfg, cb_delete_categories, &to_del); if (ret != KNOT_EOK) { - entry_dynarray_deep_free(&to_del.to_delete); + entry_array_deep_free(&to_del.to_delete); kr_cache_gc_free_state(state); return ret; } printf ("%zu records to be deleted using %.2lf MBytes of temporary memory, %zu records skipped due to memory limit.\n", - to_del.to_delete.size, ((double)to_del.used_space / 1048576.0), + to_del.to_delete.len, ((double)to_del.used_space / 1048576.0), to_del.oversize_records); //// 4. execute the planned deletions. @@ -242,23 +236,24 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state) kr_timer_start(&timer_delete); kr_timer_start(&timer_rw_txn); - rrtype_dynarray_t deleted_rrtypes = { 0 }; + rrtype_array_t deleted_rrtypes = { 0 }; ret = api->txn_begin(db, &txn, 0); if (ret != KNOT_EOK) { printf("Error starting R/W DB transaction (%s).\n", knot_strerror(ret)); - entry_dynarray_deep_free(&to_del.to_delete); + entry_array_deep_free(&to_del.to_delete); kr_cache_gc_free_state(state); return ret; } - dynarray_foreach(entry, knot_db_val_t *, i, to_del.to_delete) { - ret = api->del(&txn, *i); + for (size_t i = 0; i < to_del.to_delete.len; i++) { + knot_db_val_t *val = to_del.to_delete.at[i]; + ret = api->del(&txn, val); switch (ret) { case KNOT_EOK: deleted_records++; - const int entry_type = kr_gc_key_consistent(**i); + const int entry_type = kr_gc_key_consistent(*val); if (entry_type >= 0) // some "inconsistent" entries are OK rrtypelist_add(&deleted_rrtypes, entry_type); break; @@ -267,8 +262,8 @@ int kr_cache_gc(kr_cache_gc_cfg_t *cfg, kr_cache_gc_state_t **state) if (VERBOSE_STATUS) { // kresd normally only inserts (or overwrites), // so it's generally suspicious when a key goes missing. - printf("Record already gone (key len %zu): ", (*i)->len); - debug_printbin((*i)->data, (*i)->len); + printf("Record already gone (key len %zu): ", val->len); + debug_printbin(val->data, val->len); printf("\n"); } break; @@ -316,8 +311,8 @@ finish: printf("It took %.0lf msecs, %zu transactions (%s)\n\n", kr_timer_elapsed(&timer_delete) * 1000, rw_txn_count, knot_strerror(ret)); - rrtype_dynarray_free(&deleted_rrtypes); - entry_dynarray_deep_free(&to_del.to_delete); + array_clear(deleted_rrtypes); + entry_array_deep_free(&to_del.to_delete); // OK, let's close it in this case. kr_cache_gc_free_state(state); diff --git a/utils/cache_gc/main.c b/utils/cache_gc/main.c index 5adf19f0..fe131cd0 100644 --- a/utils/cache_gc/main.c +++ b/utils/cache_gc/main.c @@ -13,6 +13,7 @@ #include "kr_cache_gc.h" static volatile int killed = 0; +static volatile int exit_code = 0; static void got_killed(int signum) { @@ -21,12 +22,10 @@ static void got_killed(int signum) case 1: break; case 2: - exit(5); + exit_code = 5; break; - case 3: - abort(); default: - kr_assert(false); + abort(); } } @@ -60,16 +59,20 @@ int main(int argc, char *argv[]) { printf("Knot Resolver Cache Garbage Collector, version %s\n", PACKAGE_VERSION); if (setvbuf(stdout, NULL, _IONBF, 0) || setvbuf(stderr, NULL, _IONBF, 0)) { - fprintf(stderr, "Failed to to set output buffering (ignored): %s\n", + (void)fprintf(stderr, "Failed to to set output buffering (ignored): %s\n", strerror(errno)); - fflush(stderr); + (void)fflush(stderr); } - signal(SIGTERM, got_killed); - signal(SIGKILL, got_killed); - signal(SIGPIPE, got_killed); - signal(SIGCHLD, got_killed); - signal(SIGINT, got_killed); + struct sigaction act = { + .sa_handler = got_killed, + .sa_flags = SA_RESETHAND, + }; + sigemptyset(&act.sa_mask); + kr_assert(!sigaction(SIGTERM, &act, NULL)); + kr_assert(!sigaction(SIGPIPE, &act, NULL)); + kr_assert(!sigaction(SIGCHLD, &act, NULL)); + kr_assert(!sigaction(SIGINT, &act, NULL)); kr_cache_gc_cfg_t cfg = { .ro_txn_items = 200, @@ -131,7 +134,6 @@ int main(int argc, char *argv[]) return 1; } - int exit_code = 0; kr_cache_gc_state_t *gc_state = NULL; bool last_espace = false; do { |