From 3beee1cf84f95b2eac66db0a822eca1b2ce0aed4 Mon Sep 17 00:00:00 2001 From: Matteo Settenvini Date: Fri, 5 Jan 2024 17:55:52 +0100 Subject: [PATCH 1/2] dns: add dbus interfaces --- .../com.endlessm.ParentalControls.Dns.xml | 41 +++++++++++++++++++ .../com.endlessm.ParentalControls.policy.in | 40 ++++++++++++++++++ .../com.endlessm.ParentalControls.rules.in | 2 + accounts-service/meson.build | 1 + 4 files changed, 84 insertions(+) create mode 100644 accounts-service/com.endlessm.ParentalControls.Dns.xml diff --git a/accounts-service/com.endlessm.ParentalControls.Dns.xml b/accounts-service/com.endlessm.ParentalControls.Dns.xml new file mode 100644 index 0000000..fe5967c --- /dev/null +++ b/accounts-service/com.endlessm.ParentalControls.Dns.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + diff --git a/accounts-service/com.endlessm.ParentalControls.policy.in b/accounts-service/com.endlessm.ParentalControls.policy.in index ceae227..295f301 100644 --- a/accounts-service/com.endlessm.ParentalControls.policy.in +++ b/accounts-service/com.endlessm.ParentalControls.policy.in @@ -40,6 +40,46 @@ + + Change your own DNS servers + Authentication is required to change your DNS servers. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + Read your own DNS servers + Authentication is required to read your DNS servers. + + yes + yes + yes + + + + + Change another user’s DNS servers + Authentication is required to change another user’s DNS servers. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + Read another user’s DNS servers + Authentication is required to read another user’s DNS servers. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + Change your own session limits Authentication is required to change your session limits. diff --git a/accounts-service/com.endlessm.ParentalControls.rules.in b/accounts-service/com.endlessm.ParentalControls.rules.in index fa021fa..be6a3b6 100644 --- a/accounts-service/com.endlessm.ParentalControls.rules.in +++ b/accounts-service/com.endlessm.ParentalControls.rules.in @@ -24,6 +24,8 @@ polkit.addRule(function(action, subject) { * needing an additional polkit authorisation dialogue. */ if ((action.id == "com.endlessm.ParentalControls.AppFilter.ReadOwn" || action.id == "com.endlessm.ParentalControls.AppFilter.ReadAny" || + action.id == "com.endlessm.ParentalControls.Dns.ReadOwn" || + action.id == "com.endlessm.ParentalControls.Dns.ReadAny" || action.id == "com.endlessm.ParentalControls.SessionLimits.ReadOwn" || action.id == "com.endlessm.ParentalControls.SessionLimits.ReadAny") && subject.active && subject.local && diff --git a/accounts-service/meson.build b/accounts-service/meson.build index 198692c..967cfff 100644 --- a/accounts-service/meson.build +++ b/accounts-service/meson.build @@ -9,6 +9,7 @@ i18n.merge_file( dbus_interfaces = [ 'com.endlessm.ParentalControls.AccountInfo', 'com.endlessm.ParentalControls.AppFilter', + 'com.endlessm.ParentalControls.Dns', 'com.endlessm.ParentalControls.SessionLimits', ] -- 2.39.5 From 03fbdb2d267cdf3a6cc2dd9ac82d6ea4f0855eee Mon Sep 17 00:00:00 2001 From: Matteo Settenvini Date: Sat, 6 Jan 2024 10:29:09 +0100 Subject: [PATCH 2/2] dns: NSS module implementation --- meson.build | 6 +- meson_options.txt | 6 + nss/.gitignore | 10 + nss/.vscode/launch.json | 30 + nss/Cargo.lock | 2351 ++++++++++++++++++++++++++++++++ nss/Cargo.lock.license | 2 + nss/Cargo.toml | 54 + nss/README.md | 15 + nss/README.md.license | 2 + nss/build.rs | 58 + nss/meson.build | 59 + nss/src/gethostbyaddr.rs | 33 + nss/src/gethostbyname.rs | 337 +++++ nss/src/helpers.rs | 81 ++ nss/src/lib.rs | 46 + nss/src/nss_api.rs | 193 +++ nss/src/nss_bindings.rs | 5 + nss/src/policy_checker/dbus.rs | 66 + nss/src/policy_checker/mod.rs | 115 ++ nss/tests/common/dbus.rs | 142 ++ nss/tests/common/mod.rs | 148 ++ nss/tests/integration_test.rs | 243 ++++ nss/wrapper.hpp | 43 + 23 files changed, 4044 insertions(+), 1 deletion(-) create mode 100644 nss/.gitignore create mode 100644 nss/.vscode/launch.json create mode 100644 nss/Cargo.lock create mode 100644 nss/Cargo.lock.license create mode 100644 nss/Cargo.toml create mode 100644 nss/README.md create mode 100644 nss/README.md.license create mode 100644 nss/build.rs create mode 100644 nss/meson.build create mode 100644 nss/src/gethostbyaddr.rs create mode 100644 nss/src/gethostbyname.rs create mode 100644 nss/src/helpers.rs create mode 100644 nss/src/lib.rs create mode 100644 nss/src/nss_api.rs create mode 100644 nss/src/nss_bindings.rs create mode 100644 nss/src/policy_checker/dbus.rs create mode 100644 nss/src/policy_checker/mod.rs create mode 100644 nss/tests/common/dbus.rs create mode 100644 nss/tests/common/mod.rs create mode 100644 nss/tests/integration_test.rs create mode 100644 nss/wrapper.hpp diff --git a/meson.build b/meson.build index 4f0cabd..915440d 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('malcontent', 'c', version : '0.12.0', - meson_version : '>= 0.59.0', + meson_version : '>= 0.60.0', license: ['LGPL-2.1-or-later', 'GPL-2.0-or-later'], default_options : [ 'buildtype=debugoptimized', @@ -154,3 +154,7 @@ if get_option('ui').enabled() endif subdir('pam') subdir('po') + +if get_option('dns').enabled() + subdir('nss') +endif diff --git a/meson_options.txt b/meson_options.txt index 726cac1..5645301 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -9,6 +9,12 @@ option( type: 'string', description: 'directory for PAM modules' ) +option( + 'dns', + type: 'feature', + value: 'enabled', + description: 'enable NSS module support (for parental DNS controls)' +) option( 'ui', type: 'feature', diff --git a/nss/.gitignore b/nss/.gitignore new file mode 100644 index 0000000..84b5e03 --- /dev/null +++ b/nss/.gitignore @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: Matteo Settenvini +# SPDX-License-Identifier: GPL-3.0-or-later + +*.orig +*~ + +.gdbinit +.lldbinit + +/target diff --git a/nss/.vscode/launch.json b/nss/.vscode/launch.json new file mode 100644 index 0000000..54fd4b2 --- /dev/null +++ b/nss/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Cargo integration_test", + "cargo": { + "args": [ + "test", + "--no-run", + "--test=integration_test" + ] + }, + "args": [], + "cwd": "${workspaceFolder}", + "env": { + "RUST_BACKTRACE": "full", + "NSS_MALCONTENT_LOG": "trace", + }, + "preRunCommands": [ + "settings set target.process.follow-fork-mode child", + "settings set target.process.stop-on-exec false" + ] + }, + ] +} diff --git a/nss/Cargo.lock b/nss/Cargo.lock new file mode 100644 index 0000000..26aaf4f --- /dev/null +++ b/nss/Cargo.lock @@ -0,0 +1,2351 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[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 = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "async-broadcast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.3", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite 1.13.0", + "log", + "parking", + "polling 2.8.0", + "rustix 0.37.27", + "slab", + "socket2 0.4.10", + "waker-fn", +] + +[[package]] +name = "async-io" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +dependencies = [ + "async-lock 3.2.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite 2.2.0", + "parking", + "polling 3.3.1", + "rustix 0.38.28", + "slab", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +dependencies = [ + "async-io 1.13.0", + "async-lock 2.8.0", + "async-signal", + "blocking", + "cfg-if", + "event-listener 3.1.0", + "futures-lite 1.13.0", + "rustix 0.38.28", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "async-signal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +dependencies = [ + "async-io 2.2.2", + "async-lock 2.8.0", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.28", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[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 = "bindgen" +version = "0.69.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.48", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel", + "async-lock 3.2.0", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite 2.2.0", + "piper", + "tracing", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cargo_metadata" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46e3374c604fb39d1a2f35ed5e4a4e30e60d01fab49446e08f1b3e9a90aef202" +dependencies = [ + "semver", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + +[[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 2.0.48", +] + +[[package]] +name = "enumflags2" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "env_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[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 = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[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" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[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 = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[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 0.4.0", + "ipnet", + "native-tls", + "once_cell", + "rand", + "thiserror", + "tinyvec", + "tokio", + "tokio-native-tls", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b8f021164e6a984c9030023544c57789c51760065cd510572fedcfb04164e8" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot 0.12.1", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tokio-native-tls", + "tracing", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[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 = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[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 = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.5", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +dependencies = [ + "hermit-abi", + "rustix 0.38.28", + "windows-sys 0.52.0", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "ld_preload" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2b67681617e70bf9f7ddd86cfd83fdbb25a84bbae87a1e8c436c30855243f2" +dependencies = [ + "libc", +] + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "malcontent-nss" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindgen", + "elf", + "env_logger", + "event-listener 4.0.3", + "futures-util", + "gethostname", + "hickory-proto", + "hickory-resolver", + "ld_preload", + "libc", + "log", + "malcontent-nss", + "nix 0.27.1", + "once_cell", + "rusty-fork", + "serde", + "static_init", + "syslog", + "test-cdylib", + "tokio", + "tokio-util", + "zbus", + "zbus_names", + "zvariant", +] + +[[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.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[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.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", + "memoffset 0.9.0", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[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 = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "polling" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix 0.38.28", + "tracing", + "windows-sys 0.52.0", +] + +[[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 = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.48", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +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.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[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 = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.37.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[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.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "static_init" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" +dependencies = [ + "bitflags 1.3.2", + "cfg_aliases", + "libc", + "parking_lot 0.11.2", + "parking_lot_core 0.8.6", + "static_init_macro", + "winapi", +] + +[[package]] +name = "static_init_macro" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" +dependencies = [ + "cfg_aliases", + "memchr", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +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 = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand 2.0.1", + "redox_syscall 0.4.1", + "rustix 0.38.28", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "test-cdylib" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8f41b1f729f5ff5177beab62e5a9251e318df8386e260ab3c944cff502ee78d" +dependencies = [ + "cargo_metadata", + "serde", + "serde_json", + "toml", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +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.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +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.35.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.5", + "tokio-macros", + "tracing", + "windows-sys 0.48.0", +] + +[[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 2.0.48", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[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 = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[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 2.0.48", +] + +[[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 = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset 0.9.0", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna 0.5.0", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.28", +] + +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + +[[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-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[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-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[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_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[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_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[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_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[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_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "xdg-home" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +dependencies = [ + "nix 0.26.4", + "winapi", +] + +[[package]] +name = "zbus" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +dependencies = [ + "async-broadcast", + "async-process", + "async-recursion", + "async-trait", + "byteorder", + "derivative", + "enumflags2", + "event-listener 2.5.3", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix 0.26.4", + "once_cell", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "winapi", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "3.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zvariant" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +dependencies = [ + "byteorder", + "enumflags2", + "libc", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/nss/Cargo.lock.license b/nss/Cargo.lock.license new file mode 100644 index 0000000..4109904 --- /dev/null +++ b/nss/Cargo.lock.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Matteo Settenvini +SPDX-License-Identifier: CC0-1.0 diff --git a/nss/Cargo.toml b/nss/Cargo.toml new file mode 100644 index 0000000..e5cb8bf --- /dev/null +++ b/nss/Cargo.toml @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2022 Matteo Settenvini +# SPDX-License-Identifier: CC0-1.0 + +[package] +name = "malcontent-nss" +version = "0.1.0" +edition = "2021" +authors = ["Matteo Settenvini +SPDX-License-Identifier: CC-BY-SA-4.0 diff --git a/nss/build.rs b/nss/build.rs new file mode 100644 index 0000000..4ac7106 --- /dev/null +++ b/nss/build.rs @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +use {std::env, std::io::Error, std::io::ErrorKind, std::path::PathBuf}; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=wrapper.hpp"); + + // Required by NSS 2 + println!("cargo:rustc-cdylib-link-arg=-Wl,-soname,libnss_malcontent.so.2"); + + // Pass down the path to the output folder to the integration test binary + let cdylib_dir = get_cargo_target_dir().unwrap(); + println!("cargo:rustc-env=CDYLIB_OUT_DIR={}", cdylib_dir.display()); + // Set the DT_RPATH of the test binary to search in its own folder first + println!("cargo:rustc-link-arg-tests=-Wl,--disable-new-dtags"); + println!("cargo:rustc-link-arg-tests=-Wl,--rpath=$ORIGIN"); + + let bindings = bindgen::Builder::default() + .header("wrapper.hpp") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + .newtype_enum("nss_status") + .allowlist_type("nss_status") + .newtype_enum("HErrno") + .allowlist_type("HErrno") + .newtype_enum("EaiRetcode") + .allowlist_type("EaiRetcode") + .allowlist_type("gaih_addrtuple") + .allowlist_function("__nss_configure_lookup") + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn get_cargo_target_dir() -> Result> { + let out_dir = PathBuf::from(env::var("OUT_DIR")?); + let profile = std::env::var("PROFILE")?; + + let mut path = out_dir.as_path(); + loop { + if let Some(parent) = path.parent() { + path = parent; + if parent.ends_with(&profile) { + return Ok(path.to_path_buf()); + } + } else { + return Err(Box::new(Error::new( + ErrorKind::NotFound, + "Unable to determine cargo build target directory", + ))); + } + } +} diff --git a/nss/meson.build b/nss/meson.build new file mode 100644 index 0000000..428bde0 --- /dev/null +++ b/nss/meson.build @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: Matteo Settenvini +# SPDX-License-Identifier: GPL-3.0-or-later + +if get_option('buildtype') == 'debug' + profile = 'dev' + target_subdir = 'debug' +else + profile = 'release' + target_subdir = 'release' +endif + +dbus_iface_file = meson.project_source_root() / 'accounts-service/com.endlessm.ParentalControls.Dns.xml' +env = environment({ + 'DBUS_IFACE': dbus_iface_file, +}) + +cargo = find_program('cargo') +cargo_common_opts = [ + '--manifest-path', meson.current_source_dir() / 'Cargo.toml', + '--workspace', + '--profile', profile, + '--target-dir', meson.current_build_dir() / 'target', +] + +library = 'libnss_malcontent.so' + +build_library = custom_target('cargo-nss_malcontent', + output: 'target' / target_subdir / library, + build_always_stale: true, + command: [cargo, 'build'] + cargo_common_opts, + console: true, + env: env, +) + +custom_target('libnss_malcontent', + input: build_library, + output: library + '.2', # Required by NSS 3 + build_by_default: true, + command: ['ln', '-f', '@INPUT@', '@OUTPUT@'], + env: env, + install: true, + install_dir: get_option('libdir'), +) + +build_tests = custom_target('nss_malcontent-tests', + output: 'something-something-something', + build_always_stale: true, + build_by_default: true, + command: [cargo, 'test'] + cargo_common_opts + '--no-run', + console: true, + env: env +) + +test('dns', cargo, + args: ['test'] + cargo_common_opts + ['--no-fail-fast', '--message-format', 'json'], + env: env, + depends: [build_tests], + protocol: 'rust', +) diff --git a/nss/src/gethostbyaddr.rs b/nss/src/gethostbyaddr.rs new file mode 100644 index 0000000..72914ef --- /dev/null +++ b/nss/src/gethostbyaddr.rs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +use { + crate::nss_bindings::{nss_status, HErrno}, + libc::{hostent, size_t, socklen_t}, + nix::errno::Errno, + std::os::raw::{c_char, c_int, c_void}, +}; + +pub async unsafe fn with( + _addr: *const c_void, + _len: socklen_t, + _af: c_int, + _host: *mut hostent, + _buffer: *mut c_char, + _buflen: size_t, + _errnop: *mut Errno, + _h_errnop: *mut HErrno, + _ttlp: *mut i32, +) -> nss_status { + // At the moment, we are not handling this function + // in our module. + // + // We assume it is fine to go to the next module + // in the nsswitch.conf list to get an authoritative + // answer. + // + // The use case for reverse IP lookup + // should not impact parental controls. + + nss_status::NSS_STATUS_UNAVAIL +} diff --git a/nss/src/gethostbyname.rs b/nss/src/gethostbyname.rs new file mode 100644 index 0000000..1770c1b --- /dev/null +++ b/nss/src/gethostbyname.rs @@ -0,0 +1,337 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +use hickory_resolver::error::ResolveErrorKind; + +use { + crate::helpers::{set_if_valid, write_record_name_to_buf, write_vector_to_buf}, + crate::nss_bindings::{gaih_addrtuple, nss_status, HErrno}, + crate::policy_checker::POLICY_CHECKER, + hickory_proto::rr::record_type::RecordType, + hickory_proto::rr::{RData, Record}, + hickory_resolver::TokioAsyncResolver, + hickory_resolver::{lookup::Lookup, lookup_ip::LookupIp}, + libc::{c_char, c_int, hostent, size_t, AF_INET, AF_INET6}, + nix::errno, + nix::errno::Errno, + std::ffi::CStr, + std::mem::{align_of, discriminant, size_of}, + std::sync::Arc, +}; + +// See https://support.mozilla.org/en-US/kb/configuring-networks-disable-dns-over-https +const CANARY_HOSTNAME: &str = "use-application-dns.net"; + +#[derive(Debug)] +pub struct Args { + pub name: *const c_char, + pub family: c_int, + pub buffer: *mut c_char, + pub buflen: size_t, + pub errnop: *mut Errno, + pub h_errnop: *mut HErrno, + pub ttlp: *mut i32, + pub result: Result, +} + +#[derive(Debug)] +pub enum Result { + V3(*mut hostent), + V4(*mut *mut gaih_addrtuple), +} + +pub enum DnsResult { + NxDomain, + Found, +} + +pub async unsafe fn with(args: &mut Args) -> nss_status { + set_if_valid(args.errnop, errno::from_i32(0)); + set_if_valid(args.h_errnop, HErrno::Success); + + match POLICY_CHECKER.resolver().await { + Ok(None) => { + // no restrictions for user, the next NSS module will decide + nss_status::NSS_STATUS_NOTFOUND + } + Ok(Some(resolver)) => resolve_hostname_through(resolver, args).await, + Err(err) => { + log::error!("{}", err); + nss_status::NSS_STATUS_UNAVAIL + } + } +} + +async unsafe fn resolve_hostname_through( + resolver: Arc, + args: &mut Args, +) -> nss_status { + let name = match CStr::from_ptr(args.name).to_str() { + Ok(name) => name, + Err(_) => { + set_if_valid(args.errnop, Errno::EINVAL); + set_if_valid(args.h_errnop, HErrno::Internal); + return nss_status::NSS_STATUS_TRYAGAIN; + } + }; + + // Disable DoH in Firefox unless user forced it manually + if name == CANARY_HOSTNAME { + set_if_valid(args.h_errnop, HErrno::HostNotFound); + return nss_status::NSS_STATUS_SUCCESS; + } + + let lookup: std::result::Result = match args.family { + libc::AF_UNSPEC => resolver.lookup_ip(name).await.map(LookupIp::into), + libc::AF_INET => resolver.lookup(name, RecordType::A).await, + libc::AF_INET6 => resolver.lookup(name, RecordType::AAAA).await, + _ => return nss_status::NSS_STATUS_NOTFOUND, + }; + + match lookup { + Ok(result) => { + log::trace!("lookup: {} => {:?}", name, result); + prepare_response(args, result) + } + Err(err) => match err.kind() { + ResolveErrorKind::NoRecordsFound { + query: _, + soa: _, + negative_ttl: _, + response_code: _, + trusted: _, + } => { + log::trace!("lookup: {} => NXDOMAIN", name); + set_if_valid(args.h_errnop, HErrno::HostNotFound); + nss_status::NSS_STATUS_SUCCESS + } + _ => { + log::warn!("{}", err); + nss_status::NSS_STATUS_UNAVAIL + } + }, + } +} + +unsafe fn prepare_response( + args: &mut Args, + lookup: hickory_resolver::lookup::Lookup, +) -> nss_status { + if lookup.iter().peekable().peek().is_none() { + set_if_valid(args.h_errnop, HErrno::HostNotFound); + return nss_status::NSS_STATUS_SUCCESS; + } + + let ttl = lookup + .valid_until() + .duration_since(std::time::Instant::now()) + .as_secs(); + set_if_valid( + args.ttlp, + if ttl < (i32::MAX as u64) { + ttl as i32 + } else { + i32::MAX + }, + ); + + let buf = std::slice::from_raw_parts_mut(args.buffer as *mut u8, args.buflen); + let ret = match &mut args.result { + Result::V3(hostent) => { + if hostent.is_null() { + set_if_valid(args.errnop, Errno::EINVAL); + set_if_valid(args.h_errnop, HErrno::Internal); + return nss_status::NSS_STATUS_TRYAGAIN; + } + + match records_to_hostent(lookup.into(), &mut **hostent, buf) { + Ok(DnsResult::Found) => nss_status::NSS_STATUS_SUCCESS, + Ok(DnsResult::NxDomain) => { + set_if_valid(args.h_errnop, HErrno::HostNotFound); + nss_status::NSS_STATUS_SUCCESS + } + Err(err) => { + set_if_valid( + args.errnop, + err.raw_os_error() + .map(Errno::from_i32) + .unwrap_or(Errno::EAGAIN), + ); + set_if_valid(args.h_errnop, HErrno::Internal); + nss_status::NSS_STATUS_TRYAGAIN + } + } + } + Result::V4(pat) => { + if pat.is_null() { + set_if_valid(args.errnop, Errno::EINVAL); + set_if_valid(args.h_errnop, HErrno::Internal); + return nss_status::NSS_STATUS_TRYAGAIN; + } + + match records_to_gaih_addr(lookup.into(), buf) { + Ok(addrs) => { + // DEBUG: eprintln!("{:?} => {:?}", addrs, *addrs); + **pat = addrs; + nss_status::NSS_STATUS_SUCCESS + } + Err(err) => { + set_if_valid( + args.errnop, + err.raw_os_error() + .map(Errno::from_i32) + .unwrap_or(Errno::EAGAIN), + ); + set_if_valid(args.h_errnop, HErrno::Internal); + nss_status::NSS_STATUS_TRYAGAIN + } + } + } + }; + + ret +} + +// TODO: refactor to be less ugly +pub unsafe fn records_to_hostent( + lookup: Lookup, + host: &mut hostent, + buf: &mut [u8], +) -> std::io::Result { + // In C struct hostent: + // + // - for the type of queries we perform, we can assume h_aliases + // is an empty array. + // - hostent is limited to just one address type for all addresses + // in the list. We pick the type of first result, and only + // append addresses of the same type. + + let first_record = match lookup.record_iter().peekable().peek() { + Some(record) => *record, + None => return Ok(DnsResult::NxDomain), + }; + + let first_rdata = match first_record.data() { + Some(rdata) => rdata, + None => return Err(Errno::ENODATA.into()), + }; + + // First put the name in the buffer + let (buf, name_dest) = write_record_name_to_buf(first_record, buf)?; + + // Then the empty aliases array (just a null pointer) + let (mut buf, aliases_ptr) = write_vector_to_buf(buf, vec![std::ptr::null_mut()])?; + + // Then the address list + let offset = buf.as_ptr().align_offset(align_of::<*mut c_char>()); + buf = &mut buf[offset..]; + + let records = lookup + .record_iter() + .flat_map(Record::data) + .filter(|r| discriminant(*r) == discriminant(first_rdata)); + + let mut addresses = vec![]; + for record in records { + let offset = buf.as_ptr().align_offset(align_of::<*mut c_char>()); + buf = &mut buf[offset..]; + addresses.push(buf.as_mut_ptr()); + + let l = match record { + RData::A(addr) => { + let octets = addr.octets(); + let l = octets.len(); + if buf.len() < l { + return Err(Errno::ERANGE.into()); + } + buf[..l].copy_from_slice(&octets); + l + } + RData::AAAA(addr) => { + let octets = addr.octets(); + let l = octets.len(); + if buf.len() < l { + return Err(Errno::ERANGE.into()); + } + buf[..l].copy_from_slice(&octets); + l + } + _ => unreachable!(), + }; + + buf = &mut buf[l..]; + } + + addresses.push(std::ptr::null_mut()); + let (_, addr_list) = write_vector_to_buf(buf, addresses)?; + + // Finally, populate the hostent structure + host.h_name = name_dest; + host.h_aliases = aliases_ptr; + host.h_addr_list = addr_list as *mut *mut c_char; + match first_rdata { + RData::A(_) => { + host.h_addrtype = AF_INET; + host.h_length = 4; + } + RData::AAAA(_) => { + host.h_addrtype = AF_INET6; + host.h_length = 16; + } + _ => return Err(Errno::EBADMSG.into()), + } + + Ok(DnsResult::Found) +} + +// TODO: error handling codes chosen a bit sloppily +pub unsafe fn records_to_gaih_addr( + lookup: Lookup, + mut buf: &mut [u8], +) -> std::io::Result<*mut gaih_addrtuple> { + const GAIH_ADDRTUPLE_SZ: usize = size_of::(); + + let mut ret = std::ptr::null_mut(); + let mut prev_link: *mut *mut gaih_addrtuple = std::ptr::null_mut(); + + let mut name_dest; + for record in lookup.record_iter() { + // First add the name to the buffer + (buf, name_dest) = write_record_name_to_buf(record, buf)?; + + // Then add the tuple with the address + let offset = buf.as_ptr().align_offset(align_of::()); + let l = GAIH_ADDRTUPLE_SZ; + if buf.len() < offset + l { + return Err(Errno::ERANGE.into()); + } + + let tuple = &mut *(buf.as_mut_ptr().add(offset) as *mut gaih_addrtuple); + tuple.next = std::ptr::null_mut(); + tuple.name = name_dest; + tuple.scopeid = 0; // how to set tuple.scopeid correctly??? Use a custom trust_dns_resolver::ConnectionProvider? + set_if_valid(prev_link, &mut *tuple); // link from previous tuple to this tuple + prev_link = &mut (*tuple).next; + + match record.data() { + Some(RData::A(addr)) => { + tuple.family = AF_INET; + tuple.addr[0] = std::mem::transmute_copy(&addr.octets()); + } + Some(RData::AAAA(addr)) => { + tuple.family = AF_INET6; + tuple.addr = std::mem::transmute_copy(&addr.octets()); + } + Some(_) => return Err(Errno::EBADMSG.into()), + None => return Err(Errno::ENODATA.into()), + } + + if ret == std::ptr::null_mut() { + ret = tuple; + } + + buf = &mut buf[(offset + l)..]; + } + + Ok(ret) +} diff --git a/nss/src/helpers.rs b/nss/src/helpers.rs new file mode 100644 index 0000000..7539eb4 --- /dev/null +++ b/nss/src/helpers.rs @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +use { + hickory_proto::rr::resource::Record, + libc::c_char, + nix::errno::Errno, + once_cell::sync::Lazy, + std::ffi::CString, + std::mem::{align_of, size_of}, +}; + +static RUNTIME: Lazy> = Lazy::new(|| { + // The runtime should remain single-threaded, some + // programs depend on it (e.g. programs calling unshare()) + let rt = tokio::runtime::Builder::new_current_thread() + .enable_time() + .enable_io() + .build()?; + Ok(rt) +}); + +pub fn write_record_name_to_buf<'a>( + record: &Record, + buf: &'a mut [u8], +) -> std::io::Result<(&'a mut [u8], *mut c_char)> { + use std::io::{Error, ErrorKind}; + let name = CString::new(record.name().to_utf8()) + .map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string()))?; + + let offset = buf.as_ptr().align_offset(align_of::()); + let name_src = name.as_bytes_with_nul(); + let l = name_src.len(); + if buf.len() < offset + l { + return Err(Errno::ERANGE.into()); + } + + let name_dest = unsafe { buf.as_mut_ptr().add(offset) }; + unsafe { + std::ptr::copy_nonoverlapping(name_src.as_ptr(), name_dest, l); + } + Ok((&mut buf[(offset + l)..], name_dest as *mut c_char)) +} + +pub fn write_vector_to_buf<'a, T>( + buf: &'a mut [u8], + v: Vec, +) -> std::io::Result<(&'a mut [u8], *mut T)> { + let offset = buf.as_ptr().align_offset(align_of::<*mut T>()); + let l = size_of::<*mut c_char>() * v.len(); + if buf.len() < offset + l { + return Err(Errno::ERANGE.into()); + } + + let buf = &mut buf[offset..]; + let dest = buf.as_mut_ptr().cast::(); + unsafe { + std::ptr::copy_nonoverlapping(v.as_ptr() as *const T, dest, l); + } + Ok((&mut buf[l..], dest)) +} + +pub fn set_if_valid(ptr: *mut T, val: T) { + if !ptr.is_null() { + unsafe { *ptr = val }; + } +} + +pub fn block_on(f: F) -> anyhow::Result +where + F: std::future::Future, +{ + // TODO: use std::panic::catch_unwind() to be resilient + // to OOM problems + + use std::ops::Deref; + match RUNTIME.deref() { + Ok(rt) => Ok(rt.block_on(async { f.await })), + Err(e) => anyhow::bail!("Unable to start tokio runtime: {}", e), + } +} diff --git a/nss/src/lib.rs b/nss/src/lib.rs new file mode 100644 index 0000000..eddc819 --- /dev/null +++ b/nss/src/lib.rs @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +mod gethostbyaddr; +mod gethostbyname; +mod helpers; +pub mod nss_api; +mod nss_bindings; +mod policy_checker; + +use std::str::FromStr; + +struct LevelFilter(log::LevelFilter); + +impl FromStr for LevelFilter { + type Err = (); + + fn from_str(s: &str) -> Result { + let lv = match s.to_lowercase().as_str() { + "off" => log::LevelFilter::Off, + "error" => log::LevelFilter::Error, + "warn" => log::LevelFilter::Warn, + "info" => log::LevelFilter::Info, + "debug" => log::LevelFilter::Debug, + "trace" => log::LevelFilter::Trace, + _ => return Err(()), + }; + Ok(Self(lv)) + } +} + +ld_preload::ld_preload_init! { + { + let level = std::env::var("NSS_MALCONTENT_LOG") + .ok() + .and_then(|level| { LevelFilter::from_str(&level).ok() }) + .map(|x| x.0) + .unwrap_or(log::LevelFilter::Warn); + + let _ = syslog::init(syslog::Facility::LOG_AUTHPRIV, level, Some("nss_malcontent")); + } +} diff --git a/nss/src/nss_api.rs b/nss/src/nss_api.rs new file mode 100644 index 0000000..56bfce5 --- /dev/null +++ b/nss/src/nss_api.rs @@ -0,0 +1,193 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +use { + crate::helpers::{block_on, set_if_valid}, + crate::nss_bindings::{gaih_addrtuple, nss_status, HErrno}, + crate::{gethostbyaddr, gethostbyname}, + libc::{hostent, size_t, socklen_t, AF_INET}, + nix::errno::Errno, + std::os::raw::{c_char, c_int, c_void}, + std::ptr, +}; + +// -------------- by host --------------- + +#[no_mangle] +pub unsafe extern "C" fn _nss_malcontent_gethostbyname4_r( + name: *const c_char, + pat: *mut *mut gaih_addrtuple, + buffer: *mut c_char, + buflen: size_t, + errnop: *mut Errno, + h_errnop: *mut HErrno, + ttlp: *mut i32, +) -> nss_status { + log::trace!("--------------- _nss_malcontent_gethostbyname4_r ---------------------"); + + let mut args = gethostbyname::Args { + name, + family: 0, + result: gethostbyname::Result::V4(pat), + buffer, + buflen, + errnop, + h_errnop, + ttlp, + }; + + match block_on(async { gethostbyname::with(&mut args).await }) { + Ok(status) => status, + Err(runtime_error) => { + log::error!("gethostbyname4_r: {}", runtime_error); + set_if_valid(args.h_errnop, HErrno::Internal); + nss_status::NSS_STATUS_TRYAGAIN + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn _nss_malcontent_gethostbyname3_r( + name: *const c_char, + af: c_int, + host: *mut hostent, + buffer: *mut c_char, + buflen: size_t, + errnop: *mut Errno, + h_errnop: *mut HErrno, + ttlp: *mut i32, + canonp: *mut *mut char, +) -> nss_status { + log::trace!("--------------- _nss_malcontent_gethostbyname3_r ---------------------"); + + let mut args = gethostbyname::Args { + name, + family: af, + result: gethostbyname::Result::V3(host), + buffer, + buflen, + errnop, + h_errnop, + ttlp, + }; + + match block_on(async { gethostbyname::with(&mut args).await }) { + Ok(status) => { + if !host.is_null() { + set_if_valid(canonp, (*host).h_name as *mut char); + } + status + } + Err(runtime_error) => { + log::error!("gethostbyname3_r: {}", runtime_error); + set_if_valid(h_errnop, HErrno::Internal); + nss_status::NSS_STATUS_TRYAGAIN + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn _nss_malcontent_gethostbyname2_r( + name: *const c_char, + af: c_int, + host: *mut hostent, + buffer: *mut c_char, + buflen: size_t, + errnop: *mut Errno, + h_errnop: *mut HErrno, +) -> nss_status { + log::trace!("--------------- _nss_malcontent_gethostbyname2_r ---------------------"); + + _nss_malcontent_gethostbyname3_r( + name, + af, + host, + buffer, + buflen, + errnop, + h_errnop, + ptr::null_mut(), + ptr::null_mut(), + ) +} + +#[no_mangle] +pub unsafe extern "C" fn _nss_malcontent_gethostbyname_r( + name: *const c_char, + host: *mut hostent, + buffer: *mut c_char, + buflen: size_t, + errnop: *mut Errno, + h_errnop: *mut HErrno, +) -> nss_status { + log::trace!("--------------- _nss_malcontent_gethostbyname_r ---------------------"); + + _nss_malcontent_gethostbyname3_r( + name, + AF_INET, + host, + buffer, + buflen, + errnop, + h_errnop, + ptr::null_mut(), + ptr::null_mut(), + ) +} + +// ----------------- by addr ----------------- + +#[no_mangle] +pub unsafe extern "C" fn _nss_malcontent_gethostbyaddr2_r( + addr: *const c_void, + len: socklen_t, + af: c_int, + host: *mut hostent, + buffer: *mut c_char, + buflen: size_t, + errnop: *mut Errno, + h_errnop: *mut HErrno, + ttlp: *mut i32, +) -> nss_status { + log::trace!("--------------- _nss_malcontent_gethostbyaddr2_r ---------------------"); + + set_if_valid(errnop, nix::errno::from_i32(0)); + set_if_valid(h_errnop, HErrno::Success); + + match block_on(async { + gethostbyaddr::with(addr, len, af, host, buffer, buflen, errnop, h_errnop, ttlp).await + }) { + Ok(status) => status, + Err(runtime_error) => { + log::error!("gethostbyaddr2_r: {}", runtime_error); + set_if_valid(h_errnop, HErrno::Internal); + nss_status::NSS_STATUS_TRYAGAIN + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn _nss_malcontent_gethostbyaddr_r( + addr: *const c_void, + len: socklen_t, + af: c_int, + host: *mut hostent, + buffer: *mut c_char, + buflen: size_t, + errnop: *mut Errno, + h_errnop: *mut HErrno, +) -> nss_status { + log::trace!("--------------- _nss_malcontent_gethostbyaddr_r ---------------------"); + + _nss_malcontent_gethostbyaddr2_r( + addr, + len, + af, + host, + buffer, + buflen, + errnop, + h_errnop, + ptr::null_mut(), + ) +} diff --git a/nss/src/nss_bindings.rs b/nss/src/nss_bindings.rs new file mode 100644 index 0000000..fe07baf --- /dev/null +++ b/nss/src/nss_bindings.rs @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +#![allow(dead_code)] +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/nss/src/policy_checker/dbus.rs b/nss/src/policy_checker/dbus.rs new file mode 100644 index 0000000..f7a1ca5 --- /dev/null +++ b/nss/src/policy_checker/dbus.rs @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +use { + serde::{Deserialize, Serialize}, + std::net::IpAddr, + zbus::{dbus_proxy, Result}, +}; + +#[dbus_proxy( + default_service = "com.endlessm.ParentalControls", + interface = "com.endlessm.ParentalControls.Dns", + default_path = "/com/endlessm/ParentalControls/Dns", + gen_blocking = false +)] +trait MalcontentDns { + fn get_dns(&self) -> Result; +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, zvariant::Type)] +pub struct Restriction { + pub ip: IpAddr, + pub hostname: String, +} + +pub type Restrictions = Vec; + +pub async fn restrictions() -> anyhow::Result, anyhow::Error> { + #[cfg(not(feature = "integration_test"))] + let connection = { + // This is the normal behavior + zbus::Connection::session().await? + }; + + #[cfg(feature = "integration_test")] + let connection = { + use tokio::net::TcpStream; + + // During integration testing, we want to connect to a private + // bus name to avoid clashes with existing system services. + let socketaddr = std::env::var("TEST_DBUS_SOCKET") + .expect("The test has not set the TEST_DBUS_SOCKET environment variable"); + + let socket = loop { + match TcpStream::connect(&socketaddr).await { + Ok(stream) => break stream, + Err(e) if e.kind() == std::io::ErrorKind::ConnectionRefused => { + tokio::task::yield_now().await; + continue; + } + Err(e) => anyhow::bail!(e), + } + }; + + log::trace!("connecting to DBus on {}", socket.local_addr()?); + zbus::ConnectionBuilder::tcp_stream(socket) + .p2p() + .build() + .await? + }; + + let proxy = MalcontentDnsProxy::builder(&connection).build().await?; + let restrictions = proxy.get_dns().await; + log::trace!("malcontent-nss: user restrictions are {:?}", &restrictions); + Ok(restrictions?) +} diff --git a/nss/src/policy_checker/mod.rs b/nss/src/policy_checker/mod.rs new file mode 100644 index 0000000..7571bfb --- /dev/null +++ b/nss/src/policy_checker/mod.rs @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +mod dbus; + +use { + anyhow::Result, + hickory_proto::rr::domain::Name as DomainName, + hickory_resolver::config as dns_config, + hickory_resolver::TokioAsyncResolver, + nix::unistd::{getuid, Uid}, + once_cell::sync::Lazy, + std::collections::HashMap, + std::net::{SocketAddr, TcpStream}, + std::sync::{Arc, RwLock}, + std::time::Duration, +}; + +pub use self::dbus::{Restriction, Restrictions}; + +const DNS_UDP_PORT: u16 = 53; +const DNS_TLS_PORT: u16 = 853; + +pub static POLICY_CHECKER: Lazy = Lazy::new(|| PolicyChecker::new()); + +// TODO: accept notifications about config changes +pub struct PolicyChecker { + resolvers: RwLock>>>, +} + +impl PolicyChecker { + pub fn new() -> Self { + Self { + resolvers: RwLock::new(HashMap::new()), + } + } + + async fn restrictions(&self, user: Uid) -> Result { + if user.is_root() { + return Ok(vec![]); + }; + + dbus::restrictions().await + } + + pub async fn resolver(&self) -> Result>> { + let user = getuid(); // account for processes changing user during execution + + log::trace!("querying PolicyChecker::resolver() for user {}", user); + // Check if already cached + { + let ro_resolvers = self.resolvers.read().unwrap(); + if let Some(resolver) = ro_resolvers.get(&user) { + return Ok(resolver.clone()); + } + } + + // Else, initialize and cache it + { + let mut rw_resolvers = self.resolvers.write().unwrap(); + if let Some(resolver) = rw_resolvers.get(&user) { + return Ok(resolver.clone()); + } + + // try first to prime resolver with DoT implementation, + // fallback to unencrypted only if not available. + let restrictions = self.restrictions(user).await?; + let resolver = if !restrictions.is_empty() { + let resolver = TokioAsyncResolver::tokio( + resolver_config_for(restrictions), + dns_config::ResolverOpts::default(), + ); + Some(Arc::new(resolver)) + } else { + None + }; + + rw_resolvers.insert(user, resolver.clone()); + Ok(resolver) + } + } +} + +fn resolver_config_for(restrictions: Vec) -> dns_config::ResolverConfig { + use dns_config::NameServerConfigGroup as NsConfig; + + let resolver_config_group = + restrictions + .into_iter() + .fold(NsConfig::new(), |mut config, restr| { + let supports_tls = TcpStream::connect_timeout( + &SocketAddr::new(restr.ip, DNS_TLS_PORT), + Duration::from_secs(1), + ) + .is_ok(); + + let new_config = if supports_tls { + NsConfig::from_ips_tls(&[restr.ip], DNS_TLS_PORT, restr.hostname, true) + } else { + NsConfig::from_ips_clear(&[restr.ip], DNS_UDP_PORT, true) + }; + + config.merge(new_config); + config + }); + + let basename = gethostname::gethostname() + .as_os_str() + .to_str() + .map(|hn| DomainName::from_labels(hn.split('.')).ok()) + .flatten() + .map(|hn| hn.base_name()); + + dns_config::ResolverConfig::from_parts(basename, vec![], resolver_config_group) +} diff --git a/nss/tests/common/dbus.rs b/nss/tests/common/dbus.rs new file mode 100644 index 0000000..43f57fc --- /dev/null +++ b/nss/tests/common/dbus.rs @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +include!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/policy_checker/dbus.rs" +)); + +use { + std::sync::atomic::{AtomicUsize, Ordering}, + tokio_util::sync::CancellationToken, + zbus::dbus_interface, +}; + +#[derive(Debug)] +pub struct MalcontentDBusMock { + responses: Vec, + invocations_left: AtomicUsize, +} + +#[dbus_interface(name = "com.endlessm.ParentalControls.Dns")] +impl MalcontentDBusMock { + fn get_dns(&mut self) -> Restrictions { + let restrictions = self + .responses + .pop() + .expect("MockError: DBus mock is saturated"); + self.invocations_left.fetch_sub(1, Ordering::SeqCst); + restrictions + } +} + +impl MalcontentDBusMock { + pub fn new(mut responses: Vec) -> Self { + responses.reverse(); // We pop responses from the back, so... + Self { + invocations_left: AtomicUsize::new(responses.len()), + responses, + } + } +} + +impl Drop for MalcontentDBusMock { + fn drop(&mut self) { + let invocations_left = self.invocations_left.load(Ordering::Acquire); + assert_eq!( + invocations_left, 0, + "MockError: During teardown, {} invocations are still left on the mock object", + invocations_left + ); + } +} + +pub struct DBusMockServer { + handle: std::thread::JoinHandle>, + cancellation: CancellationToken, +} + +impl DBusMockServer { + pub fn new(responses: Vec) -> Result { + let token = CancellationToken::new(); + let cloned_token = token.clone(); + + let listener = std::net::TcpListener::bind("127.0.0.1:0")?; + std::env::set_var("TEST_DBUS_SOCKET", format!("{}", listener.local_addr()?)); + + let handle = std::thread::spawn(move || { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(Self::spawn_async(responses, listener, cloned_token)) + }); + + Ok(Self { + handle: handle, + cancellation: token, + }) + } + + pub fn stop(self) -> Result<()> { + self.cancellation.cancel(); + self.handle.join().unwrap() + } + + async fn spawn_async( + responses: Vec, + listener: std::net::TcpListener, + cancellation_token: CancellationToken, + ) -> Result<()> { + listener.set_nonblocking(true)?; + let listener = tokio::net::TcpListener::from_std(listener)?; + + let guid = zbus::Guid::generate(); + let mock = MalcontentDBusMock::new(responses); + let (stream, _) = listener + .accept() + .await + .expect("Server socket closed unexpectedly"); + + log::trace!("dbus mock server accepted client connection"); + let _connection = zbus::ConnectionBuilder::tcp_stream(stream) + .server(&guid) + .p2p() + .auth_mechanisms(&[zbus::AuthMechanism::Anonymous]) + .name("com.endlessm.ParentalControls") + .expect("Unable to serve given dbus name") + .serve_at("/com/endlessm/ParentalControls/Dns", mock) + .expect("Unable to server malcontent dbus mock object") + .build() + .await?; + + tokio::select! { + _ = cancellation_token.cancelled() => { + Ok(()) + } + _ = std::future::pending::<()>() => { unreachable!() } + } + } +} + +pub struct DBusMockServerGuard { + mock: Option, +} + +impl DBusMockServerGuard { + pub fn new(responses: Vec) -> Result { + Ok(Self { + mock: Some(DBusMockServer::new(responses)?), + }) + } +} + +impl Drop for DBusMockServerGuard { + fn drop(&mut self) { + self.mock + .take() + .unwrap() + .stop() + .expect("cannot stop dbus server mock"); + } +} diff --git a/nss/tests/common/mod.rs b/nss/tests/common/mod.rs new file mode 100644 index 0000000..bee09bd --- /dev/null +++ b/nss/tests/common/mod.rs @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +mod dbus; + +use { + anyhow::{anyhow, bail, ensure, Result}, + libc::{freeaddrinfo, gai_strerror, getaddrinfo}, + nix::sys::socket::{SockaddrLike as _, SockaddrStorage}, + std::env, + std::ffi::{CStr, CString}, + std::net::{IpAddr, Ipv4Addr, Ipv6Addr}, + std::path::PathBuf, + std::process::Command, + std::str::FromStr, +}; + +pub use self::dbus::{DBusMockServerGuard, Restriction, Restrictions}; + +pub type Eai = EaiRetcode; + +#[static_init::dynamic] +static GLOBAL_SETUP: () = { + let cdylib_path = test_cdylib::build_current_project(); + let file_name = cdylib_path.file_name().unwrap(); + + let mut versioned_file_name = file_name.to_owned(); + versioned_file_name.push(".2"); // required for NSS 3 modules + + let dest = PathBuf::from(std::env::current_exe().unwrap()).with_file_name(versioned_file_name); + let _ = std::fs::remove_file(&dest); + std::fs::hard_link(&cdylib_path, &dest).expect(&format!( + "Cannot hard link {} to {}", + cdylib_path.display(), + dest.display() + )); +}; + +/// Note that this should be called once per process; this implies that +/// each test calling this function is in a separate process too. +pub fn setup() -> Result<()> { + let _ = env_logger::builder() + .is_test(true) + .filter_level(log::LevelFilter::max()) + .try_init(); + + std::env::set_var("NSS_MALCONTENT_LOG", "trace"); + + // setup NSS to load our cdylib, now versioned + let db = CString::new("hosts").unwrap(); + let resolvers = CString::new("malcontent dns").unwrap(); + let nss_config_status = unsafe { __nss_configure_lookup(db.as_ptr(), resolvers.as_ptr()) }; + + if nss_config_status != 0 { + panic!( + "Unable to configure NSS to load module: __nss_configure_lookup() returned {}", + nss_config_status + ); + } + + Ok(()) +} + +pub fn resolve_with_system(family: libc::c_int, host: &str) -> Result { + let process = Command::new("getent") + .arg(match family { + libc::AF_INET => "ahostsv4", + libc::AF_INET6 => "ahostsv6", + _ => panic!("INET family must be either IPv4 or IPv6"), + }) + .arg(host) + .output()?; + ensure!( + process.status.success(), + "Failed to run getent to check host IP" + ); + let output = String::from_utf8(process.stdout)?; + log::trace!("system getent reports: {}", &output); + let addr_string = output + .as_str() + .split(' ') + .next() + .ok_or(anyhow!("Unparseable output from getent"))?; + Ok(IpAddr::from_str(&addr_string)?) +} + +pub fn resolve_with_module(family: libc::c_int, hostname: &str) -> Result> { + let c_hostname = CString::new(hostname).unwrap(); + unsafe { + let mut addr = std::ptr::null_mut(); + let hints = libc::addrinfo { + ai_family: family, + ai_flags: 0, + ai_socktype: 0, + ai_protocol: 0, + ai_addrlen: 0, + ai_addr: std::ptr::null_mut(), + ai_canonname: std::ptr::null_mut(), + ai_next: std::ptr::null_mut(), + }; + let getaddrinfo_status = + getaddrinfo(c_hostname.as_ptr(), std::ptr::null(), &hints, &mut addr); + + let error = CStr::from_ptr(gai_strerror(getaddrinfo_status)); + assert_eq!( + getaddrinfo_status, + Eai::Success.0, + "Should have gotten hostname for {}, instead got {}", + hostname, + error.to_str().unwrap() + ); + + let ips = convert_addrinfo(addr)?; + freeaddrinfo(addr); + Ok(ips) + } +} + +unsafe fn convert_addrinfo(addrs: *const libc::addrinfo) -> Result> { + let mut ips = vec![]; + + let mut addr = addrs; + while addr != std::ptr::null() { + let addr_storage = SockaddrStorage::from_raw((*addr).ai_addr, Some((*addr).ai_addrlen)) + .ok_or(anyhow!("Garbled addrinfo from getaddrinfo()"))?; + + if let Some(addr) = addr_storage.as_sockaddr_in() { + ips.push(IpAddr::V4(Ipv4Addr::from(addr.ip()))); + } else if let Some(addr) = addr_storage.as_sockaddr_in6() { + ips.push(IpAddr::V6(Ipv6Addr::from(addr.ip()))); + } else { + bail!("addrinfo is not either an IPv4 or IPv6 address") + } + + addr = (*addr).ai_next; + } + + ips.sort(); + ips.dedup(); + Ok(ips) +} diff --git a/nss/tests/integration_test.rs b/nss/tests/integration_test.rs new file mode 100644 index 0000000..2447c7e --- /dev/null +++ b/nss/tests/integration_test.rs @@ -0,0 +1,243 @@ +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + +mod common; + +use { + crate::common::{Eai, Restriction, Restrictions}, + anyhow::Result, + elf::ElfStream, + libc::{freeaddrinfo, gai_strerror, getaddrinfo}, + once_cell::sync::Lazy, + rusty_fork::rusty_fork_test, + std::net::{IpAddr, Ipv4Addr, Ipv6Addr}, + std::os::unix::fs::FileExt, + std::path::PathBuf, +}; + +static DNS0_PARENTALCONTROL_ADDRS: Lazy = Lazy::new(|| { + vec![ + Restriction { + ip: IpAddr::V4(Ipv4Addr::new(193, 110, 81, 1)), + hostname: "kids.dns0.eu".into(), + }, + Restriction { + ip: IpAddr::V4(Ipv4Addr::new(185, 253, 5, 1)), + hostname: "kids.dns0.eu".into(), + }, + Restriction { + ip: IpAddr::V6(Ipv6Addr::new(0x2a0f, 0xfc80, 0, 0, 0, 0, 0, 0x1)), + hostname: "kids.dns0.eu".into(), + }, + Restriction { + ip: IpAddr::V6(Ipv6Addr::new(0x2a0f, 0xfc81, 0, 0, 0, 0, 0, 0x1)), + hostname: "kids.dns0.eu".into(), + }, + ] +}); + +static CLOUDFLARE_PARENTALCONTROL_ADDRS: Lazy = Lazy::new(|| { + vec![ + Restriction { + ip: IpAddr::V4(Ipv4Addr::new(1, 1, 1, 3)), + hostname: "family.cloudflare-dns.com".into(), + }, + Restriction { + ip: IpAddr::V4(Ipv4Addr::new(1, 0, 0, 3)), + hostname: "family.cloudflare-dns.com".into(), + }, + Restriction { + ip: IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1113)), + hostname: "family.cloudflare-dns.com".into(), + }, + Restriction { + ip: IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1003)), + hostname: "family.cloudflare-dns.com".into(), + }, + ] +}); + +static NO_RESTRICTIONS: Lazy = Lazy::new(|| vec![]); + +#[test] +fn sanity_check() -> Result<()> { + let current_exe = PathBuf::from(std::env::current_exe().unwrap()); + let myself = std::fs::File::open(¤t_exe)?; + let mut elf_stream: ElfStream = + elf::ElfStream::open_stream(&myself)?; + let dynamic_section = elf_stream.dynamic()?.expect("No dynamic table"); + let rpath_off = dynamic_section + .iter() + .find(|x| x.d_tag == elf::abi::DT_RPATH) + .map(|x| x.d_val()) + .expect("No DT_RPATH found in test executable ELF dyn section"); + let strtable = dynamic_section + .into_iter() + .find(|x| x.d_tag == elf::abi::DT_STRTAB) + .map(|x| x.d_ptr()) + .expect("No DT_STRTAB found in test executable ELF dyn section"); + + let mut buf = [0u8; 1024]; + let _ = myself.read_at(buf.as_mut_slice(), strtable + rpath_off)?; + let end = buf + .iter() + .position(|c| *c == 0u8) + .expect(&format!("RPATH is bigger than {} bytes", buf.len())); + let rpath = std::str::from_utf8(&buf[..end])?; + assert_eq!(rpath, "$ORIGIN"); + + common::setup()?; + let lib = current_exe.parent().unwrap().join("libnss_malcontent.so.2"); + assert!(lib.exists()); + + Ok(()) +} + +rusty_fork_test! { + #[test] + fn application_dns_is_nodata() { + common::setup().unwrap(); + + let _dbus = common::DBusMockServerGuard::new(vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()]).unwrap(); + + let hostname = std::ffi::CString::new("use-application-dns.net").unwrap(); + unsafe { + let mut addr = std::ptr::null_mut(); + let getaddrinfo_status = getaddrinfo( + hostname.as_ptr(), + std::ptr::null(), + std::ptr::null(), + &mut addr, + ); + + let error = std::ffi::CStr::from_ptr(gai_strerror(getaddrinfo_status)); + assert_eq!( + getaddrinfo_status, + Eai::NoData.0, + "Should have gotten no data (NODATA), instead got {}", + error.to_str().unwrap() + ); + freeaddrinfo(addr); + }; + } + + #[test] + fn getaddrinfo_resolution() { + common::setup().unwrap(); + + const HOSTNAME: &str = "gnome.org"; + + let _dbus = common::DBusMockServerGuard::new(vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()]).unwrap(); + + unsafe { + let mut addr = std::ptr::null_mut(); + let hostname = std::ffi::CString::new(HOSTNAME).unwrap(); + let getaddrinfo_status = getaddrinfo( + hostname.as_ptr(), + std::ptr::null(), + std::ptr::null(), + &mut addr, + ); + + let error = std::ffi::CStr::from_ptr(gai_strerror(getaddrinfo_status)); + assert_eq!( + getaddrinfo_status, + Eai::Success.0, + "Should have gotten an hostname, instead got {}", + error.to_str().unwrap() + ); + freeaddrinfo(addr); + }; + } + + #[test] + fn wikipedia_is_unrestricted() { + common::setup().unwrap(); + + const HOSTNAME: &str = "wikipedia.org"; + + let _dbus = common::DBusMockServerGuard::new(vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()]).unwrap(); + + for family in [libc::AF_INET, libc::AF_INET6] { + let system_addr = common::resolve_with_system(family, HOSTNAME).unwrap(); + let our_addrs = common::resolve_with_module(family, HOSTNAME).unwrap(); + assert!(our_addrs.contains(&system_addr)); + } + } + + #[test] + fn dns0_adultsites_are_restricted() { + common::setup().unwrap(); + + const HOSTNAME: &str = "pornhub.com"; + + let _dbus = common::DBusMockServerGuard::new(vec![DNS0_PARENTALCONTROL_ADDRS.clone()]).unwrap(); + + let our_addrs_ipv4 = common::resolve_with_module(libc::AF_INET, HOSTNAME).unwrap(); + let our_addrs_ipv6 = common::resolve_with_module(libc::AF_INET, HOSTNAME).unwrap(); + assert!( + our_addrs_ipv4.is_empty(), + "Resolver answered with {:?}, should be empty", + our_addrs_ipv4 + ); + assert!( + our_addrs_ipv6.is_empty(), + "Resolver answered with {:?}, should be empty", + our_addrs_ipv6 + ); + } + + #[test] + fn cloudflare_adultsite_is_restricted_ipv4() { + common::setup().unwrap(); + + const HOSTNAME: &str = "nudity.testcategory.com"; + + let _dbus = common::DBusMockServerGuard::new(vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()]).unwrap(); + + let system_addr = common::resolve_with_system(libc::AF_INET, HOSTNAME).unwrap(); + let our_addrs = common::resolve_with_module(libc::AF_INET, HOSTNAME).unwrap(); + assert!( + !our_addrs.contains(&system_addr), + "Resolver answered with {:?}, should not contain {}", + our_addrs, + system_addr + ); + assert_eq!(our_addrs, [IpAddr::V4(Ipv4Addr::UNSPECIFIED)]); + } + + #[test] + fn cloudflare_adultsite_is_restricted_ipv6() { + common::setup().unwrap(); + + const HOSTNAME: &str = "nudity.testcategory.com"; + + let _dbus = common::DBusMockServerGuard::new(vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()]).unwrap(); + + let system_addr = common::resolve_with_system(libc::AF_INET6, HOSTNAME).unwrap(); + let our_addrs = common::resolve_with_module(libc::AF_INET6, HOSTNAME).unwrap(); + assert!( + !our_addrs.contains(&system_addr), + "Resolver answered with {:?}, should not contain {}", + our_addrs, + system_addr + ); + assert_eq!(our_addrs, [IpAddr::V6(Ipv6Addr::UNSPECIFIED)]); + } + + #[test] + fn privileged_user_bypasses_restrictions() { + common::setup().unwrap(); + + const HOSTNAME: &str = "malware.testcategory.com"; + + let _dbus = common::DBusMockServerGuard::new(vec![NO_RESTRICTIONS.clone()]).unwrap(); + + for family in [libc::AF_INET, libc::AF_INET6] { + let system_addr = common::resolve_with_system(family, HOSTNAME).unwrap(); + let our_addrs = common::resolve_with_module(family, HOSTNAME).unwrap(); + assert!(our_addrs.contains(&system_addr)); + } + } + +} diff --git a/nss/wrapper.hpp b/nss/wrapper.hpp new file mode 100644 index 0000000..30be12b --- /dev/null +++ b/nss/wrapper.hpp @@ -0,0 +1,43 @@ +/* +// SPDX-FileCopyrightText: Matteo Settenvini +// SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include + +// Work around enums in netdb.h defined as macros instead :-p + +enum class HErrno : int { + Success = 0, + HostNotFound = HOST_NOT_FOUND, + TryAgain = TRY_AGAIN, + NoRecovery = NO_RECOVERY, + NoData = NO_DATA, + #ifdef __USE_MISC + Internal = NETDB_INTERNAL, + #endif +}; + +enum class EaiRetcode : int { + Success = 0, + BadFlags = EAI_BADFLAGS, + NoName = EAI_NONAME, + Fail = EAI_FAIL, + Family = EAI_FAMILY, + SockType = EAI_SOCKTYPE, + Service = EAI_SERVICE, + Memory = EAI_MEMORY, + System = EAI_SYSTEM, + Overflow = EAI_OVERFLOW, + #ifdef __USE_GNU + NoData = EAI_NODATA, + AddrFamily = EAI_ADDRFAMILY, + InProgress = EAI_INPROGRESS, + Canceled = EAI_CANCELED, + NotCanceled = EAI_NOTCANCELED, + AllDone = EAI_ALLDONE, + Interrupted = EAI_INTR, + IdnEncode = EAI_IDN_ENCODE, + #endif /* __USE_GNU */ +}; -- 2.39.5