pax_global_header00006660000000000000000000000064145267354600014526gustar00rootroot0000000000000052 comment=e4785bd5430e7f3006bb537e95c2a0f9311f85bc containers-aardvark-dns-4fd01fd/000077500000000000000000000000001452673546000167405ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/.cirrus.yml000066400000000000000000000222211452673546000210470ustar00rootroot00000000000000--- # Format Ref: https://cirrus-ci.org/guide/writing-tasks/ # Main collection of env. vars to set for all tasks and scripts. env: # Actual|intended branch for this run DEST_BRANCH: "main" # The default is 'sh' if unspecified CIRRUS_SHELL: "/bin/bash" # Location where source repo. will be cloned CIRRUS_WORKING_DIR: "/var/tmp/aardvark-dns" # Rust package cache also lives here CARGO_HOME: "/var/cache/cargo" # Rust compiler output lives here (see Makefile) CARGO_TARGET_DIR: "$CIRRUS_WORKING_DIR/targets" # Testing depends on the latest netavark binary from upstream CI NETAVARK_BRANCH: "main" NETAVARK_URL: "https://api.cirrus-ci.com/v1/artifact/github/containers/netavark/success/binary.zip?branch=${NETAVARK_BRANCH}" # Save a little typing (path relative to $CIRRUS_WORKING_DIR) SCRIPT_BASE: "./contrib/cirrus" IMAGE_SUFFIX: "c20231116t174419z-f39f38d13" FEDORA_NETAVARK_IMAGE: "fedora-netavark-${IMAGE_SUFFIX}" FEDORA_NETAVARK_AMI: "fedora-netavark-aws-arm64-${IMAGE_SUFFIX}" EC2_INST_TYPE: "t4g.xlarge" gcp_credentials: ENCRYPTED[f6a0e4101418bec8180783b208721fc990772817364fed0346f5fd126bf0cfca03738dd8c7fb867944637a1eac7cec37] aws_credentials: ENCRYPTED[3fab904a98355f84b0bac084f8a50428ff8a27dd2b6a6c42fca77df89010e620a1da3cd246a50b1074e6787c42818080] build_task: alias: "build" # Compiling is very CPU intensive, make it chooch quicker for this task only gce_instance: &standard_build_gce_x86_64 image_project: "libpod-218412" zone: "us-central1-c" disk: 200 # GB, do not set <200 per gcloud warning re: I/O performance cpu: 8 memory: "8Gb" image_name: "${FEDORA_NETAVARK_IMAGE}" cargo_cache: &cargo_cache folder: "$CARGO_HOME" fingerprint_script: echo -e "cargo_v3_${DEST_BRANCH}_amd64\n---\n$(- aardvark-dns aardvark-dns.debug aardvark-dns.info aardvark-dns.aarch64-unknown-linux-gnu aardvark-dns.debug.aarch64-unknown-linux-gnu aardvark-dns.info.aarch64-unknown-linux-gnu bin_cache: *ro_bin_cache clone_script: *noop # The paths used for uploaded artifacts are relative here and in Cirrus artifacts_prep_script: - set -x - curl --fail --location -o /tmp/armbinary.zip ${API_URL_BASE}/build_aarch64/armbinary.zip - unzip /tmp/armbinary.zip - mv bin/* ./ - rm -rf bin artifacts_test_script: # Other CI systems depend on all files being present - ls -la # If there's a missing file, show what it was in the output - for fn in $EXP_BINS; do [[ -r "$(echo $fn|tee /dev/stderr)" ]] || exit 1; done # Upload tested binary for consumption downstream # https://cirrus-ci.org/guide/writing-tasks/#artifacts-instruction binary_artifacts: path: ./aardvark-dns* containers-aardvark-dns-4fd01fd/.fmf/000077500000000000000000000000001452673546000175665ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/.fmf/version000066400000000000000000000000021452673546000211660ustar00rootroot000000000000001 containers-aardvark-dns-4fd01fd/.github/000077500000000000000000000000001452673546000203005ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/.github/renovate.json5000066400000000000000000000041051452673546000231030ustar00rootroot00000000000000/* Renovate is a service similar to GitHub Dependabot, but with (fantastically) more configuration options. So many options in fact, if you're new I recommend glossing over this cheat-sheet prior to the official documentation: https://www.augmentedmind.de/2021/07/25/renovate-bot-cheat-sheet Configuration Update/Change Procedure: 1. Make changes 2. Manually validate changes (from repo-root): podman run -it \ -v ./.github/renovate.json5:/usr/src/app/renovate.json5:z \ docker.io/renovate/renovate:latest \ renovate-config-validator 3. Commit. Configuration Reference: https://docs.renovatebot.com/configuration-options/ Monitoring Dashboard: https://app.renovatebot.com/dashboard#github/containers Note: The Renovate bot will create/manage it's business on branches named 'renovate/*'. Otherwise, and by default, the only the copy of this file that matters is the one on the `main` branch. No other branches will be monitored or touched in any way. */ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", /************************************************* ****** Global/general configuration options ***** *************************************************/ // Re-use predefined sets of configuration options to DRY "extends": [ // https://github.com/containers/automation/blob/main/renovate/defaults.json5 "github>containers/automation//renovate/defaults.json5" ], // Permit automatic rebasing when base-branch changes by more than // one commit. "rebaseWhen": "behind-base-branch", /************************************************* *** Repository-specific configuration options *** *************************************************/ // Don't leave dep. update. PRs "hanging", assign them to people. "assignees": ["containers/netavark-maintainers"], /************************************************** ***** Manager-specific configuration options ***** **************************************************/ } containers-aardvark-dns-4fd01fd/.github/workflows/000077500000000000000000000000001452673546000223355ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/.github/workflows/check_cirrus_cron.yml000066400000000000000000000012431452673546000265450ustar00rootroot00000000000000--- # See also: # https://github.com/containers/podman/blob/main/.github/workflows/check_cirrus_cron.yml on: # Note: This only applies to the default branch. schedule: # N/B: This should correspond to a period slightly after # the last job finishes running. See job defs. at: # https://cirrus-ci.com/settings/repository/5268168076689408 - cron: '03 03 * * 1-5' # Debug: Allow triggering job manually in github-actions WebUI workflow_dispatch: {} jobs: # Ref: https://docs.github.com/en/actions/using-workflows/reusing-workflows call_cron_failures: uses: containers/podman/.github/workflows/check_cirrus_cron.yml@main secrets: inherit containers-aardvark-dns-4fd01fd/.github/workflows/rerun_cirrus_cron.yml000066400000000000000000000012361452673546000266250ustar00rootroot00000000000000--- # See also: https://github.com/containers/podman/blob/main/.github/workflows/rerun_cirrus_cron.yml on: # Note: This only applies to the default branch. schedule: # N/B: This should correspond to a period slightly after # the last job finishes running. See job defs. at: # https://cirrus-ci.com/settings/repository/5268168076689408 - cron: '01 01 * * 1-5' # Debug: Allow triggering job manually in github-actions WebUI workflow_dispatch: {} jobs: # Ref: https://docs.github.com/en/actions/using-workflows/reusing-workflows call_cron_rerun: uses: containers/podman/.github/workflows/rerun_cirrus_cron.yml@main secrets: inherit containers-aardvark-dns-4fd01fd/.gitignore000066400000000000000000000000601452673546000207240ustar00rootroot00000000000000/bin/ target/ targets/ *.swp netavark.1 vendor/ containers-aardvark-dns-4fd01fd/.packit.yaml000066400000000000000000000063151452673546000211620ustar00rootroot00000000000000--- # See the documentation for more information: # https://packit.dev/docs/configuration/ specfile_path: rpm/aardvark-dns.spec upstream_tag_template: v{version} srpm_build_deps: - cargo - make - openssl-devel jobs: - job: copr_build trigger: pull_request notifications: failure_comment: message: "Ephemeral COPR build failed. @containers/packit-build please check." targets: fedora-all-x86_64: {} fedora-all-aarch64: {} fedora-eln-x86_64: additional_repos: - "https://kojipkgs.fedoraproject.org/repos/eln-build/latest/x86_64/" fedora-eln-aarch64: additional_repos: - "https://kojipkgs.fedoraproject.org/repos/eln-build/latest/aarch64/" epel-8-x86_64: {} epel-8-aarch64: {} epel-9-x86_64: {} epel-9-aarch64: {} additional_repos: - "copr://rhcontainerbot/podman-next" enable_net: true # Run on commit to main branch - job: copr_build trigger: commit notifications: failure_comment: message: "podman-next COPR build failed. @containers/packit-build please check." branch: main owner: rhcontainerbot project: podman-next enable_net: true # Validate test - job: tests trigger: pull_request skip_build: true targets: # Only need to test on one target - fedora-latest-stable-x86_64 identifier: validate_test tmt_plan: "/plans/validate_test" # Unit tests - job: tests trigger: pull_request skip_build: true targets: &pr_test_targets - fedora-all-x86_64 - fedora-all-aarch64 - epel-8-x86_64 - epel-8-aarch64 - epel-9-x86_64 - epel-9-aarch64 identifier: unit_test tmt_plan: "/plans/unit_test" # Integration tests - job: tests trigger: pull_request targets: *pr_test_targets identifier: integration_test tmt_plan: "/plans/integration_test" # Unit tests on RHEL - job: tests trigger: pull_request skip_build: true use_internal_tf: true notifications: failure_comment: message: "podman e2e tests failed on RHEL. @containers/packit-build please check." targets: &pr_test_targets_rhel epel-8-aarch64: distros: [RHEL-8.9.0-Nightly,RHEL-8.10.0-Nightly] epel-8-x86_64: distros: [RHEL-8.9.0-Nightly,RHEL-8.10.0-Nightly] epel-9-aarch64: distros: [RHEL-9.3.0-Nightly,RHEL-9.4.0-Nightly] epel-9-x86_64: distros: [RHEL-9.3.0-Nightly,RHEL-9.4.0-Nightly] identifier: unit_test_internal tmt_plan: "/plans/unit_test" # Integration tests on RHEL - job: tests trigger: pull_request use_internal_tf: true notifications: failure_comment: message: "podman system tests failed on RHEL. @containers/packit-build please check." targets: *pr_test_targets_rhel identifier: integration_test_internal tmt_plan: "/plans/integration_test" - job: propose_downstream trigger: release update_release: false dist_git_branches: - fedora-all - job: koji_build trigger: commit dist_git_branches: - fedora-all - job: bodhi_update trigger: commit dist_git_branches: - fedora-branched # rawhide updates are created automatically containers-aardvark-dns-4fd01fd/CODE-OF-CONDUCT.md000066400000000000000000000003071452673546000213730ustar00rootroot00000000000000## The Aardvark-dns Project Community Code of Conduct The Aardvark-dns project follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/main/CODE-OF-CONDUCT.md). containers-aardvark-dns-4fd01fd/Cargo.lock000066400000000000000000000764251452673546000206630ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aardvark-dns" version = "1.9.0" dependencies = [ "anyhow", "async-broadcast", "chrono", "clap", "futures-util", "hickory-client", "hickory-proto", "hickory-server", "libc", "log", "nix", "resolv-conf", "signal-hook", "syslog", "tokio", ] [[package]] name = "addr2line" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anstream" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is-terminal", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c" dependencies = [ "anstyle", "windows-sys", ] [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "async-broadcast" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "334d75cf09b33bede6cbc20e52515853ae7bee3d4eadd9540e13ce92af983d34" dependencies = [ "event-listener", "event-listener-strategy", "futures-core", ] [[package]] name = "async-trait" version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-targets", ] [[package]] name = "clap" version = "4.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487" dependencies = [ "clap_builder", "clap_derive", "once_cell", ] [[package]] name = "clap_builder" version = "4.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "clap_lex" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "concurrent-queue" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ "crossbeam-utils", ] [[package]] name = "core-foundation-sys" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "data-encoding" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "deranged" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ "powerfmt", ] [[package]] name = "endian-type" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[package]] name = "enum-as-inner" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck", "proc-macro2", "quote", "syn", ] [[package]] name = "errno" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ "libc", "windows-sys", ] [[package]] name = "error-chain" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ "version_check", ] [[package]] name = "event-listener" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15c97b4e30ea7e4b7e7b429d6e2d8510433ba8cee4e70dfb3243794e539d29fd" dependencies = [ "event-listener", "pin-project-lite", ] [[package]] name = "form_urlencoded" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-io" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-sink" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-core", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "getrandom" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hickory-client" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f3e08124cf0ddda93b1186d4af73599de401f3b52f14cd9aaa719049379462e" dependencies = [ "cfg-if", "data-encoding", "futures-channel", "futures-util", "hickory-proto", "once_cell", "radix_trie", "rand", "thiserror", "tokio", "tracing", ] [[package]] name = "hickory-proto" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "091a6fbccf4860009355e3efc52ff4acf37a63489aad7435372d44ceeb6fbbcf" dependencies = [ "async-trait", "cfg-if", "data-encoding", "enum-as-inner", "futures-channel", "futures-io", "futures-util", "idna", "ipnet", "once_cell", "rand", "thiserror", "tinyvec", "tokio", "tracing", "url", ] [[package]] name = "hickory-server" version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fbbb45bc4dcb456445732c705e3cfdc7393b8bcae5c36ecec36b9d76bd67cb5" dependencies = [ "async-trait", "bytes", "cfg-if", "enum-as-inner", "futures-util", "hickory-proto", "serde", "thiserror", "time", "tokio", "tokio-util", "tracing", ] [[package]] name = "hostname" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" dependencies = [ "libc", "match_cfg", "winapi", ] [[package]] name = "iana-time-zone" version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "idna" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "ipnet" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", "windows-sys", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "linux-raw-sys" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "match_cfg" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "miniz_oxide" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", "windows-sys", ] [[package]] name = "nibble_vec" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" dependencies = [ "smallvec", ] [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ "bitflags", "cfg-if", "libc", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "num_threads" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" dependencies = [ "libc", ] [[package]] name = "object" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "radix_trie" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" dependencies = [ "endian-type", "nibble_vec", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "resolv-conf" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" dependencies = [ "quick-error", ] [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "serde" version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-registry" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "socket2" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys", ] [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syslog" version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7434e95bcccce1215d30f4bf84fe8c00e8de1b9be4fb736d747ca53d36e7f96f" dependencies = [ "error-chain", "hostname", "libc", "log", "time", ] [[package]] name = "thiserror" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "time" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", "libc", "num_threads", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", "bytes", "libc", "mio", "num_cpus", "pin-project-lite", "socket2", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tokio-util" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "unicode-bidi" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "url" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" containers-aardvark-dns-4fd01fd/Cargo.toml000066400000000000000000000026451452673546000206770ustar00rootroot00000000000000[package] name = "aardvark-dns" # This version specification right below is reused by .packit.sh to generate rpm version version = "1.9.0" edition = "2018" authors = ["github.com/containers"] license = "Apache-2.0" readme = "README.md" description = "A container-focused DNS server" homepage = "https://github.com/containers/aardvark-dns" repository = "https://github.com/containers/aardvark-dns" categories = ["containers", "networking", "dns", "podman"] exclude = ["/.cirrus.yml", "/.github/*"] [package.metadata.vendor-filter] # This list is not exhaustive. platforms = ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu", "s390x-unknown-linux-gnu", "riscv64gc-unknown-linux-gnu", "x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl", ] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] clap = { version = "~4.3.24", features = ["derive"] } syslog = "^6.1.0" log = "0.4.20" hickory-server = "0.24.0" hickory-proto = "0.24.0" hickory-client = "0.24.0" anyhow = "1.0.75" futures-util = { version = "0.3.29", default-features = false } signal-hook = "0.3.17" tokio = { version = "1.34.0", features = ["macros", "rt-multi-thread", "net"] } async-broadcast = "0.6.0" resolv-conf = "0.7.0" nix = { version = "0.27.1", features = ["fs", "signal"] } libc = "0.2" [build-dependencies] chrono = "0.4.31" containers-aardvark-dns-4fd01fd/DISTRO_PACKAGE.md000066400000000000000000000047361452673546000214130ustar00rootroot00000000000000# Aardvark-dns: Authoritative DNS server for A/AAAA container records This document is currently written with Fedora as a reference. As Aardvark-dns gets shipped in other distros, this should become a distro-agnostic document. ## Fedora Users Aardvark-dns is available as an official Fedora package on Fedora 35 and newer versions and is only meant to be used with Podman v4 and newer releases. On Fedora 36 and newer, fresh installations of the podman package will automatically install Aardvark-dns along with Netavark. If Aardvark-dns isn't present on your system, install it using: ```console $ sudo dnf install aardvark-dns ``` **NOTE:** Fedora 35 users will not be able to install Podman v4 using the default yum repositories. Please consult the Podman packaging docs for instructions on how to fetch Podman v4.0 on Fedora 35. If you would like to test the latest unreleased upstream code, try the podman-next COPR: ```console $ sudo dnf copr enable rhcontainerbot/podman-next $ sudo dnf install aardvark-dns ``` **CAUTION:** The podman-next COPR provides the latest unreleased sources of Podman, Aardvark-dns and Aardvark-dns as rpms which would override the versions provided by the official packages. ## Distro Packagers The Fedora packaging sources for Aardvark-dns are available at the [Aardvark-dns dist-git](https://src.fedoraproject.org/rpms/aardvark-dns). The Fedora package builds Aardvark-dns using a compressed tarball of the vendored libraries that is attached to each upstream release. You can download them with the following: `https://github.com/containers/netavark/releases/download/v{version}/aardvark-dns-v{version}.tar.gz` And then create a cargo config file to point it to the vendor dir: ``` tar xvf %{SOURCE} mkdir -p .cargo cat >.cargo/config << EOF [source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" EOF ``` The `aardvark-dns` binary is installed to `/usr/libexec/podman/aardvark-dns`. ## Dependency of netavark package The netavark package has a `Recommends` on the `aardvark-dns` package. The aardvark-dns package will be installed by default with netavark, but Netavark and Podman will be functional without it. ## Listing bundled dependencies If you need to list the bundled dependencies in your packaging sources, you can run the `cargo tree` command in the upstream source. For example, Fedora's packaging source uses: ``` $ cargo tree --prefix none | awk '{print "Provides: bundled(crate("$1")) = "$2}' | sort | uniq ``` containers-aardvark-dns-4fd01fd/LICENSE000066400000000000000000000261351452673546000177540ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. containers-aardvark-dns-4fd01fd/Makefile000066400000000000000000000073461452673546000204120ustar00rootroot00000000000000# This Makefile is intended for developer convenience. For the most part # all the targets here simply wrap calls to the `cargo` tool. Therefore, # most targets must be marked 'PHONY' to prevent `make` getting in the way # #prog :=xnixperms DESTDIR ?= PREFIX ?= /usr/local LIBEXECDIR ?= ${PREFIX}/libexec LIBEXECPODMAN ?= ${LIBEXECDIR}/podman SELINUXOPT ?= $(shell test -x /usr/sbin/selinuxenabled && selinuxenabled && echo -Z) # Get crate version by parsing the line that starts with version. CRATE_VERSION ?= $(shell grep ^version Cargo.toml | awk '{print $$3}') GIT_TAG ?= $(shell git describe --tags) # Set this to any non-empty string to enable unoptimized # build w/ debugging features. debug ?= # Set path to cargo executable CARGO ?= cargo # All complication artifacts, including dependencies and intermediates # will be stored here, for all architectures. Use a non-default name # since the (default) 'target' is used/referenced ambiguously in many # places in the tool-chain (including 'make' itself). CARGO_TARGET_DIR ?= targets export CARGO_TARGET_DIR # 'cargo' is sensitive to this env. var. value. ifdef debug $(info debug is $(debug)) # These affect both $(CARGO_TARGET_DIR) layout and contents # Ref: https://doc.rust-lang.org/cargo/guide/build-cache.html release := profile :=debug else release :=--release profile :=release endif .PHONY: all all: build bin: mkdir -p $@ $(CARGO_TARGET_DIR): mkdir -p $@ .PHONY: build build: bin $(CARGO_TARGET_DIR) $(CARGO) build $(release) cp $(CARGO_TARGET_DIR)/$(profile)/aardvark-dns bin/aardvark-dns$(if $(debug),.debug,) .PHONY: crate-publish crate-publish: @if [ "v$(CRATE_VERSION)" != "$(GIT_TAG)" ]; then\ echo "Git tag is not equivalent to the version set in Cargo.toml. Please checkout the correct tag";\ exit 1;\ fi @echo "It is expected that you have already done 'cargo login' before running this command. If not command may fail later" $(CARGO) publish --dry-run $(CARGO) publish .PHONY: clean clean: rm -rf bin if [ "$(CARGO_TARGET_DIR)" = "targets" ]; then rm -rf targets; fi $(MAKE) -C docs clean #.PHONY: docs #docs: ## build the docs on the host # $(MAKE) -C docs .PHONY: install install: install ${SELINUXOPT} -D -m0755 bin/aardvark-dns $(DESTDIR)/$(LIBEXECPODMAN)/aardvark-dns #$(MAKE) -C docs install .PHONY: uninstall uninstall: rm -f $(DESTDIR)/$(LIBEXECPODMAN)/aardvark-dns rm -f $(PREFIX)/share/man/man1/aardvark-dns*.1 #.PHONY: test test: unit integration # Used by CI to compile the unit tests but not run them .PHONY: build_unit build_unit: $(CARGO_TARGET_DIR) $(CARGO) test --no-run #.PHONY: unit unit: $(CARGO_TARGET_DIR) $(CARGO) test #.PHONY: code_coverage # Can be used by CI and users to generate code coverage report based on aardvark unit tests code_coverage: $(CARGO_TARGET_DIR) # Downloads tarpaulin only if same version is not present on local $(CARGO) install cargo-tarpaulin $(CARGO) tarpaulin -v #.PHONY: integration integration: $(CARGO_TARGET_DIR) # needs to be run as root or with podman unshare --rootless-netns bats test/ .PHONY: mock-rpm mock-rpm: rpkg local .PHONY: validate validate: $(CARGO_TARGET_DIR) $(CARGO) fmt --all -- --check $(CARGO) clippy -p aardvark-dns -- -D warnings .PHONY: vendor-tarball vendor-tarball: build install.cargo-vendor-filterer VERSION=$(shell bin/aardvark-dns --version | cut -f2 -d" ") && \ $(CARGO) vendor-filterer --format=tar.gz --prefix vendor/ && \ mv vendor.tar.gz aardvark-dns-v$$VERSION-vendor.tar.gz && \ gzip -c bin/aardvark-dns > aardvark-dns.gz && \ sha256sum aardvark-dns.gz aardvark-dns-v$$VERSION-vendor.tar.gz > sha256sum .PHONY: install.cargo-vendor-filterer install.cargo-vendor-filterer: $(CARGO) install cargo-vendor-filterer .PHONY: help help: @echo "usage: make $(prog) [debug=1]" containers-aardvark-dns-4fd01fd/OWNERS000066400000000000000000000006011452673546000176750ustar00rootroot00000000000000approvers: - baude - edsantiago - flouthoc - giuseppe - jwhonce - lsm5 - Luap99 - mheon - mtrmac - rhatdan - TomSweeneyRedHat - umohnani8 - vrothberg reviewers: - ashley-cui - baude - edsantiago - flouthoc - giuseppe - jwhonce - lsm5 - Luap99 - mheon - mtrmac - rhatdan - saschagrunert - TomSweeneyRedHat - umohnani8 - vrothberg containers-aardvark-dns-4fd01fd/README.md000066400000000000000000000020241452673546000202150ustar00rootroot00000000000000# aardvark-dns Aardvark-dns is an authoritative dns server for `A/AAAA` container records. It can forward other requests to configured resolvers. Read more about configuration in `src/backend/mod.rs`. It is mostly intended to be used with [Netavark](https://github.com/containers/netavark/) which will launch it automatically if both are installed. ```console aardvark-dns 0.1.0 USAGE: aardvark-dns [OPTIONS] FLAGS: -h, --help Print help information -V, --version Print version information OPTIONS: -c, --config Path to configuration directory -p, --port Host port for aardvark servers, defaults to 5533 SUBCOMMANDS: help Print this message or the help of the given subcommand(s) run Runs the aardvark dns server with the specified configuration directory ``` ### Build ```console make ``` ### Run Example ```console RUST_LOG=trace ./bin/aardvark-dns --config src/test/config/podman/ --port 5533 run ``` ### [Configuration file format](./config.md) containers-aardvark-dns-4fd01fd/RELEASE_NOTES.md000066400000000000000000000027341452673546000213200ustar00rootroot00000000000000# Release Notes ## v1.9.0 * update trust-dns to hickory * never report an error when the syslog init fails * dependency updates ## v1.8.0 * dependency updates ## v1.7.0 * dependency updates ## v1.6.0 * dependency updates * lower the TTL to 60s for container names ## v1.5.0 * dependency updates * code of conduct added ## v1.4.0 * Add support for network scoped dns servers; declare DNS at a network level ## v1.3.0 * allow one or more dns servers in the aardvark config ## v1.2.0 * coredns: do not combine results of A and AAAA records * run,serve: create aardvark pid in child before we notify parent process * coredns: response message set recursion available if RD is true * document configuration format ## v1.1.0 * Changed Aardvark to fork on startup to daemonize, as opposed to have this done by callers. This avoids race conditions around startup. * Name resolution is now case-insensitive. ## v1.0.3 * Updated dependancy libraries * Reduction in CPU use * Fixed bug with duplicate network names ## v1.0.2 * Updated dependency libraries * Removed vergen dependency ## v1.0.1 - Remove vendor directory from upstream github repository - Vendored libraries updates ## v1.0.0 - First release of aardvark-dns. ## v1.0.0-RC2 - Slew of bug fixes related to reverse lookups, NXDOMAIN returns, and so on. Getting very close to first official release. ## v1.0.0-RC1 - This is the first release candidate of Aardvark's initial release! All major functionality is implemented and working. containers-aardvark-dns-4fd01fd/build.rs000066400000000000000000000022101452673546000204000ustar00rootroot00000000000000use chrono::{DateTime, NaiveDateTime, TimeZone, Utc}; use std::env; use std::process::Command; fn main() { // Generate the default 'cargo:' instruction output println!("cargo:rerun-if-changed=build.rs"); // get timestamp let now = match env::var("SOURCE_DATE_EPOCH") { Ok(val) => { let naive = NaiveDateTime::from_timestamp_opt(val.parse::().unwrap(), 0).unwrap(); let datetime: DateTime = TimeZone::from_utc_datetime(&Utc, &naive); datetime } Err(_) => Utc::now(), }; println!("cargo:rustc-env=BUILD_TIMESTAMP={}", now.to_rfc3339()); // get rust target triple from TARGET env println!( "cargo:rustc-env=BUILD_TARGET={}", std::env::var("TARGET").unwrap() ); // get git commit let command = Command::new("git").args(["rev-parse", "HEAD"]).output(); let commit = match command { Ok(output) => String::from_utf8(output.stdout).unwrap(), // if error, e.g. build from source with git repo, just show empty string Err(_) => "".to_string(), }; println!("cargo:rustc-env=GIT_COMMIT={}", commit); } containers-aardvark-dns-4fd01fd/config.md000066400000000000000000000031261452673546000205310ustar00rootroot00000000000000# Configuration format Aardvark-dns will read configuration files from a given directory. Inside this directory there should be at least one config file. The name of the file equals the network name. ### First line The first line in the config must contain a comma separated list of listening ips for this network, usually the bridge ips. At least one ip must be given. **Note**: An optional second column of comma delimited domain name servers can be used at the network level. All containers on that network will inherit all the specified name servers instead of using the host's resolver. ``` [comma seperated ip4,ipv6 list][(optional)[space][comma seperated DNS servers]] ``` ### Container entries All following lines must contain the dns entries in this format: ``` [containerID][space][comma sparated ipv4 list][space][comma separated ipv6 list][space][comma separated dns names][(optional)[space][comma seperated DNS servers]] ``` Aardvark-dns will reload all config files when receiving a SIGHUB signal. ## Example ``` 10.0.0.1,fdfd::1 f35256b5e2f72ec8cb7d974d4f8841686fc8921fdfbc867285b50164e313f715 10.0.0.2 fdfd::2 testmulti1 8.8.8.8,1.1.1.1 e5df0cdbe0136a30cc3e848d495d2cc6dada25b7dedc776b4584ce2cbba6f06f 10.0.0.3 fdfd::3 testmulti2 ``` ## Example with network scoped DNS servers ``` 10.0.0.1,fdfd::1 8.8.8.8,1.1.1.1 f35256b5e2f72ec8cb7d974d4f8841686fc8921fdfbc867285b50164e313f715 10.0.0.2 fdfd::2 testmulti1 8.8.8.8,1.1.1.1 e5df0cdbe0136a30cc3e848d495d2cc6dada25b7dedc776b4584ce2cbba6f06f 10.0.0.3 fdfd::3 testmulti2 ``` Also see [./src/test/config/](./src/test/config/) for more config examples containers-aardvark-dns-4fd01fd/contrib/000077500000000000000000000000001452673546000204005ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/contrib/cirrus/000077500000000000000000000000001452673546000217075ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/contrib/cirrus/lib.sh000066400000000000000000000040351452673546000230130ustar00rootroot00000000000000 # Library of common, shared utility functions. This file is intended # to be sourced by other scripts, not called directly. # BEGIN Global export of all variables set -a # Automation library installed at image-build time, # defining $AUTOMATION_LIB_PATH in this file. if [[ -r "/etc/automation_environment" ]]; then source /etc/automation_environment fi if [[ -n "$AUTOMATION_LIB_PATH" ]]; then source $AUTOMATION_LIB_PATH/common_lib.sh else ( echo "WARNING: It does not appear that containers/automation was installed." echo " Functionality of most of this library will be negatively impacted" echo " This ${BASH_SOURCE[0]} was loaded by ${BASH_SOURCE[1]}" ) > /dev/stderr fi # Unsafe env. vars for display SECRET_ENV_RE='(ACCOUNT)|(GC[EP]..+)|(SSH)|(PASSWORD)|(TOKEN)' # setup.sh calls make_cienv() to cache these values for the life of the VM if [[ -r "/etc/ci_environment" ]]; then source /etc/ci_environment else # set default values - see make_cienv() below # Install rust packages globally instead of per-user CARGO_HOME="${CARGO_HOME:-/usr/local/cargo}" # Ensure cargo packages can be executed PATH="$PATH:$CARGO_HOME/bin" fi # END Global export of all variables set -a # Shortcut to automation library timeout/retry function retry() { err_retry 8 1000 "" "$@"; } # just over 4 minutes max # Helper to ensure a consistent environment across multiple CI scripts # containers, and shell environments (e.g. hack/get_ci_vm.sh) make_cienv(){ local envname local envval local SETUP_ENVIRONMENT=1 for envname in CARGO_HOME PATH CIRRUS_WORKING_DIR SETUP_ENVIRONMENT; do envval="${!envname}" # Properly escape values to prevent injection printf -- "$envname=%q\n" "$envval" done } complete_setup(){ set +x msg "************************************************************" msg "Completing environment setup, writing vars:" msg "************************************************************" make_cienv | tee -a /etc/ci_environment } containers-aardvark-dns-4fd01fd/contrib/cirrus/netavark_cache_groom.sh000066400000000000000000000015551452673546000264120ustar00rootroot00000000000000#!/bin/bash # # This script is intended to be run from Cirrus-CI to prepare the # rust targets cache for re-use during subsequent runs. This mainly # involves removing files and directories which change frequently # but are cheap/quick to regenerate - i.e. prevent "cache-flapping". # Any other use of this script is not supported and may cause harm. set -eo pipefail SCRIPT_DIRPATH=$(dirname ${BASH_SOURCE[0]}) source $SCRIPT_DIRPATH/lib.sh if [[ "$CIRRUS_CI" != true ]] || [[ -z "$DEST_BRANCH" ]]; then die "Script is not intended for use outside of Cirrus-CI" fi SCRIPT_DEST=$SCRIPT_DIRPATH/cache_groom.sh showrun curl --location --silent --show-error -o $SCRIPT_DEST \ https://raw.githubusercontent.com/containers/netavark/$DEST_BRANCH/contrib/cirrus/cache_groom.sh # Certain common automation library calls assume execution from this file exec bash $SCRIPT_DEST containers-aardvark-dns-4fd01fd/contrib/cirrus/runner.sh000077500000000000000000000042201452673546000235550ustar00rootroot00000000000000#!/bin/bash set -eo pipefail # This script runs in the Cirrus CI environment, invoked from .cirrus.yml . # It can also be invoked manually in a `hack/get_ci_cm.sh` environment, # documentation of said usage is TBI. # # The principal deciding factor is the first argument. For any # given value 'xyz' there must be a function '_run_xyz' to handle that # argument. source $(dirname ${BASH_SOURCE[0]})/lib.sh _run_noarg() { die "runner.sh must be called with a single argument" } _run_build() { # Assume we're on a fast VM, compile everything needed by the # rest of CI since subsequent tasks may have limited resources. make all debug=1 make build_unit # reuses some debug binaries make all # optimized/non-debug binaries # This will get scooped up and become part of the artifact archive. # Identify where the binary came from to benefit downstream consumers. cat | tee bin/aardvark-dns.info << EOF repo: $CIRRUS_REPO_CLONE_URL branch: $CIRRUS_BASE_BRANCH title: $CIRRUS_CHANGE_TITLE commit: $CIRRUS_CHANGE_IN_REPO build: https://cirrus-ci.com/build/$CIRRUS_BUILD_ID task: https://cirrus-ci.com/task/$CIRRUS_TASK_ID EOF } _run_build_aarch64() { _run_build } _run_validate() { make validate } _run_validate_aarch64() { _run_validate } _run_unit() { make unit } _run_unit_aarch64() { _run_unit } _run_integration() { make integration } _run_integration_aarch64() { make # FIXME: (@lsm5) investigate why cached binary isn't being reused _run_integration } show_env_vars msg "************************************************************" msg "Toolchain details" msg "************************************************************" rustc --version cargo --version msg "************************************************************" msg "Runner executing '$1' on $OS_REL_VER" msg "************************************************************" ((${SETUP_ENVIRONMENT:-0})) || \ die "Expecting setup.sh to have completed successfully" cd "${CIRRUS_WORKING_DIR}/" handler="_run_${1:-noarg}" if [ "$(type -t $handler)" != "function" ]; then die "Unknown/Unsupported runner.sh argument '$1'" fi $handler containers-aardvark-dns-4fd01fd/contrib/cirrus/setup.sh000077500000000000000000000026621452673546000234140ustar00rootroot00000000000000#!/bin/bash # This script configures the CI runtime environment. It's intended # to be used by Cirrus-CI, not humans. set -e source $(dirname $0)/lib.sh # Only do this once if [[ -r "/etc/ci_environment" ]]; then msg "It appears ${BASH_SOURCE[0]} already ran, exiting." exit 0 fi trap "complete_setup" EXIT msg "************************************************************" msg "Setting up runtime environment" msg "************************************************************" show_env_vars req_env_vars NETAVARK_URL NETAVARK_BRANCH cd /usr/libexec/podman rm -vf netavark* if showrun curl --fail --location -o /tmp/netavark.zip "$NETAVARK_URL" && \ unzip -o /tmp/netavark.zip; then if [[ $(uname -m) != "x86_64" ]]; then showrun mv netavark.$(uname -m)-unknown-linux-gnu netavark fi showrun chmod a+x /usr/libexec/podman/netavark else warn "Error downloading/extracting the latest pre-compiled netavark binary from CI" showrun cargo install \ --root /usr/libexec/podman \ --git https://github.com/containers/netavark \ --branch "$NETAVARK_BRANCH" showrun mv /usr/libexec/podman/bin/netavark /usr/libexec/podman fi # show netavark commit in CI logs showrun /usr/libexec/podman/netavark version # Warning, this isn't the end. An exit-handler is installed to finalize # setup of env. vars. This is required for runner.sh to operate properly. # See complete_setup() in lib.sh for details. containers-aardvark-dns-4fd01fd/docs/000077500000000000000000000000001452673546000176705ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/docs/publish-crate.md000066400000000000000000000005131452673546000227530ustar00rootroot00000000000000# Publishing aardvark-dns crate to crates.io ### Steps * Make sure you have already done `cargo login` on your current session with a valid token. * `cd aardvark-dns` * Git checkout the version which you want to publish. * `make crate-publish` * New version should be reflected here: https://crates.io/crates/aardvark-dns/versions containers-aardvark-dns-4fd01fd/hack/000077500000000000000000000000001452673546000176465ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/hack/get_ci_vm.sh000077500000000000000000000050311452673546000221400ustar00rootroot00000000000000#!/usr/bin/env bash # # For help and usage information, simply execute the script w/o any arguments. # # This script is intended to be run by Red Hat podman developers who need # to debug problems specifically related to Cirrus-CI automated testing. # It requires that you have been granted prior access to create VMs in # google-cloud. For non-Red Hat contributors, VMs are available as-needed, # with supervision upon request. set -e SCRIPT_FILEPATH=$(realpath "${BASH_SOURCE[0]}") SCRIPT_DIRPATH=$(dirname "$SCRIPT_FILEPATH") REPO_DIRPATH=$(realpath "$SCRIPT_DIRPATH/../") # Help detect what get_ci_vm container called this script GET_CI_VM="${GET_CI_VM:-0}" in_get_ci_vm() { if ((GET_CI_VM==0)); then echo "Error: $1 is not intended for use in this context" exit 2 fi } # get_ci_vm APIv1 container entrypoint calls into this script # to obtain required repo. specific configuration options. if [[ "$1" == "--config" ]]; then in_get_ci_vm "$1" # handles GET_CI_VM==0 case case "$GET_CI_VM" in 1) cat < /dev/stderr source ./contrib/cirrus/lib.sh echo "+ Running environment setup" > /dev/stderr ./contrib/cirrus/setup.sh "$CIRRUS_TASK_NAME" else # Pass this repo and CLI args into container for VM creation/management mkdir -p $HOME/.config/gcloud/ssh mkdir -p $HOME/.aws podman run -it --rm \ --tz=local \ -e NAME="$USER" \ -e SRCDIR=/src \ -e GCLOUD_ZONE="$GCLOUD_ZONE" \ -e A_DEBUG="${A_DEBUG:-0}" \ -v $REPO_DIRPATH:/src:O \ -v $HOME/.config/gcloud:/root/.config/gcloud:z \ -v $HOME/.config/gcloud/ssh:/root/.ssh:z \ -v $HOME/.aws:/root/.aws:z \ quay.io/libpod/get_ci_vm:latest "$@" fi containers-aardvark-dns-4fd01fd/plans/000077500000000000000000000000001452673546000200555ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/plans/main.fmf000066400000000000000000000013511452673546000214730ustar00rootroot00000000000000prepare: - name: Distro specific setup how: shell script: bash ./plans/prepare.sh - name: Install dependencies how: install package: - bats - bind-utils - cargo - clippy - go-md2man - iptables - jq - make - netavark - nmap-ncat - rustfmt - slirp4netns /validate_test: summary: Run validate test execute: how: tmt script: make validate /unit_test: summary: Run unit tests execute: how: tmt script: make unit /integration_test: summary: Run integration tests execute: how: tmt script: make AARDVARK=/usr/libexec/podman/aardvark-dns integration containers-aardvark-dns-4fd01fd/plans/prepare.sh000066400000000000000000000016271452673546000220550ustar00rootroot00000000000000#!/usr/bin/env bash set -eox pipefail RHEL_RELEASE=$(rpm --eval %{?rhel}) ARCH=$(uname -m) # disable container-tools module on el8 if [ $RHEL_RELEASE -eq 8 ]; then dnf -y module disable container-tools fi # install epel-release on centos stream and rhel if [ -f /etc/centos-release ]; then dnf -y install epel-release elif [ $RHEL_RELEASE -ge 8 ]; then dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-$RHEL_RELEASE.noarch.rpm dnf config-manager --set-enabled epel fi if [ -f /etc/fedora-release ]; then echo "Resizing tmpfs..." mount -o remount,size=10G /tmp fi # Some envs like containers don't have the copr plugin installed dnf -y install 'dnf-command(copr)' # Enable podman-next copr dnf -y copr enable rhcontainerbot/podman-next # Set podman-next to higher priority than default dnf config-manager --save --setopt="*:rhcontainerbot:podman-next.priority=5" containers-aardvark-dns-4fd01fd/rpm/000077500000000000000000000000001452673546000175365ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/rpm/aardvark-dns.spec000066400000000000000000000060331452673546000227710ustar00rootroot00000000000000# trust-dns-{client,server} not available # using vendored deps # RHEL doesn't include the package rust-packaging which provides %%__cargo macro, but EPEL # does. So we set it separately here and skip rust-packaging dependency for RHEL. # Buildability without EPEL is essential for packit builds. # ELN doesn't need this. %if %{defined rhel} && 0%{?rhel} < 10 %define __cargo %{_bindir}/env CARGO_HOME=.cargo RUSTC_BOOTSTRAP=1 RUSTFLAGS='-Copt-level=3 -Cdebuginfo=2 -Ccodegen-units=1 -Clink-arg=-Wl,-z,relro -Clink-arg=-Wl,-z,now --cap-lints=warn' %{_bindir}/cargo %endif %global with_debug 1 %if 0%{?with_debug} %global _find_debuginfo_dwz_opts %{nil} %global _dwz_low_mem_die_limit 0 %else %global debug_package %{nil} %endif Name: aardvark-dns %if %{defined copr_username} Epoch: 102 %endif # DO NOT TOUCH the Version string! # The TRUE source of this specfile is: # https://github.com/containers/podman/blob/main/rpm/podman.spec # If that's what you're reading, Version must be 0, and will be updated by Packit for # copr and koji builds. # If you're reading this on dist-git, the version is automatically filled in by Packit. Version: 0 # The `AND` needs to be uppercase in the License for SPDX compatibility License: Apache-2.0 AND MIT AND Zlib Release: %autorelease %if %{defined golang_arches_future} ExclusiveArch: %{golang_arches_future} %else ExclusiveArch: aarch64 ppc64le s390x x86_64 %endif Summary: Authoritative DNS server for A/AAAA container records URL: https://github.com/containers/%{name} # Tarballs fetched from upstream's release page Source0: %{url}/archive/v%{version}.tar.gz Source1: %{url}/releases/download/v%{version}/%{name}-v%{version}-vendor.tar.gz BuildRequires: cargo BuildRequires: git-core BuildRequires: make %if %{defined rhel} # rust-toolset requires the `local` repo enabled on non-koji ELN build environments BuildRequires: rust-toolset %else BuildRequires: rust-packaging BuildRequires: rust-srpm-macros %endif %description %{summary} Forwards other request to configured resolvers. Read more about configuration in `src/backend/mod.rs`. %prep %autosetup -Sgit %{name}-%{version} # Following steps are only required on environments like koji which have no # network access and thus depend on the vendored tarball. Copr pulls # dependencies directly from the network. %if !%{defined copr_username} tar fx %{SOURCE1} mkdir -p .cargo cat >.cargo/config << EOF [source.crates-io] replace-with = "vendored-sources" [source.vendored-sources] directory = "vendor" EOF %endif %build %{__make} CARGO="%{__cargo}" build %install %{__make} DESTDIR=%{buildroot} PREFIX=%{_prefix} install %files %license LICENSE %dir %{_libexecdir}/podman %{_libexecdir}/podman/%{name} %changelog %if %{defined autochangelog} %autochangelog %else # NOTE: This changelog will be visible on CentOS 8 Stream builds # Other envs are capable of handling autochangelog * Wed Jun 14 2023 RH Container Bot - Placeholder changelog for envs that are not autochangelog-ready - Contact upstream if you need to report an issue with the build. %endif containers-aardvark-dns-4fd01fd/src/000077500000000000000000000000001452673546000175275ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/backend/000077500000000000000000000000001452673546000211165ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/backend/mod.rs000066400000000000000000000120401452673546000222400ustar00rootroot00000000000000use log::error; use std::collections::HashMap; use std::net::IpAddr; use std::vec::Vec; // The core structure of the in-memory backing store for the DNS server. // TODO: I've initially intermingled v4 and v6 addresses for simplicity; the // server will get back a mix of responses and filter for v4/v6 from there. // This may not be a good decision, not sure yet; we can split later if // necessary. pub struct DNSBackend { // Map of IP -> Network membership. // Every container must have an entry in this map, otherwise we will not // service requests to the Podman TLD for it. pub ip_mappings: HashMap>, // Map of network name to map of name to IP addresses. pub name_mappings: HashMap>>, // Map of network name to map of IP address to container name. pub reverse_mappings: HashMap>>, // Map of IP address to DNS server IPs to service queries not handled // directly. pub ctr_dns_server: HashMap>>, // Map of network name and DNS server IPs. pub network_dns_server: HashMap>, } pub enum DNSResult { // We know the IP address of the requester and what networks they are in. // Here's a vector of IPs corresponding to your query. Success(Vec), // We know the IP address of the requester and what networks they are in. // However, there were no results for the requested name to look up. NXDomain, // We do not know the IP address of the requester. NoSuchIP, // Other, unspecified error occurred. Error(String), } impl DNSBackend { // Create a new backend from the given set of network mappings. pub fn new( containers: HashMap>, networks: HashMap>>, reverse: HashMap>>, ctr_dns_server: HashMap>>, network_dns_server: HashMap>, ) -> DNSBackend { DNSBackend { ip_mappings: containers, name_mappings: networks, reverse_mappings: reverse, ctr_dns_server, network_dns_server, } } // Handle a single DNS lookup made by a given IP. // The name being looked up *must* have the TLD used by the DNS server // stripped. // TODO: right now this returns v4 and v6 addresses intermixed and relies on // the caller to sort through them; we could add a v6 bool as an argument // and do it here instead. pub fn lookup(&self, requester: &IpAddr, entry: &str) -> DNSResult { // Normalize lookup entry to lowercase. let mut name = entry.to_lowercase(); let nets = match self.ip_mappings.get(requester) { Some(n) => n, None => return DNSResult::NoSuchIP, }; let mut results: Vec = Vec::new(); for net in nets { let net_names = match self.name_mappings.get(net) { Some(n) => n, None => { error!("Container with IP {} belongs to network {} but there is no listing in networks table!", requester.to_string(), net); continue; } }; // if this is a fully qualified name, remove dots so backend can perform search if !name.is_empty() { if let Some(lastchar) = name.chars().last() { if lastchar == '.' { name = (name[0..name.len() - 1]).to_string(); } } } if let Some(addrs) = net_names.get(&name) { results.append(&mut addrs.clone()); } } if results.is_empty() { return DNSResult::NXDomain; } DNSResult::Success(results) } // Returns list of network resolvers for a particular container pub fn get_network_scoped_resolvers(&self, requester: &IpAddr) -> Option> { let mut results: Vec = Vec::new(); match self.ip_mappings.get(requester) { Some(nets) => { for net in nets { match self.network_dns_server.get(net) { Some(resolvers) => results.extend_from_slice(resolvers), None => { continue; } }; } } None => return None, }; Some(results) } /// Return a single name resolved via mapping if it exists. pub fn reverse_lookup(&self, requester: &IpAddr, lookup_ip: &IpAddr) -> Option<&Vec> { let nets = match self.ip_mappings.get(requester) { Some(n) => n, None => return None, }; for ips in nets.iter().filter_map(|v| self.reverse_mappings.get(v)) { if let Some(names) = ips.get(lookup_ip) { return Some(names); } } None } } containers-aardvark-dns-4fd01fd/src/commands/000077500000000000000000000000001452673546000213305ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/commands/mod.rs000066400000000000000000000000361452673546000224540ustar00rootroot00000000000000pub mod run; pub mod version; containers-aardvark-dns-4fd01fd/src/commands/run.rs000066400000000000000000000071761452673546000225150ustar00rootroot00000000000000//! Runs the aardvark dns server with provided config use crate::server::serve; use clap::Parser; use nix::unistd; use nix::unistd::{dup2, fork, ForkResult}; use std::fs::File; use std::fs::OpenOptions; use std::io::Error; use std::io::Write; use std::os::unix::io::AsRawFd; use std::os::unix::io::FromRawFd; #[derive(Parser, Debug)] pub struct Run {} impl Run { /// The run command runs the aardvark-dns server with the given configuration. pub fn new() -> Self { Self {} } pub fn exec( &self, input_dir: String, port: u32, filter_search_domain: String, ) -> Result<(), Error> { // create a temporary path for unix socket // so parent can communicate with child and // only exit when child is ready to serve. let (ready_pipe_read, ready_pipe_write) = nix::unistd::pipe()?; // fork and verify if server is running // and exit parent // setsid() ensures that there is no controlling terminal on the child process match unsafe { fork() } { Ok(ForkResult::Parent { child, .. }) => { log::debug!("starting aardvark on a child with pid {}", child); // verify aardvark here and block till will start unistd::read(ready_pipe_read, &mut [0_u8; 1])?; unistd::close(ready_pipe_read)?; unistd::close(ready_pipe_write)?; Ok(()) } Ok(ForkResult::Child) => { // remove any controlling terminals // but don't hardstop if this fails let _ = unsafe { libc::setsid() }; // check https://docs.rs/libc // close fds -> stdout, stdin and stderr let dev_null = OpenOptions::new() .read(true) .write(true) .open("/dev/null") .map_err(|e| std::io::Error::new(e.kind(), format!("/dev/null: {}", e))); // redirect stdout, stdin and stderr to /dev/null if let Ok(dev_null) = dev_null { let fd = dev_null.as_raw_fd(); let _ = dup2(fd, 0); let _ = dup2(fd, 1); let _ = dup2(fd, 2); if fd < 2 { std::mem::forget(dev_null); } } // create aardvark pid and then notify parent if let Err(er) = serve::create_pid(&input_dir) { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("Error creating aardvark pid {}", er), )); } let mut f = unsafe { File::from_raw_fd(ready_pipe_write) }; write!(&mut f, "ready")?; unistd::close(ready_pipe_read)?; unistd::close(ready_pipe_write)?; if let Err(er) = serve::serve(&input_dir, port, &filter_search_domain) { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("Error starting server {}", er), )); } Ok(()) } Err(err) => { log::debug!("fork failed with error {}", err); Err(std::io::Error::new( std::io::ErrorKind::Other, format!("fork failed with error: {}", err), )) } } } } impl Default for Run { fn default() -> Self { Self::new() } } containers-aardvark-dns-4fd01fd/src/commands/version.rs000066400000000000000000000017261452673546000233710ustar00rootroot00000000000000use clap::Parser; use std::fmt; use std::io::Error; #[derive(Parser, Debug)] pub struct Version {} #[derive(Debug)] struct Info { version: &'static str, commit: &'static str, build_time: &'static str, target: &'static str, } // since we do not need a json library here we just create the json output manually impl fmt::Display for Info { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{{ \"version\": \"{}\", \"commit\": \"{}\", \"build_time\": \"{}\", \"target\": \"{}\" }}", self.version, self.commit, self.build_time, self.target ) } } impl Version { pub fn exec(&self) -> Result<(), Error> { let info = Info { version: env!("CARGO_PKG_VERSION"), commit: env!("GIT_COMMIT"), build_time: env!("BUILD_TIMESTAMP"), target: env!("BUILD_TARGET"), }; println!("{}", info); Ok(()) } } containers-aardvark-dns-4fd01fd/src/config/000077500000000000000000000000001452673546000207745ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/config/constants.rs000066400000000000000000000000651452673546000233570ustar00rootroot00000000000000pub static AARDVARK_PID_FILE: &str = "aardvark.pid"; containers-aardvark-dns-4fd01fd/src/config/mod.rs000066400000000000000000000334261452673546000221310ustar00rootroot00000000000000use crate::backend::DNSBackend; use log::warn; use std::collections::HashMap; use std::fs::{metadata, read_dir, read_to_string}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::vec::Vec; pub mod constants; // Parse configuration files in the given directory. // Configuration files are formatted as follows: // The name of the file will be interpreted as the name of the network. // The first line must be the gateway IP(s) of the network, comma-separated. // All subsequent individual lines contain info on a single container and are // formatted as: // // Where space is a single space character. // Returns a complete DNSBackend struct (all that is necessary for looks) and // Silent clippy: sometimes clippy marks useful tyes as complex and for this case following type is // convinient #[allow(clippy::type_complexity)] pub fn parse_configs( dir: &str, ) -> Result< ( DNSBackend, HashMap>, HashMap>, ), std::io::Error, > { if !metadata(dir)?.is_dir() { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("config directory {} must exist and be a directory", dir), )); } let mut network_membership: HashMap> = HashMap::new(); let mut container_ips: HashMap> = HashMap::new(); let mut reverse: HashMap>> = HashMap::new(); let mut network_names: HashMap>> = HashMap::new(); let mut listen_ips_4: HashMap> = HashMap::new(); let mut listen_ips_6: HashMap> = HashMap::new(); let mut ctr_dns_server: HashMap>> = HashMap::new(); let mut network_dns_server: HashMap> = HashMap::new(); // Enumerate all files in the directory, read them in one by one. // Steadily build a map of what container has what IPs and what // container is in what networks. let configs = read_dir(dir)?; for config in configs { // Each entry is a result. Interpret Err to mean the config was removed // while we were working; warn only, don't error. // Might be safer to completely restart the process, but there's also a // chance that, if we do that, we never finish and update the config, // assuming the files in question are modified at a sufficiently high // rate. match config { Ok(cfg) => { // dont process aardvark pid files if let Some(path) = cfg.path().file_name() { if path == constants::AARDVARK_PID_FILE { continue; } } let parsed_network_config = parse_config(cfg.path().as_path())?; let network_name: String = match cfg.path().file_name() { // This isn't *completely* safe, but I do not foresee many // cases where our network names include non-UTF8 // characters. Some(s) => match s.to_str() { Some(st) => st.to_string(), None => return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("configuration file {} name has non-UTF8 characters", s.to_string_lossy()), )), }, None => return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("configuration file {} does not have a file name, cannot identify network name", cfg.path().to_string_lossy()), )), }; // Network DNS Servers were found while parsing config // lets populate the backend if !parsed_network_config.network_dnsservers.is_empty() { network_dns_server.insert( network_name.clone(), parsed_network_config.network_dnsservers, ); } for ip in parsed_network_config.network_bind_ip { match ip { IpAddr::V4(a) => listen_ips_4 .entry(network_name.clone()) .or_default() .push(a), IpAddr::V6(b) => listen_ips_6 .entry(network_name.clone()) .or_default() .push(b), } } for entry in parsed_network_config.container_entry { // Container network membership let ctr_networks = network_membership.entry(entry.id.clone()).or_default(); // Keep the network deduplicated if !ctr_networks.contains(&network_name) { ctr_networks.push(network_name.clone()); } // Container IP addresses let mut new_ctr_ips: Vec = Vec::new(); if let Some(v4) = entry.v4 { for ip in v4 { reverse .entry(network_name.clone()) .or_default() .entry(IpAddr::V4(ip)) .or_default() .append(&mut entry.aliases.clone()); ctr_dns_server.insert(IpAddr::V4(ip), entry.dns_servers.clone()); new_ctr_ips.push(IpAddr::V4(ip)); } } if let Some(v6) = entry.v6 { for ip in v6 { reverse .entry(network_name.clone()) .or_default() .entry(IpAddr::V6(ip)) .or_default() .append(&mut entry.aliases.clone()); ctr_dns_server.insert(IpAddr::V6(ip), entry.dns_servers.clone()); new_ctr_ips.push(IpAddr::V6(ip)); } } let ctr_ips = container_ips.entry(entry.id.clone()).or_default(); ctr_ips.append(&mut new_ctr_ips.clone()); // Network aliases to IPs map. let network_aliases = network_names.entry(network_name.clone()).or_default(); for alias in entry.aliases { let alias_entries = network_aliases.entry(alias).or_default(); alias_entries.append(&mut new_ctr_ips.clone()); } } } Err(e) => warn!("Error reading config file for server update: {}", e), } } // Set up types to be returned. let mut ctrs: HashMap> = HashMap::new(); for (ctr_id, ips) in container_ips { match network_membership.get(&ctr_id) { Some(s) => { for ip in ips { let ip_networks = ctrs.entry(ip).or_default(); ip_networks.append(&mut s.clone()); } } None => { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!( "Container ID {} has an entry in IPs table, but not network membership table", ctr_id ), )) } } } Ok(( DNSBackend::new( ctrs, network_names, reverse, ctr_dns_server, network_dns_server, ), listen_ips_4, listen_ips_6, )) } // A single entry in a config file struct CtrEntry { id: String, v4: Option>, v6: Option>, aliases: Vec, dns_servers: Option>, } // A simplified type for results retured by // parse_config after parsing a single network // config. struct ParsedNetworkConfig { network_bind_ip: Vec, container_entry: Vec, network_dnsservers: Vec, } // Read and parse a single given configuration file fn parse_config(path: &std::path::Path) -> Result { let content = read_to_string(path)?; let mut is_first = true; let mut bind_addrs: Vec = Vec::new(); let mut network_dns_servers: Vec = Vec::new(); let mut ctrs: Vec = Vec::new(); // Split on newline, parse each line for line in content.split('\n') { if line.is_empty() { continue; } if is_first { let network_parts = line.split(' ').collect::>(); if network_parts.is_empty() { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("invalid network configuration file: {}", path.display()), )); } // process bind ip for ip in network_parts[0].split(',') { let local_ip = match ip.parse() { Ok(l) => l, Err(e) => { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("error parsing ip address {}: {}", ip, e), )) } }; bind_addrs.push(local_ip); } // If network parts contain more than one col then // we have custom dns server also defined at network level // lets process that. if network_parts.len() > 1 { for ip in network_parts[1].split(',') { let local_ip = match ip.parse() { Ok(l) => l, Err(e) => { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("error parsing network dns address {}: {}", ip, e), )) } }; network_dns_servers.push(local_ip); } } is_first = false; continue; } // Split on space let parts = line.split(' ').collect::>(); if parts.len() < 4 { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!( "configuration file {} line {} is improperly formatted - too few entries", path.to_string_lossy(), line ), )); } let v4_addrs: Option> = if !parts[1].is_empty() { let ipv4 = match parts[1].split(',').map(|i| i.parse()).collect() { Ok(i) => i, Err(e) => { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("error parsing IP address {}: {}", parts[1], e), )) } }; Some(ipv4) } else { None }; let v6_addrs: Option> = if !parts[2].is_empty() { let ipv6 = match parts[2].split(',').map(|i| i.parse()).collect() { Ok(i) => i, Err(e) => { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("error parsing IP address {}: {}", parts[2], e), )) } }; Some(ipv6) } else { None }; let aliases: Vec = parts[3] .split(',') .map(|x| x.to_string().to_lowercase()) .collect::>(); if aliases.is_empty() { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!( "configuration file {} line {} is improperly formatted - no names given", path.to_string_lossy(), line ), )); } let dns_servers: Option> = if parts.len() == 5 && !parts[4].is_empty() { let dns_server = match parts[4].split(',').map(|i| i.parse()).collect() { Ok(i) => i, Err(e) => { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("error parsing DNS server address {}: {}", parts[4], e), )) } }; Some(dns_server) } else { None }; ctrs.push(CtrEntry { id: parts[0].to_string().to_lowercase(), v4: v4_addrs, v6: v6_addrs, aliases, dns_servers, }); } // Must provide at least one bind address if bind_addrs.is_empty() { return Err(std::io::Error::new( std::io::ErrorKind::InvalidData, format!( "configuration file {} does not provide any bind addresses", path.to_string_lossy() ), )); } Ok(ParsedNetworkConfig { network_bind_ip: bind_addrs, container_entry: ctrs, network_dnsservers: network_dns_servers, }) } containers-aardvark-dns-4fd01fd/src/dns/000077500000000000000000000000001452673546000203135ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/dns/coredns.rs000066400000000000000000000554441452673546000223320ustar00rootroot00000000000000use crate::backend::DNSBackend; use crate::backend::DNSResult; use futures_util::StreamExt; use futures_util::TryStreamExt; use hickory_client::{client::AsyncClient, proto::xfer::SerialMessage, rr::rdata, rr::Name}; use hickory_proto::{ op::{Message, MessageType, ResponseCode}, rr::{DNSClass, RData, Record, RecordType}, udp::{UdpClientStream, UdpStream}, xfer::{dns_handle::DnsHandle, BufDnsStreamHandle, DnsRequest}, DnsStreamHandle, }; use log::{debug, error, trace, warn}; use resolv_conf; use resolv_conf::ScopedIp; use std::convert::TryInto; use std::env; use std::fs::File; use std::io::Read; use std::net::{IpAddr, SocketAddr}; use std::sync::{Arc, Mutex}; use tokio::net::UdpSocket; // Containers can be recreated with different ips quickly so // do not let the clients cache to dns response for to long, // aardvark-dns runs on the same host so caching is not that important. // see https://github.com/containers/netavark/discussions/644 const CONTAINER_TTL: u32 = 60; pub struct CoreDns { name: Name, // name or origin network_name: String, // raw network name address: IpAddr, // server address port: u32, // server port backend: Arc, // server's data store kill_switch: Arc>, // global kill_switch filter_search_domain: String, // filter_search_domain rx: async_broadcast::Receiver, // kill switch receiver resolv_conf: resolv_conf::Config, // host's parsed /etc/resolv.conf } impl CoreDns { // Most of the arg can be removed in design refactor. // so dont create a struct for this now. #[allow(clippy::too_many_arguments)] pub async fn new( address: IpAddr, port: u32, network_name: &str, forward_addr: IpAddr, forward_port: u16, backend: Arc, kill_switch: Arc>, filter_search_domain: String, rx: async_broadcast::Receiver, ) -> anyhow::Result { // this does not have to be unique, if we fail getting server name later // start with empty name let mut name: Name = Name::new(); if network_name.len() > 10 { // to long to set this as name of dns server strip only first 10 chars // trust dns limitation, this is nothing to worry about since server name // has nothing to do without DNS logic, name can be random as well. if let Ok(n) = Name::parse(&network_name[..10], None) { name = n; } } else if let Ok(n) = Name::parse(network_name, None) { name = n; } debug!( "Will Forward dns requests to udp://{:?}:{}", forward_addr, forward_port, ); let mut resolv_conf: resolv_conf::Config = resolv_conf::Config::new(); let mut buf = Vec::with_capacity(4096); if let Ok(mut f) = File::open("/etc/resolv.conf") { if f.read_to_end(&mut buf).is_ok() { if let Ok(conf) = resolv_conf::Config::parse(&buf) { resolv_conf = conf; } } } let network_name = network_name.to_owned(); Ok(CoreDns { name, network_name, address, port, backend, kill_switch, filter_search_domain, rx, resolv_conf, }) } pub async fn run(&mut self) -> anyhow::Result<()> { tokio::try_join!(self.register_port())?; Ok(()) } // registers port supports udp for now async fn register_port(&mut self) -> anyhow::Result<()> { debug!("Starting listen on udp {:?}:{}", self.address, self.port); let no_proxy: bool = env::var("AARDVARK_NO_PROXY").is_ok(); // Do we need to serve on tcp anywhere in future ? let socket = UdpSocket::bind(format!("{}:{}", self.address, self.port)).await?; let address = SocketAddr::new(self.address, self.port.try_into().unwrap()); let (mut receiver, sender_original) = UdpStream::with_bound(socket, address); loop { tokio::select! { _ = self.rx.recv() => { break; }, v = receiver.next() => { let msg_received = match v { Some(value) => value, _ => { // None received, nothing to process so continue debug!("None recevied from stream, continue the loop"); continue; } }; match msg_received { Ok(msg) => { let src_address = msg.addr(); let mut dns_resolver = self.resolv_conf.clone(); let sender = sender_original.clone().with_remote_addr(src_address); let (name, record_type, mut req) = match parse_dns_msg(msg) { Some((name, record_type, req)) => (name, record_type, req), _ => { error!("None received while parsing dns message, this is not expected server will ignore this message"); continue; } }; let mut resolved_ip_list: Vec = Vec::new(); let mut nameservers_scoped: Vec = Vec::new(); // Add resolvers configured for container if let Some(Some(dns_servers)) = self.backend.ctr_dns_server.get(&src_address.ip()) { if !dns_servers.is_empty() { for dns_server in dns_servers.iter() { nameservers_scoped.push(ScopedIp::from(*dns_server)); } } // Add network scoped resolvers only if container specific resolvers were not configured } else if let Some(network_dns_servers) = self.backend.get_network_scoped_resolvers(&src_address.ip()) { for dns_server in network_dns_servers.iter() { nameservers_scoped.push(ScopedIp::from(*dns_server)); } } // Override host resolvers with custom resolvers if any were // configured for container or network. if !nameservers_scoped.is_empty() { dns_resolver = resolv_conf::Config::new(); dns_resolver.nameservers = nameservers_scoped; } // Create debug and trace info for key parameters. trace!("server name: {:?}", self.name.to_ascii()); debug!("request source address: {:?}", src_address); trace!("requested record type: {:?}", record_type); debug!("checking if backend has entry for: {:?}", name); trace!( "server backend.name_mappings: {:?}", self.backend.name_mappings ); trace!("server backend.ip_mappings: {:?}", self.backend.ip_mappings); trace!( "server backend kill switch: {:?}", self.kill_switch.lock().is_ok() ); // if record type is PTR try resolving early and return if record found if record_type == RecordType::PTR { let mut ptr_lookup_ip: String; // Are we IPv4 or IPv6? if name.contains(".in-addr.arpa.") { // IPv4 ptr_lookup_ip = name.trim_end_matches(".in-addr.arpa.").split('.').rev().collect::>().join("."); } else if name.contains(".ip6.arpa.") { // IPv6 ptr_lookup_ip = name.trim_end_matches(".ip6.arpa.").split('.').rev().collect::(); // We removed all periods; now we need to insert a : every 4 characters. // split_off() reduces the original string to 4 characters and returns the remainder. // So append the 4-character and continue going until we run out of characters. let mut split: Vec = Vec::new(); while ptr_lookup_ip.len() > 4 { let tmp = ptr_lookup_ip.split_off(4); split.push(ptr_lookup_ip); ptr_lookup_ip = tmp; } // Length should be equal to 4 here, but just use > 0 for safety. if !ptr_lookup_ip.is_empty() { split.push(ptr_lookup_ip); } ptr_lookup_ip = split.join(":"); } else { // Not a valid address, so force parse() to fail // TODO: this is ugly and I don't like it ptr_lookup_ip = String::from("not an ip"); } trace!("Performing lookup reverse lookup for ip: {:?}", ptr_lookup_ip.to_owned()); // We should probably log malformed queries, but for now if-let should be fine. if let Ok(lookup_ip) = ptr_lookup_ip.parse() { if let Some(reverse_lookup) = self.backend.reverse_lookup(&src_address.ip(), &lookup_ip) { let mut req_clone = req.clone(); for entry in reverse_lookup { if let Ok(answer) = Name::from_ascii(format!("{}.", entry)) { req_clone.add_answer( Record::new() .set_ttl(CONTAINER_TTL) .set_rr_type(RecordType::PTR) .set_dns_class(DNSClass::IN) .set_data(Some(RData::PTR(rdata::PTR(answer)))) .clone(), ); } } reply(sender.clone(), src_address, &req_clone); } }; } // attempt intra network resolution match self.backend.lookup(&src_address.ip(), name.as_str()) { // If we go success from backend lookup DNSResult::Success(_ip_vec) => { debug!("Found backend lookup"); resolved_ip_list = _ip_vec; } // For everything else assume the src_address was not in ip_mappings _ => { debug!( "No backend lookup found, try resolving in current resolvers entry" ); if let Some(container_mappings) = self.backend.name_mappings.get(&self.network_name) { for (key, value) in container_mappings { // if query contains search domain, strip it out. // Why? This is a workaround so aardvark works well // with setup which was created for dnsname/dnsmasq let mut request_name = name.as_str().to_owned(); let mut filter_domain_ndots_complete = self.filter_search_domain.to_owned(); filter_domain_ndots_complete.push('.'); if request_name.ends_with(&self.filter_search_domain) { request_name = match request_name.strip_suffix(&self.filter_search_domain) { Some(value) => value.to_string(), _ => { error!("Unable to parse string suffix, ignore parsing this request"); continue; } }; request_name.push('.'); } if request_name.ends_with(&filter_domain_ndots_complete) { request_name = match request_name.strip_suffix(&filter_domain_ndots_complete) { Some(value) => value.to_string(), _ => { error!("Unable to parse string suffix, ignore parsing this request"); continue; } }; request_name.push('.'); } // convert key to fully qualified domain name let mut key_fqdn = key.to_owned(); key_fqdn.push('.'); if key_fqdn == request_name { resolved_ip_list = value.to_vec(); } } } } } let record_name: Name = match Name::from_str_relaxed(name.as_str()) { Ok(name) => name, Err(e) => { // log and continue server error!("Error while parsing record name: {:?}", e); continue; } }; if !resolved_ip_list.is_empty() { if record_type == RecordType::A { for record_addr in resolved_ip_list { if let IpAddr::V4(ipv4) = record_addr { req.add_answer( Record::new() .set_name(record_name.clone()) .set_ttl(CONTAINER_TTL) .set_rr_type(RecordType::A) .set_dns_class(DNSClass::IN) .set_data(Some(RData::A(rdata::A(ipv4)))) .clone(), ); } } } else if record_type == RecordType::AAAA { for record_addr in resolved_ip_list { if let IpAddr::V6(ipv6) = record_addr { req.add_answer( Record::new() .set_name(record_name.clone()) .set_ttl(CONTAINER_TTL) .set_rr_type(RecordType::AAAA) .set_dns_class(DNSClass::IN) .set_data(Some(RData::AAAA(rdata::AAAA(ipv6)))) .clone(), ); } } } reply(sender, src_address, &req); } else { debug!("Not found, forwarding dns request for {:?}", name); let request_name = name.as_str().to_owned(); let filter_search_domain_ndots = self.filter_search_domain.clone() + "."; if no_proxy || request_name.ends_with(&self.filter_search_domain) || request_name.ends_with(&filter_search_domain_ndots) || request_name.matches('.').count() == 1 { let mut nx_message = req.clone(); nx_message.set_response_code(ResponseCode::NXDomain); reply(sender.clone(), src_address, &nx_message); } else { let nameservers = dns_resolver.nameservers.clone(); tokio::spawn(async move { // forward dns request to hosts's /etc/resolv.conf for nameserver in nameservers { let connection = UdpClientStream::::new(SocketAddr::new( nameserver.into(), 53, )); if let Ok((cl, req_sender)) = AsyncClient::connect(connection).await { tokio::spawn(req_sender); if let Some(resp) = forward_dns_req(cl, req.clone()).await { if reply(sender.clone(), src_address, &resp).is_some() { // request resolved from following resolver so // break and don't try other resolvers break; } } } } }); } } } Err(e) => error!("Error parsing dns message {:?}", e), } }, } } Ok(()) //TODO: My IDE sees this as unreachable code. Fix when refactoring } } fn reply(mut sender: BufDnsStreamHandle, socket_addr: SocketAddr, msg: &Message) -> Option<()> { let id = msg.id(); let mut msg_mut = msg.clone(); msg_mut.set_message_type(MessageType::Response); // If `RD` is set and `RA` is false set `RA`. if msg.recursion_desired() && !msg.recursion_available() { msg_mut.set_recursion_available(true); } let response = SerialMessage::new(msg_mut.to_vec().ok()?, socket_addr); match sender.send(response) { Ok(_) => { debug!("[{}] success reponse", id); } Err(e) => { error!("[{}] fail response: {:?}", id, e); } } Some(()) } fn parse_dns_msg(body: SerialMessage) -> Option<(String, RecordType, Message)> { match Message::from_vec(body.bytes()) { Ok(msg) => { let mut name: String = "".to_string(); let mut record_type: RecordType = RecordType::A; let parsed_msg = format!( "[{}] parsed message body: {} edns: {}", msg.id(), msg.queries() .first() .map(|q| { name = q.name().to_string(); record_type = q.query_type(); format!("{} {} {}", q.name(), q.query_type(), q.query_class(),) }) .unwrap_or_else(Default::default,), msg.extensions().is_some(), ); debug!("parsed message {:?}", parsed_msg); Some((name, record_type, msg)) } Err(e) => { warn!("Failed while parsing message: {}", e); None } } } async fn forward_dns_req(cl: AsyncClient, message: Message) -> Option { let req = DnsRequest::new(message, Default::default()); let id = req.id(); match cl.send(req).try_next().await { Ok(Some(response)) => { for answer in response.answers() { debug!( "{} {} {} {} => {:#?}", id, answer.name().to_string(), answer.record_type(), answer.dns_class(), answer.data(), ); } let mut response_message = response.into_message(); response_message.set_id(id); Some(response_message) } Ok(None) => { error!("{} dns request got empty response", id); None } Err(e) => { error!("{} dns request failed: {}", id, e); None } } } containers-aardvark-dns-4fd01fd/src/dns/mod.rs000066400000000000000000000000211452673546000214310ustar00rootroot00000000000000pub mod coredns; containers-aardvark-dns-4fd01fd/src/lib.rs000066400000000000000000000001201452673546000206340ustar00rootroot00000000000000pub mod backend; pub mod commands; pub mod config; pub mod dns; pub mod server; containers-aardvark-dns-4fd01fd/src/main.rs000066400000000000000000000050361452673546000210250ustar00rootroot00000000000000use std::env; use std::str::FromStr; use clap::{Parser, Subcommand}; use aardvark_dns::commands::{run, version}; use log::Level; use syslog::{BasicLogger, Facility, Formatter3164}; #[derive(Parser, Debug)] #[clap(version = env!("CARGO_PKG_VERSION"))] struct Opts { /// Path to configuration directory #[clap(short, long)] config: Option, /// Host port for aardvark servers, defaults to 5533 #[clap(short, long)] port: Option, /// Filters search domain for backward compatiblity with dnsname/dnsmasq #[clap(short, long)] filter_search_domain: Option, /// Aardvark-dns trig command #[clap(subcommand)] subcmd: SubCommand, } #[derive(Subcommand, Debug)] enum SubCommand { /// Runs the aardvark dns server with the specified configuration directory. Run(run::Run), /// Display info about aardvark. Version(version::Version), } fn main() { let formatter = Formatter3164 { facility: Facility::LOG_USER, hostname: None, process: "aardvark-dns".into(), pid: 0, }; let log_level = match env::var("RUST_LOG") { Ok(val) => match Level::from_str(&val) { Ok(level) => level, Err(e) => { eprintln!("aardvark-dns: failed to parse RUST_LOG level: {}", e); Level::Info } }, // if env is not set default to info Err(_) => Level::Info, }; // On error do nothing, running on system without syslog is fine and we should not clutter // logs with meaningless errors, https://github.com/containers/podman/issues/19809. if let Ok(logger) = syslog::unix(formatter) { if let Err(e) = log::set_boxed_logger(Box::new(BasicLogger::new(logger))) .map(|()| log::set_max_level(log_level.to_level_filter())) { eprintln!("aardvark-dns: failed to initialize syslog logger: {}", e) }; } let opts = Opts::parse(); let dir = opts.config.unwrap_or_else(|| String::from("/dev/stdin")); let port = opts.port.unwrap_or(5533_u32); let filter_search_domain = opts .filter_search_domain .unwrap_or_else(|| String::from(".dns.podman")); let result = match opts.subcmd { SubCommand::Run(run) => run.exec(dir, port, filter_search_domain), SubCommand::Version(version) => version.exec(), }; match result { Ok(_) => {} Err(err) => { eprintln!("aardvark-dns: {}", err); std::process::exit(1); } } } #[cfg(test)] mod test; containers-aardvark-dns-4fd01fd/src/server/000077500000000000000000000000001452673546000210355ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/server/mod.rs000066400000000000000000000001021452673546000221530ustar00rootroot00000000000000// Serve DNS requests on the given bind addresses. pub mod serve; containers-aardvark-dns-4fd01fd/src/server/serve.rs000066400000000000000000000202561452673546000225340ustar00rootroot00000000000000use crate::backend::DNSBackend; use crate::config; use crate::config::constants::AARDVARK_PID_FILE; use crate::dns::coredns::CoreDns; use log::{debug, error, info}; use signal_hook::consts::signal::SIGHUP; use signal_hook::iterator::Signals; use std::fs; use std::net::IpAddr; use std::net::Ipv4Addr; use std::sync::{Arc, Mutex}; use std::thread; use async_broadcast::broadcast; use std::fs::File; use std::io::prelude::*; use std::path::Path; use std::process; // Will be only used by server to share backend // across threads #[derive(Clone)] struct DNSBackendWithArc { pub backend: Arc, } pub fn create_pid(config_path: &str) -> Result<(), std::io::Error> { // before serving write its pid to _config_path so other process can notify // aardvark of data change. let path = Path::new(config_path).join(AARDVARK_PID_FILE); let mut pid_file = match File::create(path) { Err(err) => { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("Unable to get process pid: {}", err), )); } Ok(file) => file, }; let server_pid = process::id().to_string(); if let Err(err) = pid_file.write_all(server_pid.as_bytes()) { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("Unable to write pid to file: {}", err), )); } Ok(()) } pub fn serve( config_path: &str, port: u32, filter_search_domain: &str, ) -> Result<(), std::io::Error> { loop { if let Err(er) = core_serve_loop(config_path, port, filter_search_domain) { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("Server Error {}", er), )); } } } fn core_serve_loop( config_path: &str, port: u32, filter_search_domain: &str, ) -> Result<(), std::io::Error> { let mut signals = Signals::new([SIGHUP])?; match config::parse_configs(config_path) { Ok((backend, listen_ip_v4, listen_ip_v6)) => { let mut thread_handles = vec![]; // we need mutex so we so threads can still modify lock // clippy is only doing linting and asking us to use atomic bool // so manually allow this #[allow(clippy::mutex_atomic)] let kill_switch = Arc::new(Mutex::new(false)); // kill server if listen_ip's are empty if listen_ip_v4.is_empty() && listen_ip_v6.is_empty() { //no configuration found kill the server info!("No configuration found stopping the sever"); let path = Path::new(config_path).join(AARDVARK_PID_FILE); match fs::remove_file(path) { Ok(_) => {} Err(err) => { error!("failed to remove the pid file: {}", &err); process::exit(1); } } process::exit(0); } // Prevent memory duplication: since backend is immutable across threads so create Arc and share let shareable_arc = DNSBackendWithArc { backend: Arc::from(backend), }; debug!("Successfully parsed config"); debug!("Listen v4 ip {:?}", listen_ip_v4); debug!("Listen v6 ip {:?}", listen_ip_v6); // create a receiver and sender for async broadcast channel let (tx, rx) = broadcast(1000); for (network_name, listen_ip_list) in listen_ip_v4 { for ip in listen_ip_list { let network_name_clone = network_name.clone(); let filter_search_domain_clone = filter_search_domain.to_owned(); let backend_arc_clone = shareable_arc.clone(); let kill_switch_arc_clone = Arc::clone(&kill_switch); let receiver = rx.clone(); let handle = thread::spawn(move || { if let Err(_e) = start_dns_server( &network_name_clone, IpAddr::V4(ip), backend_arc_clone, kill_switch_arc_clone, port, filter_search_domain_clone.to_string(), receiver, ) { error!("Unable to start server {}", _e); return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("Error while invoking start_dns_server: {}", _e), )); } Ok(()) }); thread_handles.push(handle); } } for (network_name, listen_ip_list) in listen_ip_v6 { for ip in listen_ip_list { let network_name_clone = network_name.clone(); let filter_search_domain_clone = filter_search_domain.to_owned(); let backend_arc_clone = shareable_arc.clone(); let kill_switch_arc_clone = Arc::clone(&kill_switch); let receiver = rx.clone(); let handle = thread::spawn(move || { if let Err(_e) = start_dns_server( &network_name_clone, IpAddr::V6(ip), backend_arc_clone, kill_switch_arc_clone, port, filter_search_domain_clone.to_string(), receiver, ) { return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("Error while invoking start_dns_server: {}", _e), )); } Ok(()) }); thread_handles.push(handle); } } let handle_signal = thread::spawn(move || { if let Some(sig) = signals.forever().next() { info!("Received SIGHUP will refresh servers: {:?}", sig); } }); if handle_signal.join().is_ok() { send_broadcast(&tx); if let Ok(mut switch) = kill_switch.lock() { *switch = true; }; } for handle in thread_handles { if let Err(e) = handle.join() { error!("Error from thread: {:?}", e); } } // close and drop broadcast channel tx.close(); drop(tx); Ok(()) } Err(e) => Err(std::io::Error::new( std::io::ErrorKind::Other, format!("unable to parse config: {}", e), )), } } #[tokio::main] async fn start_dns_server( name: &str, addr: IpAddr, backend_arc: DNSBackendWithArc, kill_switch: Arc>, port: u32, filter_search_domain: String, rx: async_broadcast::Receiver, ) -> Result<(), std::io::Error> { let forward: IpAddr = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)); match CoreDns::new( addr, port, name, forward, 53_u16, backend_arc.backend, kill_switch, filter_search_domain, rx, ) .await { Ok(mut server) => match server.run().await { Ok(_) => Ok(()), Err(e) => Err(std::io::Error::new( std::io::ErrorKind::Other, format!("unable to start CoreDns server: {}", e), )), }, Err(e) => Err(std::io::Error::new( std::io::ErrorKind::Other, format!("unable to create CoreDns server: {}", e), )), } } #[tokio::main] async fn send_broadcast(tx: &async_broadcast::Sender) { if let Err(e) = tx.broadcast(true).await { error!("unable to broadcast to child threads: {:?}", e); } } containers-aardvark-dns-4fd01fd/src/test/000077500000000000000000000000001452673546000205065ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/test/config/000077500000000000000000000000001452673546000217535ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/test/config/network_scoped_custom_dns/000077500000000000000000000000001452673546000272375ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/test/config/network_scoped_custom_dns/podman000066400000000000000000000006711452673546000304440ustar00rootroot0000000000000010.88.0.1 127.0.0.1,::2 68fb291b0318b54a71f6f3636e58bd0896f084e5ba4fa311ecf36e019c5e6e43 10.88.0.2 condescendingnash 8.8.8.8 68fb291b0318b54a71f6f3636e58bd0896f084e5ba4fa311ecf36e019c5e6e48 10.88.0.5 HelloWorld 3.3.3.3,1.1.1.1,::1 95655fb6832ba134efa66e9c80862a6c9b04f3cc6abf8adfdda8c38112c2c6fa 10.88.0.3 hopefulmontalcini,testdbctr 8bcc5fe0cb09bee5dfb71d61503a87688cfc82aa5f130bcedb19357a17765926 10.88.0.4 trustingzhukovsky,ctr1,ctra containers-aardvark-dns-4fd01fd/src/test/config/podman/000077500000000000000000000000001452673546000232315ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/test/config/podman/podman000066400000000000000000000006171452673546000244360ustar00rootroot0000000000000010.88.0.1 68fb291b0318b54a71f6f3636e58bd0896f084e5ba4fa311ecf36e019c5e6e43 10.88.0.2 condescendingnash 68fb291b0318b54a71f6f3636e58bd0896f084e5ba4fa311ecf36e019c5e6e48 10.88.0.5 HelloWorld 95655fb6832ba134efa66e9c80862a6c9b04f3cc6abf8adfdda8c38112c2c6fa 10.88.0.3 hopefulmontalcini,testdbctr 8bcc5fe0cb09bee5dfb71d61503a87688cfc82aa5f130bcedb19357a17765926 10.88.0.4 trustingzhukovsky,ctr1,ctra containers-aardvark-dns-4fd01fd/src/test/config/podman2000066400000000000000000000004731452673546000232420ustar00rootroot0000000000000010.88.0.1 68fb291b0318b54a71f6f3636e58bd0896f084e5ba4fa311ecf36e019c5e6e43 10.88.0.2 condescending_nash 95655fb6832ba134efa66e9c80862a6c9b04f3cc6abf8adfdda8c38112c2c6fa 10.88.0.3 hopeful_montalcini,testdbctr 8bcc5fe0cb09bee5dfb71d61503a87688cfc82aa5f130bcedb19357a17765926 10.88.0.4 trusting_zhukovsky,ctr1,ctra containers-aardvark-dns-4fd01fd/src/test/config/podman_bad_config/000077500000000000000000000000001452673546000253645ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/test/config/podman_bad_config/podman000066400000000000000000000002141452673546000265620ustar00rootroot0000000000000010.88.0.1 dfdsfds 10.88.0.2 condescendingnash 95655fb6832ba134efa66e9c80862a6c9b04f3cc6abf8adfdda8c38112c2c6fa hopefulmontalcini,testdbctr containers-aardvark-dns-4fd01fd/src/test/config/podman_custom_dns_servers/000077500000000000000000000000001452673546000272405ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/test/config/podman_custom_dns_servers/podman000066400000000000000000000006531452673546000304450ustar00rootroot0000000000000010.88.0.1 68fb291b0318b54a71f6f3636e58bd0896f084e5ba4fa311ecf36e019c5e6e43 10.88.0.2 condescendingnash 8.8.8.8 68fb291b0318b54a71f6f3636e58bd0896f084e5ba4fa311ecf36e019c5e6e48 10.88.0.5 HelloWorld 3.3.3.3,1.1.1.1,::1 95655fb6832ba134efa66e9c80862a6c9b04f3cc6abf8adfdda8c38112c2c6fa 10.88.0.3 hopefulmontalcini,testdbctr 8bcc5fe0cb09bee5dfb71d61503a87688cfc82aa5f130bcedb19357a17765926 10.88.0.4 trustingzhukovsky,ctr1,ctra containers-aardvark-dns-4fd01fd/src/test/config/podman_v6_entries/000077500000000000000000000000001452673546000253755ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/src/test/config/podman_v6_entries/podman_v6_entries000066400000000000000000000006361452673546000307470ustar00rootroot0000000000000010.89.0.1 7b46c7ad93fcbcb945c35286a5ba19d6976093e2ce39d2cb38ba1eba636404ab 10.89.0.2 test1,7b46c7ad93fc 7b46c7ad93fcbcb945c35286a5ba19d6976093e2ce39d2cb38ba1eba636404ab fdfd:733b:dc3:220b::2 test1,7b46c7ad93fc 88dde8a2489780d3c8c90db54a9a97faf5dbe4f555b23e27880ca189dae0e2b0 10.89.0.3 test2,88dde8a24897 88dde8a2489780d3c8c90db54a9a97faf5dbe4f555b23e27880ca189dae0e2b0 fdfd:733b:dc3:220b::3 test2,88dde8a24897 containers-aardvark-dns-4fd01fd/src/test/config/podman_v6_entries/podman_v6_entries_proper000066400000000000000000000003761452673546000323370ustar00rootroot0000000000000010.0.0.1,10.0.1.1,fdfd::1,fddd::1 f35256b5e2f72ec8cb7d974d4f8841686fc8921fdfbc867285b50164e313f715 10.0.0.2,10.0.1.2 fdfd::2,fddd::2 testmulti1 e5df0cdbe0136a30cc3e848d495d2cc6dada25b7dedc776b4584ce2cbba6f06f 10.0.0.3,10.0.1.3 fdfd::3,fddd::3 testmulti2 containers-aardvark-dns-4fd01fd/src/test/config/test1000066400000000000000000000003661452673546000227430ustar00rootroot00000000000000fd35:fb67:49e1:349::1 68fb291b0318b54a71f6f3636e58bd0896f084e5ba4fa311ecf36e019c5e6e43 fd35:fb67:49e1:349::2 condescending_nash 8bcc5fe0cb09bee5dfb71d61503a87688cfc82aa5f130bcedb19357a17765926 fd35:fb67:49e1:349::3 trusting_zhukovsky,ctr1,ctra containers-aardvark-dns-4fd01fd/src/test/mod.rs000066400000000000000000000000161452673546000216300ustar00rootroot00000000000000pub mod test; containers-aardvark-dns-4fd01fd/src/test/test.rs000066400000000000000000000636671452673546000220550ustar00rootroot00000000000000//use super::*; #[cfg(test)] // perform unit tests for config, backend and lookup logic // following tests will not test server and event loop since // event-loop and server can be tested via integration tests mod tests { use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use aardvark_dns::backend::DNSResult; use aardvark_dns::config; use std::str::FromStr; /* -------------------------------------------- */ // --------- Test aardvark-dns config --------- /* -------------------------------------------- */ #[test] // Test loading of config file from directory fn test_loading_config_file() { match config::parse_configs("src/test/config/podman") { Ok(_) => {} Err(e) => panic!("{}", e), } } #[test] // Test loading of config file from directory with custom DNS for containers fn test_loading_config_file_with_dns_servers() { config::parse_configs("src/test/config/podman_custom_dns_servers").unwrap(); } #[test] // Test loading of config file from directory with custom DNS for containers // and custom DNS servers at network level as well. fn test_loading_config_file_with_network_scoped_dns_servers() { config::parse_configs("src/test/config/network_scoped_custom_dns").unwrap(); } #[test] // Parse config files from stub data fn test_parsing_config_files() { match config::parse_configs("src/test/config/podman") { Ok((_, listen_ip_v4, _)) => { listen_ip_v4.contains_key("podman"); assert_eq!(listen_ip_v4["podman"].len(), 1); assert_eq!("10.88.0.1".parse(), Ok(listen_ip_v4["podman"][0])); } Err(e) => panic!("{}", e), } } #[test] // Parse bad config files must fail fn test_parsing_bad_config_files() { match config::parse_configs("src/test/config/podman_bad_config") { Ok((_, _, _)) => panic!("parsing bad config must fail"), Err(_) => {} } } /* -------------------------------------------- */ // -------Verify backend custom dns server ---- /* -------------------------------------------- */ #[test] // Backend must populate ctr_dns_servers via custom // DNS servers for container from the aardvark config fn test_backend_custom_dns_server() { match config::parse_configs("src/test/config/podman_custom_dns_servers") { Ok((backend, _, _)) => { // Should contain custom DNS server 8.8.8.8 let mut dns_server = backend .ctr_dns_server .get(&IpAddr::V4(Ipv4Addr::new(10, 88, 0, 2))); let mut expected_dns_server = IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)); assert_eq!(dns_server.unwrap().clone().unwrap()[0], expected_dns_server); // Should contain custom DNS servers 3.3.3.3 and 1.1.1.1 dns_server = backend .ctr_dns_server .get(&IpAddr::V4(Ipv4Addr::new(10, 88, 0, 5))); expected_dns_server = IpAddr::V4(Ipv4Addr::new(3, 3, 3, 3)); assert_eq!(dns_server.unwrap().clone().unwrap()[0], expected_dns_server); expected_dns_server = IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)); assert_eq!(dns_server.unwrap().clone().unwrap()[1], expected_dns_server); expected_dns_server = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); assert_eq!(dns_server.unwrap().clone().unwrap()[2], expected_dns_server); // Shoudld not contain any DNS server dns_server = backend .ctr_dns_server .get(&IpAddr::V4(Ipv4Addr::new(10, 88, 0, 3))); assert_eq!(dns_server.unwrap().clone(), None); } Err(e) => panic!("{}", e), } } #[test] // Backend must populate ctr_dns_servers via custom // DNS servers for container from container entry and // network dns servers as well. fn test_backend_network_scoped_custom_dns_server() { match config::parse_configs("src/test/config/network_scoped_custom_dns") { Ok((backend, _, _)) => { let expected_dnsservers = vec![ IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 2)), ]; let test_cases_source = ["10.88.0.2", "10.88.0.3", "10.88.0.4", "10.88.0.5"]; // verify if network scoped resolvers for all the containers is equivalent to // expectedDNSServers for container in test_cases_source.iter() { let output = backend.get_network_scoped_resolvers(&IpAddr::from_str(container).unwrap()); let mut output_dnsservers = Vec::new(); for server in output.unwrap().iter() { output_dnsservers.push(*server); } assert_eq!(expected_dnsservers, output_dnsservers); } } Err(e) => panic!("{}", e), } } /* -------------------------------------------- */ // -------Test aardvark-dns lookup logic ------ /* -------------------------------------------- */ #[test] // Check lookup query from backend and simulate // dns request from same container to itself but // aardvark must return one ip address i.e v4. // Request address must be v4. // Same container --> (resolve) Same container name --> (on) Same Network fn test_lookup_queries_from_backend_simulate_same_container_request_from_v4_on_v4_entries() { match config::parse_configs("src/test/config/podman") { Ok((backend, _, _)) => { match backend.lookup(&"10.88.0.2".parse().unwrap(), "condescendingnash") { DNSResult::Success(ip_vec) => { assert_eq!(ip_vec.len(), 1); assert_eq!("10.88.0.2".parse(), Ok(ip_vec[0])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // case-insensitive dns request from same container // to itself but aardvark must return one ip address i.e v4. // Request address must be v4. // Same container --> (resolve) Same container name --> (on) Same Network fn test_lookup_queries_from_backend_simulate_same_container_request_from_v4_on_v4_entries_case_insensitive( ) { match config::parse_configs("src/test/config/podman") { Ok((backend, _, _)) => { match backend.lookup(&"10.88.0.2".parse().unwrap(), "helloworld") { DNSResult::Success(ip_vec) => { assert_eq!(ip_vec.len(), 1); assert_eq!("10.88.0.5".parse(), Ok(ip_vec[0])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // case-insensitive dns request from same container // to itself but aardvark must return one ip address i.e v4. // Request address must be v4. // Same container --> (resolve) Same container name --> (on) Same Network fn test_lookup_queries_from_backend_simulate_same_container_request_from_v4_on_v4_entries_case_insensitive_uppercase( ) { match config::parse_configs("src/test/config/podman") { Ok((backend, _, _)) => { match backend.lookup(&"10.88.0.2".parse().unwrap(), "HELLOWORLD") { DNSResult::Success(ip_vec) => { assert_eq!(ip_vec.len(), 1); assert_eq!("10.88.0.5".parse(), Ok(ip_vec[0])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // nx_domain on bad lookup queries. fn test_lookup_queries_from_backend_simulate_nx_domain() { match config::parse_configs("src/test/config/podman") { Ok((backend, _, _)) => { match backend.lookup(&"10.88.0.2".parse().unwrap(), "somebadquery") { DNSResult::NXDomain => {} _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // dns request from same container to itself but // aardvark must return one ip address i.e v4. // Request address must be v4. // Same container --> (resolve) different container name --> (on) Same Network fn test_lookup_queries_from_backend_simulate_different_container_request_from_v4() { match config::parse_configs("src/test/config/podman") { Ok((backend, _, _)) => { match backend.lookup(&"10.88.0.2".parse().unwrap(), "trustingzhukovsky") { DNSResult::Success(ip_vec) => { assert_eq!(ip_vec.len(), 1); assert_eq!("10.88.0.4".parse(), Ok(ip_vec[0])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // dns request from same container to itself but // aardvark must return one ip address i.e v4. // Request address must be v4. // Same container --> (resolve) different container name by alias --> (on) Same Network fn test_lookup_queries_from_backend_simulate_different_container_request_from_v4_by_alias() { match config::parse_configs("src/test/config/podman") { Ok((backend, _, _)) => match backend.lookup(&"10.88.0.2".parse().unwrap(), "ctr1") { DNSResult::Success(ip_vec) => { // verfiy length for issues like: https://github.com/containers/aardvark-dns/issues/120 assert_eq!(ip_vec.len(), 1); assert_eq!("10.88.0.4".parse(), Ok(ip_vec[0])); } _ => panic!("unexpected dns result"), }, Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // dns request from same container to itself but // aardvark must return two ip address for v4 and v6. // Request address must be v4. // Same container --> (resolve) Same container name --> (on) Same Network fn test_lookup_queries_from_backend_simulate_same_container_request_from_v4_on_v6_and_v4_entries( ) { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { listen_ip_v6.contains_key("podman_v6_entries"); listen_ip_v4.contains_key("podman_v6_entries"); match backend.lookup(&"10.89.0.2".parse().unwrap(), "test1") { DNSResult::Success(ip_vec) => { // verfiy length for issues like: https://github.com/containers/aardvark-dns/issues/120 assert_eq!(ip_vec.len(), 2); assert_eq!("10.89.0.2".parse(), Ok(ip_vec[0])); assert_eq!("fdfd:733b:dc3:220b::2".parse(), Ok(ip_vec[1])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // dns request from same container to itself but // aardvark must return two ip address for v4 and v6. // Request address must be v6. // Same container --> (resolve) Same container name --> (on) Same Network fn test_lookup_queries_from_backend_simulate_same_container_request_from_v6_on_v6_and_v4_entries( ) { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { listen_ip_v6.contains_key("podman_v6_entries"); listen_ip_v4.contains_key("podman_v6_entries"); match backend.lookup(&"fdfd:733b:dc3:220b::2".parse().unwrap(), "test1") { DNSResult::Success(ip_vec) => { // verfiy length for issues like: https://github.com/containers/aardvark-dns/issues/120 assert_eq!(ip_vec.len(), 2); assert_eq!("10.89.0.2".parse(), Ok(ip_vec[0])); assert_eq!("fdfd:733b:dc3:220b::2".parse(), Ok(ip_vec[1])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // dns request from container to another container but // aardvark must return two ip address for v4 and v6. // Request address must be v6. // Same container --> (resolve) different container name --> (on) Same Network fn test_lookup_queries_from_backend_simulate_different_container_request_from_v6_on_v6_and_v4_entries( ) { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { listen_ip_v6.contains_key("podman_v6_entries"); listen_ip_v4.contains_key("podman_v6_entries"); match backend.lookup(&"fdfd:733b:dc3:220b::2".parse().unwrap(), "test2") { DNSResult::Success(ip_vec) => { // verfiy length for issues like: https://github.com/containers/aardvark-dns/issues/120 assert_eq!(ip_vec.len(), 2); assert_eq!("10.89.0.3".parse(), Ok(ip_vec[0])); assert_eq!("fdfd:733b:dc3:220b::3".parse(), Ok(ip_vec[1])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // dns request from container to another container but // aardvark must return two ip address for v4 and v6. // Request address must be v6. // Same container --> (resolve) different container name --> (on) Same Network fn test_lookup_queries_from_backend_simulate_different_container_request_from_v4_on_v6_and_v4_entries( ) { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { listen_ip_v6.contains_key("podman_v6_entries"); listen_ip_v4.contains_key("podman_v6_entries"); match backend.lookup(&"10.89.0.2".parse().unwrap(), "test2") { DNSResult::Success(ip_vec) => { // verfiy length for issues like: https://github.com/containers/aardvark-dns/issues/120 assert_eq!(ip_vec.len(), 2); assert_eq!("10.89.0.3".parse(), Ok(ip_vec[0])); assert_eq!("fdfd:733b:dc3:220b::3".parse(), Ok(ip_vec[1])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check lookup query from backend and simulate // dns request from container to another container but // aardvark must return two ip address for v4 and v6. // Request address must be v6. // Same container --> (resolve) different container by id --> (on) Same Network fn test_lookup_queries_from_backend_simulate_different_container_request_by_id_from_v4_on_v6_and_v4_entries( ) { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { listen_ip_v6.contains_key("podman_v6_entries"); listen_ip_v4.contains_key("podman_v6_entries"); match backend.lookup(&"10.89.0.2".parse().unwrap(), "88dde8a24897") { DNSResult::Success(ip_vec) => { // verfiy length for issues like: https://github.com/containers/aardvark-dns/issues/120 assert_eq!(ip_vec.len(), 2); assert_eq!("10.89.0.3".parse(), Ok(ip_vec[0])); assert_eq!("fdfd:733b:dc3:220b::3".parse(), Ok(ip_vec[1])); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } /* -------------------------------------------- */ // ---Test aardvark-dns reverse lookup logic -- /* -------------------------------------------- */ #[test] // Check reverse lookup query from backend and simulate // dns request from same container to itself by IP // aardvark must return container name and alias // Same container --> (resolve) Same ip --> (on) Same Network fn test_reverse_lookup_queries_from_backend_by_ip_v4() { match config::parse_configs("src/test/config/podman") { Ok((backend, _, _)) => { match backend .reverse_lookup(&"10.88.0.4".parse().unwrap(), &"10.88.0.4".parse().unwrap()) { Some(lookup_vec) => { assert_eq!( &vec![ "trustingzhukovsky".to_string(), "ctr1".to_string(), "ctra".to_string() ], lookup_vec ); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } #[test] // Check reverse lookup query from backend and simulate // dns request from same container to itself by IP // aardvark must return container name and alias // Same container --> (resolve) Same ip --> (on) Same Network fn test_reverse_lookup_queries_from_backend_by_ip_v6() { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, _, _)) => { match backend.reverse_lookup( &"fdfd:733b:dc3:220b::2".parse().unwrap(), &"fdfd:733b:dc3:220b::2".parse().unwrap(), ) { Some(lookup_vec) => { assert_eq!( &vec!["test1".to_string(), "7b46c7ad93fc".to_string()], lookup_vec ); } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } /* -------------------------------------------- */ // ---------Test aardvark-dns backend --------- /* -------------------------------------------- */ #[test] // Check ip_mappings generated by backend fn test_generated_ip_mappings_in_backend() { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { listen_ip_v6.contains_key("podman_v6_entries"); listen_ip_v4.contains_key("podman_v6_entries"); backend .ip_mappings .contains_key(&"fdfd:733b:dc3:220b::2".parse().unwrap()); backend .ip_mappings .contains_key(&"10.89.0.3".parse().unwrap()); assert_eq!( vec!["podman_v6_entries"], backend.ip_mappings[&"fdfd:733b:dc3:220b::2".parse().unwrap()] ); assert_eq!( vec!["podman_v6_entries"], backend.ip_mappings[&"10.89.0.3".parse().unwrap()] ); } Err(e) => panic!("{}", e), } } #[test] // Check name_mappings generated by backend fn test_generated_name_mappings_in_backend() { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { listen_ip_v6.contains_key("podman_v6_entries"); listen_ip_v4.contains_key("podman_v6_entries"); // check if contains key backend.name_mappings.contains_key("podman_v6_entries"); // container id must be in name entries backend.name_mappings["podman_v6_entries"].contains_key("7b46c7ad93fc"); backend.name_mappings["podman_v6_entries"].contains_key("88dde8a24897"); // container names must be in name entries backend.name_mappings["podman_v6_entries"].contains_key("test1"); backend.name_mappings["podman_v6_entries"].contains_key("test2"); assert_eq!( "10.89.0.3".parse(), Ok(backend.name_mappings["podman_v6_entries"]["test2"][0]) ); assert_eq!( "fdfd:733b:dc3:220b::3".parse(), Ok(backend.name_mappings["podman_v6_entries"]["test2"][1]) ); // name entries must contain all ip addresses for container test1 assert_eq!( "10.89.0.2".parse(), Ok(backend.name_mappings["podman_v6_entries"]["test1"][0]) ); assert_eq!( "fdfd:733b:dc3:220b::2".parse(), Ok(backend.name_mappings["podman_v6_entries"]["test1"][1]) ); // name entries must contain all ip addresses for container with id 7b46c7ad93fc assert_eq!( "10.89.0.2".parse(), Ok(backend.name_mappings["podman_v6_entries"]["7b46c7ad93fc"][0]) ); assert_eq!( "fdfd:733b:dc3:220b::2".parse(), Ok(backend.name_mappings["podman_v6_entries"]["7b46c7ad93fc"][1]) ); // name entries must contain all ip addresses for container with id 88dde8a24897 assert_eq!( "10.89.0.3".parse(), Ok(backend.name_mappings["podman_v6_entries"]["88dde8a24897"][0]) ); assert_eq!( "fdfd:733b:dc3:220b::3".parse(), Ok(backend.name_mappings["podman_v6_entries"]["88dde8a24897"][1]) ); } Err(e) => panic!("{}", e), } } #[test] // Check reverse_mappings generated by backend fn test_generated_reverse_mappings_in_backend() { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { listen_ip_v6.contains_key("podman_v6_entries"); listen_ip_v4.contains_key("podman_v6_entries"); // all ips must have reverse lookups backend.reverse_mappings["podman_v6_entries"] .contains_key(&"10.89.0.3".parse().unwrap()); backend.reverse_mappings["podman_v6_entries"] .contains_key(&"10.89.0.2".parse().unwrap()); backend.reverse_mappings["podman_v6_entries"] .contains_key(&"fdfd:733b:dc3:220b::2".parse().unwrap()); backend.reverse_mappings["podman_v6_entries"] .contains_key(&"fdfd:733b:dc3:220b::3".parse().unwrap()); } Err(e) => panic!("{}", e), } } #[test] // Parse a config which contains multiple ipv4 and ipv6 addresses ona single line fn test_parse_multiple_ipv4_ipv6_addresses() { match config::parse_configs("src/test/config/podman_v6_entries") { Ok((backend, listen_ip_v4, listen_ip_v6)) => { assert_eq!( listen_ip_v4["podman_v6_entries_proper"], vec![ "10.0.0.1".parse::().unwrap(), "10.0.1.1".parse().unwrap() ] ); assert_eq!( listen_ip_v6["podman_v6_entries_proper"], vec![ "fdfd::1".parse::().unwrap(), "fddd::1".parse().unwrap() ] ); match backend.lookup(&"10.0.0.2".parse().unwrap(), "testmulti1") { DNSResult::Success(ip_vec) => { assert_eq!( ip_vec, vec![ "10.0.0.2".parse::().unwrap(), "10.0.1.2".parse().unwrap(), "fdfd::2".parse().unwrap(), "fddd::2".parse().unwrap() ] ) } _ => panic!("unexpected dns result"), } match backend.lookup(&"10.0.0.2".parse().unwrap(), "testmulti2") { DNSResult::Success(ip_vec) => { assert_eq!( ip_vec, vec![ "10.0.0.3".parse::().unwrap(), "10.0.1.3".parse().unwrap(), "fdfd::3".parse().unwrap(), "fddd::3".parse().unwrap() ] ) } _ => panic!("unexpected dns result"), } } Err(e) => panic!("{}", e), } } } containers-aardvark-dns-4fd01fd/test/000077500000000000000000000000001452673546000177175ustar00rootroot00000000000000containers-aardvark-dns-4fd01fd/test/100-basic-name-resolution.bats000066400000000000000000000157741452673546000253060ustar00rootroot00000000000000#!/usr/bin/env bats -*- bats -*- # # basic netavark tests # load helpers # custom DNS server is set to `127.0.0.255` which is invalid DNS server # hence all the external request must fail, this test is expected to fail # with exit code 124 @test "basic container - dns itself (custom bad dns server)" { setup_slirp4netns subnet_a=$(random_subnet 5) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" custom_dns_server='"127.0.0.255"' aliases='"a1", "1a"' config_a1=$config ip_a1=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$config_a1" a1_pid=$CONTAINER_NS_PID run_in_container_netns "$a1_pid" "dig" "+short" "aone" "@$gw" assert "$ip_a1" # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" # custom dns server is set to 3.3.3.3 which is not a valid DNS server so external DNS request must fail expected_rc=124 run_in_container_netns "$a1_pid" "dig" "+short" "google.com" "@$gw" } # custom DNS server is set to `8.8.8.8, 1.1.1.1` which is valid DNS server # hence all the external request must paas. @test "basic container - dns itself (custom good dns server)" { setup_slirp4netns subnet_a=$(random_subnet 5) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" custom_dns_server='"8.8.8.8","1.1.1.1"' aliases='"a1", "1a"' config_a1=$config ip_a1=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$config_a1" a1_pid=$CONTAINER_NS_PID run_in_container_netns "$a1_pid" "dig" "+short" "aone" "@$gw" assert "$ip_a1" # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" run_in_container_netns "$a1_pid" "dig" "+short" "google.com" "@$gw" # validate that we get an ipv4 assert "$output" =~ "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" } @test "basic container - dns itself custom" { setup_slirp4netns subnet_a=$(random_subnet 5) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" aliases='"a1", "1a"' config_a1=$config ip_a1=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$config_a1" a1_pid=$CONTAINER_NS_PID run_in_container_netns "$a1_pid" "dig" "+short" "aone" "@$gw" assert "$ip_a1" # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" run_in_container_netns "$a1_pid" "dig" "+short" "google.com" "@$gw" # validate that we get an ipv4 assert "$output" =~ "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" } @test "basic container - ndots incomplete bad entry must NXDOMAIN instead of forwarding and timing out" { setup_slirp4netns subnet_a=$(random_subnet 5) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" aliases='"a1", "1a"' config_a1=$config ip_a1=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$config_a1" a1_pid=$CONTAINER_NS_PID expected_rc=1 run_in_container_netns "$a1_pid" "host" "-t" "ns" "bone" "$gw" assert "$output" =~ "NXDOMAIN" } @test "basic container - dns itself on container with ipaddress v6" { setup_slirp4netns subnet_a=$(random_subnet 6) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" aliases='"a1", "1a"' config_a1=$config ip_a1=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$config_a1" a1_pid=$CONTAINER_NS_PID run_in_container_netns "$a1_pid" "dig" "+short" "aone" "@$gw" "AAAA" assert "$ip_a1" # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" run_in_container_netns "$a1_pid" "dig" "+short" "google.com" "@$gw" "AAAA" # validate that we got valid ipv6 # check that the output is not empty assert "$lines[0]" != "" "got at least one result" for ip in "${lines[@]}"; do run_helper ipcalc -6c "$ip" done # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" } @test "basic container - dns itself with long network name" { subnet_a=$(random_subnet 5) long_name="podman11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" create_config network_name="$long_name" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" aliases='"a1", "1a"' config_a1=$config ip_a1=$(echo "$config_a1" | jq -r .networks.$long_name.static_ips[0]) gw=$(echo "$config_a1" | jq -r .network_info.$long_name.subnets[0].gateway) create_container "$config_a1" a1_pid=$CONTAINER_NS_PID run_in_container_netns "$a1_pid" "dig" "+short" "aone" "@$gw" assert "$ip_a1" # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" } @test "two containers on the same network" { # container a1 subnet_a=$(random_subnet 5) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" aliases='"a1", "1a"' config_a1="$config" a1_ip=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$config_a1" a1_pid=$CONTAINER_NS_PID # container a2 create_config network_name="podman1" container_id=$(random_string 64) container_name="atwo" subnet="$subnet_a" aliases='"a2", "2a"' config_a2="$config" a2_ip=$(echo "$config_a2" | jq -r .networks.podman1.static_ips[0]) create_container "$config_a2" a2_pid="$CONTAINER_NS_PID" # Resolve container names to IPs dig "$a1_pid" "atwo" "$gw" assert "$a2_ip" # Set recursion bit assert "$output" !~ "WARNING: recursion requested but not available" dig "$a2_pid" "aone" "$gw" assert "$a1_ip" # Set recursion bit is already set if requested so output must not # contain unexpected warning. assert "$output" !~ "WARNING: recursion requested but not available" } containers-aardvark-dns-4fd01fd/test/200-two-networks.bats000066400000000000000000000100161452673546000235520ustar00rootroot00000000000000#!/usr/bin/env bats -*- bats -*- # # basic netavark tests # load helpers @test "two containers on different networks" { setup_slirp4netns # container a1 on subnet a subnet_a=$(random_subnet 5) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" a1_config="$config" a1_ip=$(echo "$a1_config" | jq -r .networks.podman1.static_ips[0]) a_gw=$(echo "$a1_config" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$a1_config" a1_pid="$CONTAINER_NS_PID" # container b1 on subnet b subnet_b=$(random_subnet 5) create_config network_name="podman2" container_id=$(random_string 64) container_name="bone" subnet="$subnet_b" b1_config="$config" b1_ip=$(echo "$b1_config" | jq -r .networks.podman2.static_ips[0]) b_gw=$(echo "$b1_config" | jq -r .network_info.podman2.subnets[0].gateway) create_container "$b1_config" b1_pid="$CONTAINER_NS_PID" # container a1 should not resolve b1 and we should get # a NXDOMAIN dig "$a1_pid" "bone" "$a_gw" assert "" expected_rc=1 run_in_container_netns "$a1_pid" "host" "-t" "ns" "bone" "$a_gw" assert "$output" =~ "Host bone not found" assert "$output" =~ "NXDOMAIN" # container b1 should not resolve a1 and we should get # a NXDOMAIN dig "$b1_pid" "aone" "$b_gw" assert "" expected_rc=1 run_in_container_netns "$b1_pid" "host" "-t" "ns" "aone" "$b_gw" assert "$output" =~ "Host aone not found" assert "$output" =~ "NXDOMAIN" # a1 should be able to resolve itself dig "$a1_pid" "aone" "$a_gw" assert $a1_ip # b1 should be able to resolve itself dig "$b1_pid" "bone" "$b_gw" assert $b1_ip } @test "two subnets with isolated container and one shared" { # container a1 on subnet a subnet_a=$(random_subnet 5) subnet_b=$(random_subnet 5) # A1 create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" a1_config=$config a1_container_id=$(echo "$a1_config" | jq -r .container_id) a1_ip=$(echo "$a1_config" | jq -r .networks.podman1.static_ips[0]) a_gw=$(echo "$a1_config" | jq -r .network_info.podman1.subnets[0].gateway) a1_hash=$(echo "$a1_config" | jq -r .network_info.podman1.id) create_container "$a1_config" a1_pid=$CONTAINER_NS_PID # container b1 on subnet b create_config network_name="podman2" container_id=$(random_string 64) container_name="bone" subnet="$subnet_b" b1_config=$config b1_ip=$(echo "$b1_config" | jq -r .networks.podman2.static_ips[0]) b_gw=$(echo "$b1_config" | jq -r .network_info.podman2.subnets[0].gateway) b1_hash=$(echo "$b1_config" | jq -r .network_info.podman1.id) create_container "$b1_config" b1_pid=$CONTAINER_NS_PID b_subnets=$(echo $b1_config | jq -r .network_info.podman2.subnets[0]) # AB2 create_config network_name="podman1" container_id=$(random_string 64) container_name="abtwo" subnet="$subnet_a" a2_config=$config a2_ip=$(echo "$a2_config" | jq -r .networks.podman1.static_ips[0]) b2_ip=$(random_ip_in_subnet "$subnet_b") create_network "podman2" "$b2_ip" "eth1" b2_network="{$new_network}" create_network_infos "podman2" "$b1_hash" "$b_subnets" b2_network_info="{$new_network_info}" ab2_config=$(jq -r ".networks += $b2_network" <<<"$a2_config") ab2_config=$(jq -r ".network_info += $b2_network_info" <<<"$ab2_config") create_container "$ab2_config" ab2_pid=$CONTAINER_NS_PID # aone should be able to resolve AB2 and NOT B1 dig "$a1_pid" "abtwo" "$a_gw" assert "$a2_ip" dig "$a1_pid" "bone" "$a_gw" assert "" # bone should be able to resolve AB2 and NOT A1 dig "$b1_pid" "abtwo" "$b_gw" assert "$b2_ip" dig "$b1_pid" "aone" "$b_gw" assert "" # abtwo should be able to resolve A1, B1, and AB2 on both gws dig "$ab2_pid" "aone" "$a_gw" assert "$a1_ip" dig "$ab2_pid" "bone" "$b_gw" assert "$b1_ip" # check ab2 from itself, first from the a side dig "$ab2_pid" "abtwo" "$a_gw" assert "${#lines[@]}" = 2 assert "$output" =~ "$a2_ip" assert "$output" =~ "$b2_ip" # and now from the bside dig "$ab2_pid" "abtwo" "$b_gw" assert "${#lines[@]}" = 2 assert "$output" =~ "$a2_ip" assert "$output" =~ "$b2_ip" } containers-aardvark-dns-4fd01fd/test/300-three-networks.bats000066400000000000000000000232511452673546000240560ustar00rootroot00000000000000#!/usr/bin/env bats -*- bats -*- # # basic netavark tests # load helpers @test "three networks with a connect" { subnet_a=$(random_subnet 5) subnet_b=$(random_subnet 5) # A1 create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" a1_config=$config a1_container_id=$(echo "$a1_config" | jq -r .container_id) a1_ip=$(echo "$a1_config" | jq -r .networks.podman1.static_ips[0]) a_gw=$(echo "$a1_config" | jq -r .network_info.podman1.subnets[0].gateway) a1_hash=$(echo "$a1_config" | jq -r .network_info.podman1.id) create_container "$a1_config" a1_pid=$CONTAINER_NS_PID # container b1 on subnet b create_config network_name="podman2" container_id=$(random_string 64) container_name="bone" subnet="$subnet_b" b1_config=$config b1_ip=$(echo "$b1_config" | jq -r .networks.podman2.static_ips[0]) b_gw=$(echo "$b1_config" | jq -r .network_info.podman2.subnets[0].gateway) b1_hash=$(echo "$b1_config" | jq -r .network_info.podman1.id) create_container "$b1_config" b1_pid=$CONTAINER_NS_PID b_subnets=$(echo $b1_config | jq -r .network_info.podman2.subnets[0]) # AB2 create_config network_name="podman1" container_id=$(random_string 64) container_name="abtwo" subnet="$subnet_a" a2_config=$config a2_ip=$(echo "$a2_config" | jq -r .networks.podman1.static_ips[0]) b2_ip=$(random_ip_in_subnet "$subnet_b") create_network "podman2" "$b2_ip" "eth1" b2_network="{$new_network}" create_network_infos "podman2" "$b1_hash" "$b_subnets" b2_network_info="{$new_network_info}" ab2_config=$(jq -r ".networks += $b2_network" <<<"$a2_config") ab2_config=$(jq -r ".network_info += $b2_network_info" <<<"$ab2_config") create_container "$ab2_config" ab2_pid=$CONTAINER_NS_PID # aone should be able to resolve AB2 and NOT B1 dig "$a1_pid" "abtwo" "$a_gw" assert "$a2_ip" dig "$a1_pid" "bone" "$a_gw" assert "" # bone should be able to resolve AB2 and NOT A1 dig "$b1_pid" "abtwo" "$b_gw" assert "$b2_ip" dig "$b1_pid" "aone" "$b_gw" assert "" # abtwo should be able to resolve A1, B1, and AB2 on both gws dig "$ab2_pid" "aone" "$a_gw" assert "$a1_ip" dig "$ab2_pid" "bone" "$b_gw" assert "$b1_ip" # check ab2 from itself, first from the a side dig "$ab2_pid" "abtwo" "$a_gw" assert "${#lines[@]}" = 2 assert "$output" =~ "$a2_ip" assert "$output" =~ "$b2_ip" # and now from the bside dig "$ab2_pid" "abtwo" "$b_gw" assert "${#lines[@]}" = 2 assert "$output" =~ "$a2_ip" assert "$output" =~ "$b2_ip" } @test "three subnets, one container on two of the subnets, network connect" { # Create all three subnets subnet_a=$(random_subnet 5) subnet_b=$(random_subnet 5) subnet_c=$(random_subnet 5) # A1 on subnet A create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" a1_config=$config a1_container_id=$(echo "$a1_config" | jq -r .container_id) a1_ip=$(echo "$a1_config" | jq -r .networks.podman1.static_ips[0]) a_gw=$(echo "$a1_config" | jq -r .network_info.podman1.subnets[0].gateway) a1_hash=$(echo "$a1_config" | jq -r .network_info.podman1.id) create_container "$a1_config" a1_pid=$CONTAINER_NS_PID # C1 on subnet C create_config network_name="podman3" container_id=$(random_string 64) container_name="cone" subnet="$subnet_c" c1_config=$config c1_container_id=$(echo "$c1_config" | jq -r .container_id) c1_ip=$(echo "$c1_config" | jq -r .networks.podman3.static_ips[0]) c_gw=$(echo "$c1_config" | jq -r .network_info.podman3.subnets[0].gateway) c1_hash=$(echo "$c1_config" | jq -r .network_info.podman3.id) create_container "$c1_config" c1_pid=$CONTAINER_NS_PID c_subnets=$(echo $c1_config | jq -r .network_info.podman3.subnets[0]) # We now have one container on A and one on C. We now similate # a network connect on both to B. # # This is also where things get tricky and we are trying to mimic # a connect. First, we need to trim off the last two container # configs for teardown. We will leave the NS_PIDS alone because # the order should be OK. # Create B1 config for network connect create_config network_name="podman2" container_id=$(random_string 64) container_name="aone" subnet="$subnet_b" aliases='"aone_nw"' b1_config=$config # The container ID should be the same b1_config=$(jq ".container_id |= \"$a1_container_id\"" <<<"$b1_config") b1_config=$(jq ".networks.podman2.interface_name |= \"eth1\"" <<<"$b1_config") b1_network=$(echo "$b1_config" | jq -r .networks) b1_network_info=$(echo "$b1_config" | jq -r .network_info) b1_ip=$(echo "$b1_network" | jq -r .podman2.static_ips[0]) b_gw=$(echo "$b1_network_info" | jq -r .podman2.subnets[0].gateway) # Now we must merge a1 and b1 for eventual teardown a1b1_config=$(jq -r ".networks += $b1_network" <<<"$a1_config") a1b1_config=$(jq -r ".network_info += $b1_network_info" <<<"$a1b1_config") # Create B2 config for network connect # create_config network_name="podman2" container_id=$(random_string 64) container_name="cone" subnet="$subnet_b" aliases='"cone_nw"' b2_config=$config # The container ID should be the same b2_config=$(jq ".container_id |= \"$c1_container_id\"" <<<"$b2_config") b2_config=$(jq ".networks.podman2.interface_name |= \"eth1\"" <<<"$b2_config") b2_network=$(echo "$b2_config" | jq -r .networks) b2_network_info=$(echo "$b2_config" | jq -r .network_info) b2_ip=$(echo "$b2_network" | jq -r .podman2.static_ips[0]) # Now we must merge c1 and b2 for eventual teardown c1b2_config=$(jq -r ".networks += $b2_network" <<<"$c1_config") c1b2_config=$(jq -r ".network_info += $b2_network_info" <<<"$c1b2_config") # Create the containers but do not add to NS_PIDS or CONTAINER_CONFIGS connect "$a1_pid" "$b1_config" connect "$c1_pid" "$b2_config" # Reset CONTAINER_CONFIGS and add the two news ones CONTAINER_CONFIGS=("$a1b1_config" "$c1b2_config") # Verify # b1 should be able to resolve cone through b subnet dig "$a1_pid" "cone" "$b_gw" assert "$b2_ip" # a1 should be able to resolve cone dig "$a1_pid" "cone" "$a_gw" assert "$b2_ip" # a1b1 should be able to resolve cone_nw alias dig "$a1_pid" "cone_nw" "$a_gw" assert "$b2_ip" # b2 should be able to resolve cone through b subnet dig "$c1_pid" "aone" "$b_gw" assert "$b1_ip" # c1 should be able to resolve aone dig "$c1_pid" "aone" "$c_gw" assert "$b1_ip" # b2c1 should be able to resolve aone_nw alias dig "$c1_pid" "aone_nw" "$c_gw" assert "$b1_ip" } @test "three subnets two ipaddress v6 and one ipaddress v4, one container on two of the subnets, network connect" { # Create all three subnets # Two of the subnets must be on ip addresss v6 and one on ip address v4 subnet_a=$(random_subnet 5) subnet_b=$(random_subnet 6) subnet_c=$(random_subnet 6) # A1 on subnet A create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" a1_config=$config a1_container_id=$(echo "$a1_config" | jq -r .container_id) a1_ip=$(echo "$a1_config" | jq -r .networks.podman1.static_ips[0]) a_gw=$(echo "$a1_config" | jq -r .network_info.podman1.subnets[0].gateway) a1_hash=$(echo "$a1_config" | jq -r .network_info.podman1.id) create_container "$a1_config" a1_pid=$CONTAINER_NS_PID # C1 on subnet C create_config network_name="podman3" container_id=$(random_string 64) container_name="cone" subnet="$subnet_c" c1_config=$config c1_container_id=$(echo "$c1_config" | jq -r .container_id) c1_ip=$(echo "$c1_config" | jq -r .networks.podman3.static_ips[0]) c_gw=$(echo "$c1_config" | jq -r .network_info.podman3.subnets[0].gateway) c1_hash=$(echo "$c1_config" | jq -r .network_info.podman3.id) create_container "$c1_config" c1_pid=$CONTAINER_NS_PID c_subnets=$(echo $c1_config | jq -r .network_info.podman3.subnets[0]) # We now have one container on A and one on C. We now similate # a network connect on both to B. # Create B1 config for network connect create_config network_name="podman2" container_id=$(random_string 64) container_name="aone" subnet="$subnet_b" aliases='"aone_nw"' b1_config=$config # The container ID should be the same b1_config=$(jq ".container_id |= \"$a1_container_id\"" <<<"$b1_config") b1_config=$(jq ".networks.podman2.interface_name |= \"eth1\"" <<<"$b1_config") b1_network=$(echo "$b1_config" | jq -r .networks) b1_network_info=$(echo "$b1_config" | jq -r .network_info) b1_ip=$(echo "$b1_network" | jq -r .podman2.static_ips[0]) b_gw=$(echo "$b1_network_info" | jq -r .podman2.subnets[0].gateway) # Now we must merge a1 and b1 for eventual teardown a1b1_config=$(jq -r ".networks += $b1_network" <<<"$a1_config") a1b1_config=$(jq -r ".network_info += $b1_network_info" <<<"$a1b1_config") # Create B2 config for network connect # create_config network_name="podman2" container_id=$(random_string 64) container_name="cone" subnet="$subnet_b" aliases='"cone_nw"' b2_config=$config # The container ID should be the same b2_config=$(jq ".container_id |= \"$c1_container_id\"" <<<"$b2_config") b2_config=$(jq ".networks.podman2.interface_name |= \"eth1\"" <<<"$b2_config") b2_network=$(echo "$b2_config" | jq -r .networks) b2_network_info=$(echo "$b2_config" | jq -r .network_info) b2_ip=$(echo "$b2_network" | jq -r .podman2.static_ips[0]) # Now we must merge c1 and b2 for eventual teardown c1b2_config=$(jq -r ".networks += $b2_network" <<<"$c1_config") c1b2_config=$(jq -r ".network_info += $b2_network_info" <<<"$c1b2_config") # Create the containers but do not add to NS_PIDS or CONTAINER_CONFIGS connect "$a1_pid" "$b1_config" connect "$c1_pid" "$b2_config" # Reset CONTAINER_CONFIGS and add the two news ones CONTAINER_CONFIGS=("$a1b1_config" "$c1b2_config") # Verify # b1 should be able to resolve cone through b subnet dig "$a1_pid" "cone" "$b_gw" "AAAA" assert "$b2_ip" # a1 should be able to resolve cone dig "$a1_pid" "cone" "$a_gw" "AAAA" assert "$b2_ip" } containers-aardvark-dns-4fd01fd/test/400-aliases.bats000066400000000000000000000017561452673546000225250ustar00rootroot00000000000000#!/usr/bin/env bats -*- bats -*- # # basic netavark tests # load helpers @test "two containers on the same network with aliases" { # container a1 subnet_a=$(random_subnet 5) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" aliases='"a1", "1a"' config_a1="$config" a1_ip=$(echo "$config_a1" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$config_a1" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$config_a1" a1_pid=$CONTAINER_NS_PID # container a2 create_config network_name="podman1" container_id=$(random_string 64) container_name="atwo" subnet="$subnet_a" aliases='"a2", "2a"' config_a2="$config" a2_ip=$(echo "$config_a2" | jq -r .networks.podman1.static_ips[0]) create_container "$config_a2" a2_pid="$CONTAINER_NS_PID" dig "$a1_pid" "a2" "$gw" assert "$a2_ip" dig "$a1_pid" "2a" "$gw" assert "$a2_ip" dig "$a2_pid" "a1" "$gw" assert "$a1_ip" dig "$a2_pid" "1a" "$gw" assert "$a1_ip" } containers-aardvark-dns-4fd01fd/test/500-reverse-lookups.bats000066400000000000000000000041761452673546000242510ustar00rootroot00000000000000#!/usr/bin/env bats -*- bats -*- # # basic netavark tests # load helpers @test "check reverse lookups" { # container a1 subnet_a=$(random_subnet 5) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" aliases='"a1", "1a"' a1_config="$config" a1_ip=$(echo "$a1_config" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$a1_config" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$a1_config" a1_pid=$CONTAINER_NS_PID # container a2 create_config network_name="podman1" container_id=$(random_string 64) container_name="atwo" subnet="$subnet_a" aliases='"a2", "2a"' a2_config="$config" a2_ip=$(echo "$a2_config" | jq -r .networks.podman1.static_ips[0]) create_container "$a2_config" a2_pid="$CONTAINER_NS_PID" echo "$a1_config" echo "$a2_config" # Resolve IPs to container names dig_reverse "$a1_pid" "$a2_ip" "$gw" assert "$output" =~ "atwo" assert "$output" =~ "a2" assert "$output" =~ "2a" dig_reverse "$a2_pid" "$a1_ip" "$gw" assert "$output" =~ "aone" assert "$output" =~ "a1" assert "$output" =~ "1a" } @test "check reverse lookups on ipaddress v6" { # container a1 subnet_a=$(random_subnet 6) create_config network_name="podman1" container_id=$(random_string 64) container_name="aone" subnet="$subnet_a" aliases='"a1", "1a"' a1_config="$config" a1_ip=$(echo "$a1_config" | jq -r .networks.podman1.static_ips[0]) gw=$(echo "$a1_config" | jq -r .network_info.podman1.subnets[0].gateway) create_container "$a1_config" a1_pid=$CONTAINER_NS_PID # container a2 create_config network_name="podman1" container_id=$(random_string 64) container_name="atwo" subnet="$subnet_a" aliases='"a2", "2a"' a2_config="$config" a2_ip=$(echo "$a2_config" | jq -r .networks.podman1.static_ips[0]) create_container "$a2_config" a2_pid="$CONTAINER_NS_PID" echo "$a1_config" echo "$a2_config" # Resolve IPs to container names dig_reverse "$a1_pid" "$a2_ip" "$gw" assert "$output" =~ "atwo" assert "$output" =~ "a2" assert "$output" =~ "2a" dig_reverse "$a2_pid" "$a1_ip" "$gw" assert "$output" =~ "aone" assert "$output" =~ "a1" assert "$output" =~ "1a" } containers-aardvark-dns-4fd01fd/test/helpers.bash000066400000000000000000000415531452673546000222300ustar00rootroot00000000000000# -*- bash -*- # Netavark binary to run NETAVARK=${NETAVARK:-/usr/libexec/podman/netavark} TESTSDIR=${TESTSDIR:-$(dirname ${BASH_SOURCE})} AARDVARK=${AARDVARK:-$TESTSDIR/../bin/aardvark-dns} # export RUST_BACKTRACE so that we get a helpful stack trace export RUST_BACKTRACE=full HOST_NS_PID= CONTAINER_NS_PID= CONTAINER_CONFIGS=() CONTAINER_NS_PIDS=() #### Functions below are taken from podman and buildah and adapted to netavark. ################ # run_helper # Invoke args, with timeout, using BATS 'run' ################ # # Second, we use 'timeout' to abort (with a diagnostic) if something # takes too long; this is preferable to a CI hang. # # Third, we log the command run and its output. This doesn't normally # appear in BATS output, but it will if there's an error. # # Next, we check exit status. Since the normal desired code is 0, # that's the default; but the expected_rc var can override: # # expected_rc=125 run_helper nonexistent-subcommand # expected_rc=? run_helper some-other-command # let our caller check status # # Since we use the BATS 'run' mechanism, $output and $status will be # defined for our caller. # function run_helper() { # expected_rc if unset set default to 0 expected_rc="${expected_rc-0}" if [ "$expected_rc" == "?" ]; then expected_rc= fi # Remember command args, for possible use in later diagnostic messages MOST_RECENT_COMMAND="$*" # stdout is only emitted upon error; this echo is to help a debugger echo "$_LOG_PROMPT $*" # BATS hangs if a subprocess remains and keeps FD 3 open; this happens # if a process crashes unexpectedly without cleaning up subprocesses. run timeout --foreground -v --kill=10 10 "$@" 3>&- # without "quotes", multiple lines are glommed together into one if [ -n "$output" ]; then echo "$output" fi if [ "$status" -ne 0 ]; then echo -n "[ rc=$status " if [ -n "$expected_rc" ]; then if [ "$status" -eq "$expected_rc" ]; then echo -n "(expected) " else echo -n "(** EXPECTED $expected_rc **) " fi fi echo "]" fi if [ "$status" -eq 124 ]; then if expr "$output" : ".*timeout: sending" >/dev/null; then # It's possible for a subtest to _want_ a timeout if [[ "$expected_rc" != "124" ]]; then echo "*** TIMED OUT ***" false fi fi fi if [ -n "$expected_rc" ]; then if [ "$status" -ne "$expected_rc" ]; then die "exit code is $status; expected $expected_rc" fi fi # unset unset expected_rc } ######### # die # Abort with helpful message ######### function die() { # FIXME: handle multi-line output echo "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" >&2 echo "#| FAIL: $*" >&2 echo "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" >&2 false } ############ # assert # Compare actual vs expected string; fail if mismatch ############ # # Compares string (default: $output) against the given string argument. # By default we do an exact-match comparison against $output, but there # are two different ways to invoke us, each with an optional description: # # xpect "EXPECT" [DESCRIPTION] # xpect "RESULT" "OP" "EXPECT" [DESCRIPTION] # # The first form (one or two arguments) does an exact-match comparison # of "$output" against "EXPECT". The second (three or four args) compares # the first parameter against EXPECT, using the given OPerator. If present, # DESCRIPTION will be displayed on test failure. # # Examples: # # xpect "this is exactly what we expect" # xpect "${lines[0]}" =~ "^abc" "first line begins with abc" # function assert() { local actual_string="$output" local operator='==' local expect_string="$1" local testname="$2" case "${#*}" in 0) die "Internal error: 'assert' requires one or more arguments" ;; 1 | 2) ;; 3 | 4) actual_string="$1" operator="$2" expect_string="$3" testname="$4" ;; *) die "Internal error: too many arguments to 'assert'" ;; esac # Comparisons. # Special case: there is no !~ operator, so fake it via '! x =~ y' local not= local actual_op="$operator" if [[ $operator == '!~' ]]; then not='!' actual_op='=~' fi if [[ $operator == '=' || $operator == '==' ]]; then # Special case: we can't use '=' or '==' inside [[ ... ]] because # the right-hand side is treated as a pattern... and '[xy]' will # not compare literally. There seems to be no way to turn that off. if [ "$actual_string" = "$expect_string" ]; then return fi elif [[ $operator == '!=' ]]; then # Same special case as above if [ "$actual_string" != "$expect_string" ]; then return fi else if eval "[[ $not \$actual_string $actual_op \$expect_string ]]"; then return elif [ $? -gt 1 ]; then die "Internal error: could not process 'actual' $operator 'expect'" fi fi # Test has failed. Get a descriptive test name. if [ -z "$testname" ]; then testname="${MOST_RECENT_BUILDAH_COMMAND:-[no test name given]}" fi # Display optimization: the typical case for 'expect' is an # exact match ('='), but there are also '=~' or '!~' or '-ge' # and the like. Omit the '=' but show the others; and always # align subsequent output lines for ease of comparison. local op='' local ws='' if [ "$operator" != '==' ]; then op="$operator " ws=$(printf "%*s" ${#op} "") fi # This is a multi-line message, which may in turn contain multi-line # output, so let's format it ourself, readably local actual_split IFS=$'\n' read -rd '' -a actual_split <<<"$actual_string" || true printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2 printf "#| FAIL: %s\n" "$testname" >&2 printf "#| expected: %s'%s'\n" "$op" "$expect_string" >&2 printf "#| actual: %s'%s'\n" "$ws" "${actual_split[0]}" >&2 local line for line in "${actual_split[@]:1}"; do printf "#| > %s'%s'\n" "$ws" "$line" >&2 done printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" >&2 false } ################# # assert_json # Compare actual json vs expected string; fail if mismatch ################# # assert_json works like assert except that it accepts one extra parameter, # the jq query string. # There are two different ways to invoke us, each with an optional description: # # xpect "JQ_QUERY" "EXPECT" [DESCRIPTION] # xpect "JSON_STRING" "JQ_QUERY" "OP" "EXPECT" [DESCRIPTION] # Important this function will overwrite $output, so if you need to use the value # more than once you need to safe it in another variable. function assert_json() { local actual_json="$output" local operator='==' local jq_query="$1" local expect_string="$2" local testname="$3" case "${#*}" in 0 | 1) die "Internal error: 'assert_json' requires two or more arguments" ;; 2 | 3) ;; 4 | 5) actual_json="$1" jq_query="$2" operator="$3" expect_string="$4" testname="$5" ;; *) die "Internal error: too many arguments to 'assert_json'" ;; esac run_helper jq -r "$jq_query" <<<"$actual_json" assert "$output" "$operator" "$expect_string" "$testname" } ################### # random_string # Pseudorandom alphanumeric string of given length ################### function random_string() { local length=${1:-10} head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length } ################### # random_subnet # generate a random private subnet ################### # # by default it will return a 10.x.x.0/24 ipv4 subnet # if "6" is given as first argument it will return a "fdx:x:x:x::/64" ipv6 subnet function random_subnet() { if [[ "$1" == "6" ]]; then printf "fd%x:%x:%x:%x::/64" $((RANDOM % 256)) $((RANDOM % 65535)) $((RANDOM % 65535)) $((RANDOM % 65535)) else printf "10.%d.%d.0/24" $((RANDOM % 256)) $((RANDOM % 256)) fi } ######################### # random_ip_in_subnet # get a random from a given subnet ######################### # the first arg must be an subnet created by random_subnet # otherwise this function might return an invalid ip function random_ip_in_subnet() { # first trim subnet local net_ip=${1%/*} local num= # if ip has colon it is ipv6 if [[ "$net_ip" == *":"* ]]; then # make sure to not get 0 or 1 num=$(printf "%x" $((RANDOM % 65533 + 2))) else # if ipv4 we have to trim the final 0 net_ip=${net_ip%0} # make sure to not get 0, 1 or 255 num=$(printf "%d" $((RANDOM % 252 + 2))) fi printf "$net_ip%s" $num } ######################### # gateway_from_subnet # get the first ip from a given subnet ######################### # the first arg must be an subnet created by random_subnet # otherwise this function might return an invalid ip function gateway_from_subnet() { # first trim subnet local net_ip=${1%/*} # set first ip in network as gateway local num=1 # if ip has dor it is ipv4 if [[ "$net_ip" == *"."* ]]; then # if ipv4 we have to trim the final 0 net_ip=${net_ip%0} fi printf "$net_ip%s" $num } function create_netns() { # create a new netns and mountns and run a sleep process to keep it alive # we have to redirect stdout/err to /dev/null otherwise bats will hang unshare -mn sleep inf &>/dev/null & pid=$! # we have to wait for unshare and check that we have a new ns before returning local timeout=2 while [[ $timeout -gt 0 ]]; do if [ "$(readlink /proc/self/ns/net)" != "$(readlink /proc/$pid/ns/net)" ]; then echo $pid return fi sleep 1 let timeout=$timeout-1 done die "Timed out waiting for unshare new netns" } function get_container_netns_path() { echo /proc/$1/ns/net } ################ # run_netavark # Invoke $NETAVARK, with timeout, using BATS 'run' ################ # # This is the preferred mechanism for invoking netavark: first, it # it joins the test network namespace before it invokes $NETAVARK, # which may be 'netavark' or '/some/path/netavark'. function run_netavark() { run_in_host_netns $NETAVARK "--config" "$AARDVARK_TMPDIR" "-a" "$AARDVARK" "$@" } ################ # run_in_container_netns # Run args in container netns ################ # # first arg must be the container pid function run_in_container_netns() { con_pid=$1 shift run_helper nsenter -n -t $con_pid "$@" } ################ # run_in_host_netns # Run args in host netns ################ # function run_in_host_netns() { run_helper nsenter -m -n -t $HOST_NS_PID "$@" } ################ # create_config# Creates a config netavark can use ################ # # The following arguments are supported, the order does not matter: # network_name=$network_name # container_id=$container_id # container_name=$container_name # subnet=$subnet specifies the network subnet # custom_dns_serve=$custom_dns_server # aliases=$aliases comma seperated container aliases for dns resolution. function create_config() { local network_name="" local container_id="" local container_name="" local subnet="" local custom_dns_server local aliases="" # parse arguments while [[ "$#" -gt 0 ]]; do IFS='=' read -r arg value <<<"$1" case "$arg" in network_name) network_name="$value" ;; container_id) container_id="$value" ;; container_name) container_name="$value" ;; subnet) subnet="$value" ;; custom_dns_server) custom_dns_server="$value" ;; aliases) aliases="$value" ;; *) die "unknown argument for '$arg' create_config" ;; esac shift done container_ip=$(random_ip_in_subnet $subnet) container_gw=$(gateway_from_subnet $subnet) subnets="{\"subnet\":\"$subnet\",\"gateway\":\"$container_gw\"}" create_network "$network_name" "$container_ip" "eth0" "$aliases" create_network_infos "$network_name" $(random_string 64) "$subnets" read -r -d '\0' config <"$AARDVARK_TMPDIR/slirp4.log" & SLIRP4NETNS_PID=$! # create new resolv.conf with slirp4netns dns echo "nameserver 10.0.2.3" >"$AARDVARK_TMPDIR/resolv.conf" run_in_host_netns mount --bind "$AARDVARK_TMPDIR/resolv.conf" /etc/resolv.conf local timeout=6 while [[ $timeout -gt 1 ]]; do run_in_host_netns ip addr if [[ "$output" =~ "tap0" ]]; then return fi sleep 1 let timeout=$timeout-1 done cat "$AARDVARK_TMPDIR/slirp4.log" die "Timed out waiting for slirp4netns to start" } function basic_teardown() { rm -fr "$AARDVARK_TMPDIR" } ################ # netavark_teardown# tears down a network ################ function netavark_teardown() { run_netavark teardown $1 <<<"$2" } function teardown() { # Now call netavark with all the configs and then kill the netns associated with it for i in "${!CONTAINER_CONFIGS[@]}"; do netavark_teardown $(get_container_netns_path "${CONTAINER_NS_PIDS[$i]}") "${CONTAINER_CONFIGS[$i]}" kill -9 "${CONTAINER_NS_PIDS[$i]}" done if [[ -n "$SLIRP4NETNS_PID" ]]; then kill -9 $SLIRP4NETNS_PID SLIRP4NETNS_PID="" fi # Finally kill the host netns if [ ! -z "$HOST_NS_PID" ]; then echo "$HOST_NS_PID" kill -9 "$HOST_NS_PID" fi basic_teardown } function dig() { # first arg is container_netns_pid # second arg is name # third arg is server addr run_in_container_netns "$1" "dig" "+short" "$2" "@$3" $4 } function dig_reverse() { # first arg is container_netns_pid # second arg is the IP address # third arg is server addr #run_in_container_netns "$1" "dig" "-x" "$2" "+short" "@$3" run_in_container_netns "$1" "nslookup" "$2" "$3" } function setup() { basic_host_setup }