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',
]
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