init
This commit is contained in:
commit
c8735ef768
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/result
|
390
Cargo.lock
generated
Normal file
390
Cargo.lock
generated
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "boxcar"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "510a90332002c1af3317ef6b712f0dab697f30bbe809b86965eac2923c0bca8e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctor"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "endian-type"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "err-derive"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c34a887c8df3ed90498c1c437ce21f211c8e27672921a8ffa293cb8d6d4caa9e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iptrie"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0de342269e2f44480e31fc4bd43d9d0ae081cce8fcfc43ce125299df95dbf1c9"
|
||||||
|
dependencies = [
|
||||||
|
"ipnet",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mnl-sys"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9750685b201e1ecfaaf7aa5d0387829170fa565989cc481b49080aa155f70457"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nftables"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b5081f5cae4d24af558828494371be6672d02a693e9b4cbca4a0cbae5443e6f"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_path_to_error",
|
||||||
|
"strum",
|
||||||
|
"strum_macros",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nftnl"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9201688bd0bc571dfa4c21ce0a525480c8b782776cf88e12571fa89108dd920"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"err-derive",
|
||||||
|
"log",
|
||||||
|
"nftnl-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nftnl-sys"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26b5c587b6a5e76a3a5d51e0a757ae66dbff38c277563485807ae979ce361b56"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nibble_vec"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prefix-tree"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f499660c89b7cfbbf11bb2faefe26a187062d7ff0f06bc4aba434328213f044"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "radix_trie"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
|
||||||
|
dependencies = [
|
||||||
|
"endian-type",
|
||||||
|
"nibble_vec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.205"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.205"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.122"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_path_to_error"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.26.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.12.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.72",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unbound-mod"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"boxcar",
|
||||||
|
"ctor",
|
||||||
|
"ipnet",
|
||||||
|
"iptrie",
|
||||||
|
"libc",
|
||||||
|
"mnl-sys",
|
||||||
|
"nftables",
|
||||||
|
"nftnl",
|
||||||
|
"nftnl-sys",
|
||||||
|
"prefix-tree",
|
||||||
|
"radix_trie",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
29
Cargo.toml
Normal file
29
Cargo.toml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
[package]
|
||||||
|
name = "unbound-mod"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["rlib", "cdylib"]
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
boxcar = "0.2.5"
|
||||||
|
ctor = { version = "0.2.8", optional = true }
|
||||||
|
ipnet = { version = "2.9.0", features = ["serde"] }
|
||||||
|
iptrie = "0.8.5"
|
||||||
|
libc = "0.2.155"
|
||||||
|
mnl-sys = { version = "0.2.1", features = ["mnl-1-0-4"] }
|
||||||
|
nftables = "0.4.1"
|
||||||
|
nftnl = { version = "0.6.2", features = ["nftnl-1-1-2"] }
|
||||||
|
nftnl-sys = { version = "0.6.1", features = ["nftnl-1-1-2"] }
|
||||||
|
prefix-tree = "0.5.0"
|
||||||
|
radix_trie = "0.2.1"
|
||||||
|
serde = { version = "1.0.205", features = ["derive"] }
|
||||||
|
serde_json = "1.0.122"
|
||||||
|
smallvec = "1.13.2"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
example = ["ctor"]
|
||||||
|
default = ["example"]
|
4
dummy.h
Normal file
4
dummy.h
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#include "config.h"
|
||||||
|
#include "services/cache/dns.h"
|
||||||
|
#include "services/localzone.h"
|
||||||
|
#include "dynlibmod/dynlibmod.h"
|
27
flake.lock
Normal file
27
flake.lock
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1723151029,
|
||||||
|
"narHash": "sha256-7wD4ZirqgEtc8F4CnoK0XbwP8kg67M230Gsww0Ju12w=",
|
||||||
|
"owner": "chayleaf",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "77f4097425aedb26e3b55cc2bde13fc9e19e2e0c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "chayleaf",
|
||||||
|
"ref": "unbound",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
31
flake.nix
Normal file
31
flake.nix
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
description = "A very basic flake";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:chayleaf/nixpkgs/unbound";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }: {
|
||||||
|
|
||||||
|
packages.x86_64-linux.bindings = let
|
||||||
|
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||||
|
in pkgs.unbound-full.overrideAttrs (old: {
|
||||||
|
name = "unbound-dynmod-bindings.rs";
|
||||||
|
nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.rust-bindgen pkgs.rustfmt ];
|
||||||
|
phases = ["unpackPhase" "patchPhase" "configurePhase" "installPhase"];
|
||||||
|
outputs = [ "out" ];
|
||||||
|
installPhase = ''
|
||||||
|
cp ${./dummy.h} ./dummy.h
|
||||||
|
bindgen ./dummy.h -- -I $PWD > $out
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
|
devShells.x86_64-linux.default = let
|
||||||
|
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||||
|
in pkgs.mkShell {
|
||||||
|
name = "unbound-rust-mod-shell";
|
||||||
|
LIBMNL_LIB_DIR = "${nixpkgs.lib.getLib pkgs.libmnl}/lib";
|
||||||
|
LIBNFTNL_LIB_DIR = "${nixpkgs.lib.getLib pkgs.libnftnl}/lib";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
22040
src/bindings.rs
Normal file
22040
src/bindings.rs
Normal file
File diff suppressed because it is too large
Load diff
740
src/example.rs
Normal file
740
src/example.rs
Normal file
|
@ -0,0 +1,740 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ffi::CString,
|
||||||
|
fmt::Display,
|
||||||
|
fs::File,
|
||||||
|
io::{self, BufRead, BufReader, Write},
|
||||||
|
net::{Ipv4Addr, Ipv6Addr},
|
||||||
|
os::raw::c_char,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
str::FromStr,
|
||||||
|
sync::{
|
||||||
|
mpsc::{self, RecvError},
|
||||||
|
Mutex, RwLock,
|
||||||
|
},
|
||||||
|
time::{Duration, Instant, SystemTime},
|
||||||
|
};
|
||||||
|
|
||||||
|
use ctor::ctor;
|
||||||
|
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
|
||||||
|
use iptrie::{Ipv4Prefix, Ipv6Prefix, RTrieSet};
|
||||||
|
use nftnl::set::SetKey;
|
||||||
|
use prefix_tree::PrefixSet;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
unbound::{rr_class, rr_type},
|
||||||
|
UnboundMod,
|
||||||
|
};
|
||||||
|
|
||||||
|
type Domain = SmallVec<[u8; 32]>;
|
||||||
|
type DomainSeg = SmallVec<[u8; 16]>;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ExampleMod {
|
||||||
|
domain_name_overrides: HashMap<Domain, Domain>,
|
||||||
|
nft_token: Option<String>,
|
||||||
|
tmp_nft_token: Option<String>,
|
||||||
|
nft_queries: HashMap<String, NftQuery>,
|
||||||
|
cache4: IpCache<Ipv4Addr>,
|
||||||
|
cache6: IpCache<Ipv6Addr>,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
ruleset_queue: Option<mpsc::Sender<(SmallVec<[usize; 5]>, smallvec::SmallVec<[IpNet; 8]>)>>,
|
||||||
|
error_lock: Mutex<()>,
|
||||||
|
domains_write_lock: Mutex<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
struct IpCache<T>(
|
||||||
|
RwLock<(
|
||||||
|
radix_trie::Trie<IpCacheKey, usize>,
|
||||||
|
Vec<(RwLock<smallvec::SmallVec<[T; 4]>>, Mutex<()>)>,
|
||||||
|
)>,
|
||||||
|
PathBuf,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
struct IpCacheKey(Domain);
|
||||||
|
impl radix_trie::TrieKey for IpCacheKey {
|
||||||
|
fn encode_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for IpCache<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(
|
||||||
|
RwLock::new((radix_trie::Trie::new(), Vec::new())),
|
||||||
|
PathBuf::new(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IpCache<T> {
|
||||||
|
fn get_maybe_update_rev(
|
||||||
|
&self,
|
||||||
|
domain_r: Domain,
|
||||||
|
upd: impl FnOnce(Option<&smallvec::SmallVec<[T; 4]>>) -> Option<smallvec::SmallVec<[T; 4]>>,
|
||||||
|
) {
|
||||||
|
let lock = self.0.read().unwrap();
|
||||||
|
let domain_r = IpCacheKey(domain_r);
|
||||||
|
let key = lock.0.get(&domain_r).copied();
|
||||||
|
if let Some(val) = if let Some(key) = key {
|
||||||
|
upd(lock.1.get(key).map(|x| x.0.read().unwrap()).as_deref())
|
||||||
|
} else {
|
||||||
|
upd(None)
|
||||||
|
} {
|
||||||
|
if let Some(key) = key {
|
||||||
|
*lock.1.get(key).unwrap().0.write().unwrap() = val;
|
||||||
|
} else {
|
||||||
|
drop(lock);
|
||||||
|
let mut lock = self.0.write().unwrap();
|
||||||
|
let key = lock.1.len();
|
||||||
|
lock.0.insert(domain_r, key).unwrap();
|
||||||
|
lock.1.push((RwLock::new(val), Mutex::new(())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToString + PartialEq> IpCache<T> {
|
||||||
|
fn set(&self, domain: &str, domain_r: IpCacheKey, val: smallvec::SmallVec<[T; 4]>) -> bool {
|
||||||
|
let lock = self.0.read().unwrap();
|
||||||
|
let key = lock.0.get(&domain_r).copied();
|
||||||
|
let to_write = val
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
let mut path = self.1.clone();
|
||||||
|
path.push(domain);
|
||||||
|
let finish = move |_lock| {
|
||||||
|
let Ok(mut file) = File::create(path) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
file.write_all(to_write.as_bytes()).unwrap_or(());
|
||||||
|
};
|
||||||
|
if let Some(key) = key {
|
||||||
|
let mut lock = lock.1.get(key).unwrap().0.write().unwrap();
|
||||||
|
if *lock == val {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*lock = val;
|
||||||
|
finish(lock);
|
||||||
|
} else {
|
||||||
|
drop(lock);
|
||||||
|
let mut lock = self.0.write().unwrap();
|
||||||
|
let key = lock.1.len();
|
||||||
|
lock.0.insert(domain_r, key).unwrap();
|
||||||
|
lock.1.push((RwLock::new(val), Mutex::new(())));
|
||||||
|
drop(lock);
|
||||||
|
finish(
|
||||||
|
self.0
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.get(key)
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
|
.write()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: FromStr> IpCache<T> {
|
||||||
|
fn load(&mut self, dir: &Path) -> Result<(), io::Error> {
|
||||||
|
std::fs::create_dir_all(dir)?;
|
||||||
|
let mut lock = self.0.write().unwrap();
|
||||||
|
assert!(lock.1.is_empty());
|
||||||
|
let domains = std::fs::read_dir("/var/lib/unbound/domains4/")?;
|
||||||
|
for entry in domains.filter_map(|x| x.ok()) {
|
||||||
|
let domain = entry.file_name();
|
||||||
|
let Some(domain) = domain.to_str() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(age) = entry
|
||||||
|
.metadata()
|
||||||
|
.and_then(|x| x.modified())
|
||||||
|
.ok()
|
||||||
|
.and_then(|x| SystemTime::now().duration_since(x).ok())
|
||||||
|
{
|
||||||
|
if age > Duration::from_secs(60 * 60 * 24 * 7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let domain_r = IpCacheKey(
|
||||||
|
domain
|
||||||
|
.split('.')
|
||||||
|
.rev()
|
||||||
|
.map(|x| x.as_bytes())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(&b"."[..])
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
let key = lock.1.len();
|
||||||
|
lock.0.insert(domain_r, key).unwrap();
|
||||||
|
let Ok(reader) = std::fs::File::open(entry.path()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let mut reader = BufReader::new(reader);
|
||||||
|
let mut line = String::new();
|
||||||
|
let mut ips = SmallVec::new();
|
||||||
|
while reader.read_line(&mut line).is_ok() {
|
||||||
|
let trimmed = line.trim();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ips.extend(T::from_str(trimmed));
|
||||||
|
line.clear();
|
||||||
|
}
|
||||||
|
lock.1.push((RwLock::new(ips), Mutex::new(())));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NftData {
|
||||||
|
ips4: iptrie::Ipv4RTrieSet,
|
||||||
|
ips6: iptrie::Ipv6RTrieSet,
|
||||||
|
name4: CString,
|
||||||
|
name6: CString,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NftQuery {
|
||||||
|
domains: RwLock<prefix_tree::PrefixSet<DomainSeg>>,
|
||||||
|
dynamic: bool,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExampleMod {
|
||||||
|
fn report(&self, code: &str, err: impl Display) {
|
||||||
|
if let Ok(mut file) = std::fs::OpenOptions::new()
|
||||||
|
.append(true)
|
||||||
|
.open("/var/lib/unbound/error.log")
|
||||||
|
{
|
||||||
|
let _lock = self.error_lock.lock();
|
||||||
|
if file.write_all(code.as_bytes()).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if file.write_all(b": ").is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if file.write_all(err.to_string().as_bytes()).is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if file.write_all(b"\n").is_err() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.flush().unwrap_or(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct DpiInfo {
|
||||||
|
domains: Vec<String>,
|
||||||
|
// name: String,
|
||||||
|
// restriction: {"code": "ban"}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnboundMod for ExampleMod {
|
||||||
|
type EnvData = ();
|
||||||
|
type QstateData = ();
|
||||||
|
fn init(_env: &mut crate::unbound::ModuleEnv<Self::EnvData>) -> Result<Self, ()> {
|
||||||
|
let mut ret = Self {
|
||||||
|
nft_token: std::env::var_os("NFT_TOKEN")
|
||||||
|
.map(|x| x.to_str().ok_or(()).map(|s| ".".to_owned() + s))
|
||||||
|
.transpose()?,
|
||||||
|
tmp_nft_token: std::env::var_os("NFT_TOKEN")
|
||||||
|
.map(|x| x.to_str().ok_or(()).map(|s| ".tmp".to_owned() + s))
|
||||||
|
.transpose()?,
|
||||||
|
..Self::default()
|
||||||
|
};
|
||||||
|
if let Some(s) = std::env::var_os("DOMAIN_NAME_OVERRIDES") {
|
||||||
|
for (k, v) in s
|
||||||
|
.to_str()
|
||||||
|
.map(|x| x.to_owned())
|
||||||
|
.ok_or(())?
|
||||||
|
.split(';')
|
||||||
|
.filter_map(|x| x.split_once("->"))
|
||||||
|
{
|
||||||
|
ret.domain_name_overrides
|
||||||
|
.insert(k.as_bytes().into(), v.as_bytes().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut nft_queries = HashMap::new();
|
||||||
|
let mut rulesets = Vec::new();
|
||||||
|
if let Some(s) = std::env::var_os("NFT_QUERIES") {
|
||||||
|
for (i, (name, set4, set6)) in s
|
||||||
|
.to_str()
|
||||||
|
.map(|x| x.to_owned())
|
||||||
|
.ok_or(())?
|
||||||
|
.split(';')
|
||||||
|
.filter_map(|x| x.split_once(':'))
|
||||||
|
.filter_map(|(name, sets)| {
|
||||||
|
sets.split_once(',').map(|(set4, set6)| (name, set4, set6))
|
||||||
|
})
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let (name, dynamic) = if let Some(name) = name.strip_suffix('!') {
|
||||||
|
(name, true)
|
||||||
|
} else {
|
||||||
|
(name, false)
|
||||||
|
};
|
||||||
|
nft_queries.insert(
|
||||||
|
name.to_owned(),
|
||||||
|
NftQuery {
|
||||||
|
domains: RwLock::new(PrefixSet::new()),
|
||||||
|
dynamic,
|
||||||
|
index: i,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
rulesets.push(NftData {
|
||||||
|
ips4: RTrieSet::new(),
|
||||||
|
ips6: RTrieSet::new(),
|
||||||
|
name4: CString::from_vec_with_nul((set4.to_owned() + "\0").into()).unwrap(),
|
||||||
|
name6: CString::from_vec_with_nul((set6.to_owned() + "\0").into()).unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load cached domains
|
||||||
|
if let Err(err) = ret.cache4.load(Path::new("/var/lib/unbound/domains4/")) {
|
||||||
|
ret.report("domains4", err);
|
||||||
|
}
|
||||||
|
if let Err(err) = ret.cache6.load(Path::new("/var/lib/unbound/domains6/")) {
|
||||||
|
ret.report("domains6", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load json files
|
||||||
|
for ((k, v), r) in nft_queries.iter_mut().zip(rulesets.iter_mut()) {
|
||||||
|
for base in ["/etc/unbound", "/var/lib/unbound"] {
|
||||||
|
let mut v_domains = v.domains.write().unwrap();
|
||||||
|
if let Ok(file) = std::fs::File::open(format!("{base}/{k}_domains.json")) {
|
||||||
|
match serde_json::from_reader::<_, Vec<String>>(file) {
|
||||||
|
Ok(domains) => {
|
||||||
|
for domain in domains {
|
||||||
|
v_domains.insert(
|
||||||
|
domain
|
||||||
|
.split('.')
|
||||||
|
.rev()
|
||||||
|
.map(|x| x.as_bytes().into())
|
||||||
|
.collect::<SmallVec<[DomainSeg; 5]>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => ret.report("domains", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(file) = std::fs::File::open(format!("{base}/{k}_dpi.json")) {
|
||||||
|
match serde_json::from_reader::<_, Vec<DpiInfo>>(file) {
|
||||||
|
Ok(dpi_info) => {
|
||||||
|
for domain in dpi_info.iter().flat_map(|x| &x.domains) {
|
||||||
|
v_domains.insert(
|
||||||
|
domain
|
||||||
|
.split('.')
|
||||||
|
.rev()
|
||||||
|
.map(|x| x.as_bytes().into())
|
||||||
|
.collect::<SmallVec<[DomainSeg; 5]>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => ret.report("dpi", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(file) = std::fs::File::open(format!("{base}/{k}_ips.json")) {
|
||||||
|
match serde_json::from_reader::<_, Vec<IpNet>>(file) {
|
||||||
|
Ok(ips) => {
|
||||||
|
r.ips4.extend(ips.iter().filter_map(|x| {
|
||||||
|
if let IpNet::V4(x) = x {
|
||||||
|
Ipv4Prefix::new(x.addr(), x.prefix_len()).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
r.ips6.extend(ips.iter().filter_map(|x| {
|
||||||
|
if let IpNet::V6(x) = x {
|
||||||
|
Ipv6Prefix::new(x.addr(), x.prefix_len()).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Err(err) => ret.report("ips", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for domain in v_domains.iter() {
|
||||||
|
ret.cache4.get_maybe_update_rev(
|
||||||
|
domain
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.as_slice())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(&b"."[..])
|
||||||
|
.into(),
|
||||||
|
|val| {
|
||||||
|
if let Some(val) = val {
|
||||||
|
r.ips4.extend(val.iter().map(|x| Ipv4Prefix::from(*x)));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ret.cache6.get_maybe_update_rev(
|
||||||
|
domain
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.as_slice())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(&b"."[..])
|
||||||
|
.into(),
|
||||||
|
|val| {
|
||||||
|
if let Some(val) = val {
|
||||||
|
r.ips6.extend(val.iter().map(|x| Ipv6Prefix::from(*x)));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add stuff to nftables
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
ret.ruleset_queue = Some(tx);
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let table = nftnl::Table::new(
|
||||||
|
&CString::from_vec_with_nul(b"global\0".to_vec()).unwrap(),
|
||||||
|
nftnl::ProtoFamily::Inet,
|
||||||
|
);
|
||||||
|
let mut first = true;
|
||||||
|
let mut bufs = vec![Vec::<IpNet>::new(); rulesets.len()];
|
||||||
|
let mut len = 0;
|
||||||
|
let mut queue_start = Instant::now();
|
||||||
|
loop {
|
||||||
|
let res = if len == 0 {
|
||||||
|
match rx.recv() {
|
||||||
|
Ok(val) => {
|
||||||
|
queue_start = Instant::now();
|
||||||
|
len += val.1.len();
|
||||||
|
Some(val)
|
||||||
|
}
|
||||||
|
Err(RecvError) => break,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match rx.recv_timeout((queue_start + Duration::from_secs(30)) - Instant::now())
|
||||||
|
{
|
||||||
|
Ok(val) => {
|
||||||
|
len += val.1.len();
|
||||||
|
Some(val)
|
||||||
|
}
|
||||||
|
Err(mpsc::RecvTimeoutError::Timeout) => None,
|
||||||
|
Err(mpsc::RecvTimeoutError::Disconnected) => break,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let do_it = res.is_none()
|
||||||
|
|| len >= 128
|
||||||
|
|| (Instant::now() - queue_start) > Duration::from_secs(25);
|
||||||
|
if let Some((rulesets, ips)) = res {
|
||||||
|
for ruleset in rulesets {
|
||||||
|
bufs[ruleset].extend(ips.iter().copied());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct FlushSetMsg<'a, T> {
|
||||||
|
set: &'a nftnl::set::Set<'a, T>,
|
||||||
|
}
|
||||||
|
unsafe impl<'a, T> nftnl::NlMsg for FlushSetMsg<'a, T> {
|
||||||
|
unsafe fn write(
|
||||||
|
&self,
|
||||||
|
buf: *mut std::ffi::c_void,
|
||||||
|
seq: u32,
|
||||||
|
_msg_type: nftnl::MsgType,
|
||||||
|
) {
|
||||||
|
let header = nftnl_sys::nftnl_nlmsg_build_hdr(
|
||||||
|
buf as *mut c_char,
|
||||||
|
libc::NFT_MSG_DELSETELEM as u16,
|
||||||
|
self.set.get_family() as u16,
|
||||||
|
0,
|
||||||
|
seq,
|
||||||
|
);
|
||||||
|
nftnl_sys::nftnl_set_elems_nlmsg_build_payload(header, self.set.as_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if do_it {
|
||||||
|
let mut batch = nftnl::Batch::new();
|
||||||
|
for (ruleset, buf) in rulesets.iter().zip(bufs.iter_mut()) {
|
||||||
|
// internally represented as a range
|
||||||
|
struct Cidr<T>(T);
|
||||||
|
impl SetKey for Cidr<Ipv4Net> {
|
||||||
|
const TYPE: u32 = Ipv4Addr::TYPE;
|
||||||
|
const LEN: u32 = Ipv4Addr::LEN * 2;
|
||||||
|
fn data(&self) -> Box<[u8]> {
|
||||||
|
let data = u32::from_be_bytes(self.0.network().octets());
|
||||||
|
let mask = u32::from_be_bytes(self.0.netmask().octets());
|
||||||
|
let mut ret = [0u8; (Self::LEN) as usize];
|
||||||
|
ret[..(Self::LEN as usize)]
|
||||||
|
.copy_from_slice(&self.0.network().octets());
|
||||||
|
ret[(Self::LEN as usize)..]
|
||||||
|
.copy_from_slice(&u32::to_be_bytes(!mask | data));
|
||||||
|
Box::new(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl SetKey for Cidr<Ipv6Net> {
|
||||||
|
const TYPE: u32 = Ipv6Addr::TYPE;
|
||||||
|
const LEN: u32 = Ipv6Addr::LEN * 2;
|
||||||
|
fn data(&self) -> Box<[u8]> {
|
||||||
|
let data = u128::from_be_bytes(self.0.network().octets());
|
||||||
|
let mask = u128::from_be_bytes(self.0.netmask().octets());
|
||||||
|
let mut ret = [0u8; (Self::LEN) as usize];
|
||||||
|
ret[..(Self::LEN as usize)]
|
||||||
|
.copy_from_slice(&self.0.network().octets());
|
||||||
|
ret[(Self::LEN as usize)..]
|
||||||
|
.copy_from_slice(&u128::to_be_bytes(!mask | data));
|
||||||
|
Box::new(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let set4 = nftnl::set::Set::<Cidr<Ipv4Net>>::new(
|
||||||
|
&ruleset.name4,
|
||||||
|
0,
|
||||||
|
&table,
|
||||||
|
nftnl::ProtoFamily::Ipv4,
|
||||||
|
);
|
||||||
|
let set6 = nftnl::set::Set::<Cidr<Ipv6Net>>::new(
|
||||||
|
&ruleset.name6,
|
||||||
|
0,
|
||||||
|
&table,
|
||||||
|
nftnl::ProtoFamily::Ipv6,
|
||||||
|
);
|
||||||
|
if first {
|
||||||
|
batch.add(&FlushSetMsg { set: &set4 }, nftnl::MsgType::Del);
|
||||||
|
batch.add(&FlushSetMsg { set: &set6 }, nftnl::MsgType::Del);
|
||||||
|
}
|
||||||
|
let mut set4 = nftnl::set::Set::new(
|
||||||
|
&ruleset.name4,
|
||||||
|
0,
|
||||||
|
&table,
|
||||||
|
nftnl::ProtoFamily::Ipv4,
|
||||||
|
);
|
||||||
|
let mut set6 = nftnl::set::Set::new(
|
||||||
|
&ruleset.name6,
|
||||||
|
0,
|
||||||
|
&table,
|
||||||
|
nftnl::ProtoFamily::Ipv6,
|
||||||
|
);
|
||||||
|
let mut added4 = false;
|
||||||
|
let mut added6 = false;
|
||||||
|
for ip in buf.drain(..) {
|
||||||
|
match ip {
|
||||||
|
IpNet::V4(ip) => {
|
||||||
|
set4.add(&Cidr(ip));
|
||||||
|
added4 = true;
|
||||||
|
}
|
||||||
|
IpNet::V6(ip) => {
|
||||||
|
set6.add(&Cidr(ip));
|
||||||
|
added6 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if added4 {
|
||||||
|
batch.add_iter(set4.elems_iter(), nftnl::MsgType::Add);
|
||||||
|
}
|
||||||
|
if added6 {
|
||||||
|
batch.add_iter(set6.elems_iter(), nftnl::MsgType::Add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len = 0;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn operate(
|
||||||
|
&self,
|
||||||
|
qstate: &mut crate::unbound::ModuleQstate<Self::QstateData>,
|
||||||
|
_event: crate::unbound::ModuleEvent,
|
||||||
|
_entry: &mut crate::unbound::OutboundEntryMut,
|
||||||
|
) {
|
||||||
|
let info = qstate.qinfo_mut();
|
||||||
|
let name = info.qname().to_bytes();
|
||||||
|
let rev_domain = name.strip_suffix(b".").unwrap_or(name);
|
||||||
|
if let Some(rev_domain) = self
|
||||||
|
.nft_token
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|token| rev_domain.strip_suffix(token.as_bytes()))
|
||||||
|
{
|
||||||
|
for (qname, query) in self.nft_queries.iter() {
|
||||||
|
if query.dynamic && rev_domain.ends_with(qname.as_bytes()) {
|
||||||
|
if let Some(rev_domain) =
|
||||||
|
rev_domain.strip_suffix((".".to_owned() + qname).as_bytes())
|
||||||
|
{
|
||||||
|
let rev_domain = rev_domain
|
||||||
|
.split(|x| *x == b'.')
|
||||||
|
.map(|x| x.into())
|
||||||
|
.collect::<SmallVec<[_; 5]>>();
|
||||||
|
let mut domains = query.domains.write().unwrap();
|
||||||
|
if domains.insert(rev_domain.clone()) {
|
||||||
|
drop(domains);
|
||||||
|
let file_name = format!("/var/lib/unbound/{qname}_domains.json");
|
||||||
|
let domain = match String::from_utf8(
|
||||||
|
rev_domain
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|x| x.as_slice())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(&b"."[..]),
|
||||||
|
) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(err) => {
|
||||||
|
self.report("domain utf-8", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _lock = self.domains_write_lock.lock().unwrap();
|
||||||
|
let mut old: Vec<String> = if let Ok(file) = File::open(&file_name) {
|
||||||
|
match serde_json::from_reader(file) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(err) => {
|
||||||
|
self.report("domains json", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
old.push(domain);
|
||||||
|
match File::create(file_name) {
|
||||||
|
Ok(file) => {
|
||||||
|
if let Err(err) = serde_json::to_writer(file, &old) {
|
||||||
|
self.report("domains write", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => self.report("domains create", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if let Some(rev_domain) = self
|
||||||
|
.tmp_nft_token
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|token| rev_domain.strip_suffix(token.as_bytes()))
|
||||||
|
{
|
||||||
|
for (qname, query) in self.nft_queries.iter() {
|
||||||
|
if query.dynamic && rev_domain.ends_with(qname.as_bytes()) {
|
||||||
|
if let Some(rev_domain) =
|
||||||
|
rev_domain.strip_suffix((".".to_owned() + qname).as_bytes())
|
||||||
|
{
|
||||||
|
let rev_domain = rev_domain
|
||||||
|
.split(|x| *x == b'.')
|
||||||
|
.map(|x| x.into())
|
||||||
|
.collect::<SmallVec<[_; 5]>>();
|
||||||
|
let mut domains = query.domains.write().unwrap();
|
||||||
|
domains.insert(rev_domain.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let split_rev_domain = rev_domain
|
||||||
|
.split(|x| *x == b'.')
|
||||||
|
.map(|x| x.into())
|
||||||
|
.collect::<SmallVec<[_; 5]>>();
|
||||||
|
let mut qnames: SmallVec<[usize; 5]> = SmallVec::new();
|
||||||
|
for query in self.nft_queries.values() {
|
||||||
|
if query.domains.read().unwrap().contains(&split_rev_domain) {
|
||||||
|
qnames.push(query.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if qnames.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(ret) = qstate.return_msg_mut() {
|
||||||
|
if let Some(rep) = ret.rep() {
|
||||||
|
let mut ip4: SmallVec<[Ipv4Addr; 4]> = SmallVec::new();
|
||||||
|
let mut ip6: SmallVec<[Ipv6Addr; 4]> = SmallVec::new();
|
||||||
|
for rrset in rep.rrsets() {
|
||||||
|
let entry = rrset.entry();
|
||||||
|
let d = entry.data();
|
||||||
|
let rk = rrset.rk();
|
||||||
|
// IN
|
||||||
|
if rk.rrset_class() != rr_class::IN {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (data, _ttl) in d.rr_data() {
|
||||||
|
match rk.type_() {
|
||||||
|
rr_type::A if data.len() == 2 + 4 && &data[..2] == b"\0\x04" => {
|
||||||
|
ip4.push(Ipv4Addr::from(
|
||||||
|
<[u8; 4]>::try_from(&data[2..2 + 4]).unwrap(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
rr_type::AAAA if data.len() == 2 + 16 && &data[..2] == b"\0\x10" => {
|
||||||
|
ip6.push(Ipv6Addr::from(
|
||||||
|
<[u8; 16]>::try_from(&data[2..2 + 16]).unwrap(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ip4.is_empty() || !ip6.is_empty() {
|
||||||
|
let domain = match String::from_utf8(
|
||||||
|
split_rev_domain
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.map(|x| x.as_slice())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(&b"."[..]),
|
||||||
|
) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(err) => {
|
||||||
|
self.report("domain utf-8", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut split_rev_domain = split_rev_domain.into_iter();
|
||||||
|
if let Some(first) = split_rev_domain.next() {
|
||||||
|
let first: Domain = first.to_vec().into();
|
||||||
|
let joined_rev_domain =
|
||||||
|
split_rev_domain.fold(first, |mut res, mut next| {
|
||||||
|
res.push(b'.');
|
||||||
|
res.append(&mut next);
|
||||||
|
res
|
||||||
|
});
|
||||||
|
let mut to_send: SmallVec<[IpNet; 8]> = SmallVec::new();
|
||||||
|
to_send.extend(ip4.iter().copied().map(Ipv4Net::from).map(IpNet::from));
|
||||||
|
to_send.extend(ip6.iter().copied().map(Ipv6Net::from).map(IpNet::from));
|
||||||
|
let keep4 = !ip4.is_empty()
|
||||||
|
&& self
|
||||||
|
.cache4
|
||||||
|
.set(&domain, IpCacheKey(joined_rev_domain.clone()), ip4);
|
||||||
|
let keep6 = !ip6.is_empty()
|
||||||
|
&& self
|
||||||
|
.cache6
|
||||||
|
.set(&domain, IpCacheKey(joined_rev_domain.clone()), ip6);
|
||||||
|
to_send
|
||||||
|
.retain(|x| x.addr().is_ipv4() && keep4 || x.addr().is_ipv6() && keep6);
|
||||||
|
if !to_send.is_empty() {
|
||||||
|
self.ruleset_queue
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.send((qnames, to_send))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[ctor]
|
||||||
|
fn setup() {
|
||||||
|
crate::set_unbound_mod::<ExampleMod>();
|
||||||
|
}
|
100
src/exports.rs
Normal file
100
src/exports.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use crate::bindings::{module_env, module_ev, module_qstate, outbound_entry};
|
||||||
|
|
||||||
|
/// Initialize module internals, like database etc.
|
||||||
|
/// Called just once on module load.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `env` - module environment
|
||||||
|
/// - `id` - module identifier
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// Returns 1 or 0 (success or failure)
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn init(
|
||||||
|
env: *mut module_env,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
) -> ::std::os::raw::c_int {
|
||||||
|
if let Some(fac) = crate::MODULE_FACTORY.take() {
|
||||||
|
fac(env, id)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deinitialize module internals.
|
||||||
|
/// Called just once on module unload.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn deinit(env: *mut module_env, id: ::std::os::raw::c_int) {
|
||||||
|
if let Some(module) = crate::MODULE.take() {
|
||||||
|
module.internal_deinit(env, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform action on pending query. Accepts a new query, or work on pending query.
|
||||||
|
/// You have to set qstate.ext_state on exit.
|
||||||
|
/// The state informs unbound about result and controls the following states.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `qstate` - query state structure
|
||||||
|
/// - `event` - event type
|
||||||
|
/// - `id` - module identifier
|
||||||
|
/// - `entry` - outbound list entry (only used by the iterator module in unbound)
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn operate(
|
||||||
|
qstate: *mut module_qstate,
|
||||||
|
event: module_ev,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
entry: *mut outbound_entry,
|
||||||
|
) {
|
||||||
|
if let Some(module) = crate::module() {
|
||||||
|
module.internal_operate(qstate, event, id, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inform super querystate about the results from this subquerystate.
|
||||||
|
/// Is called when the querystate is finished.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// - `qstate` - query state
|
||||||
|
/// - `id` - module identifier
|
||||||
|
/// - `super_qstate` - mesh state
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn inform_super(
|
||||||
|
qstate: *mut module_qstate,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
super_qstate: *mut module_qstate,
|
||||||
|
) {
|
||||||
|
if let Some(module) = crate::module() {
|
||||||
|
module.internal_inform_super(qstate, id, super_qstate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear is called once a query is complete and the response has been sent
|
||||||
|
/// back. It is used to clear up any per-query allocations.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn clear(qstate: *mut module_qstate, id: ::std::os::raw::c_int) {
|
||||||
|
if let Some(module) = crate::module() {
|
||||||
|
module.internal_clear(qstate, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get mem is called when Unbound is printing performance information. This
|
||||||
|
/// only happens explicitly and is only used to show memory usage to the user.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn get_mem(env: *mut module_env, id: ::std::os::raw::c_int) -> usize {
|
||||||
|
crate::module()
|
||||||
|
.map(|module| module.internal_get_mem(env, id))
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// function interface assertions
|
||||||
|
const _INIT: crate::bindings::func_init_t = Some(init);
|
||||||
|
const _DEINIT: crate::bindings::func_deinit_t = Some(deinit);
|
||||||
|
const _OPERATE: crate::bindings::func_operate_t = Some(operate);
|
||||||
|
const _INFORM: crate::bindings::func_inform_t = Some(inform_super);
|
||||||
|
const _CLEAR: crate::bindings::func_clear_t = Some(clear);
|
||||||
|
const _GET_MEM: crate::bindings::func_get_mem_t = Some(get_mem);
|
187
src/lib.rs
Normal file
187
src/lib.rs
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||||
|
#[allow(
|
||||||
|
dead_code,
|
||||||
|
improper_ctypes,
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
non_upper_case_globals,
|
||||||
|
unused_imports
|
||||||
|
)]
|
||||||
|
mod bindings;
|
||||||
|
#[cfg(feature = "example")]
|
||||||
|
mod example;
|
||||||
|
mod exports;
|
||||||
|
mod unbound;
|
||||||
|
|
||||||
|
pub fn add(left: usize, right: usize) -> usize {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UnboundMod: Send + Sync + Sized + RefUnwindSafe + UnwindSafe {
|
||||||
|
type EnvData;
|
||||||
|
type QstateData;
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
|
fn init(_env: &mut unbound::ModuleEnv<Self::EnvData>) -> Result<Self, ()> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
fn deinit(self, _env: &mut unbound::ModuleEnv<Self::EnvData>) {}
|
||||||
|
fn operate(
|
||||||
|
&self,
|
||||||
|
_qstate: &mut unbound::ModuleQstate<Self::QstateData>,
|
||||||
|
_event: unbound::ModuleEvent,
|
||||||
|
_entry: &mut unbound::OutboundEntryMut,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
fn inform_super(
|
||||||
|
&self,
|
||||||
|
_qstate: &mut unbound::ModuleQstate<Self::QstateData>,
|
||||||
|
_super_qstate: &mut unbound::ModuleQstate<::std::ffi::c_void>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
fn clear(&self, _qstate: &mut unbound::ModuleQstate<Self::QstateData>) {}
|
||||||
|
|
||||||
|
fn get_mem(&self, _env: &mut unbound::ModuleEnv<Self::EnvData>) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Be safe
|
||||||
|
unsafe trait SealedUnboundMod: Send + Sync {
|
||||||
|
unsafe fn internal_deinit(
|
||||||
|
self: Box<Self>,
|
||||||
|
env: *mut bindings::module_env,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
);
|
||||||
|
unsafe fn internal_operate(
|
||||||
|
&self,
|
||||||
|
qstate: *mut bindings::module_qstate,
|
||||||
|
event: bindings::module_ev,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
entry: *mut bindings::outbound_entry,
|
||||||
|
);
|
||||||
|
unsafe fn internal_inform_super(
|
||||||
|
&self,
|
||||||
|
qstate: *mut bindings::module_qstate,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
super_qstate: *mut bindings::module_qstate,
|
||||||
|
);
|
||||||
|
unsafe fn internal_clear(
|
||||||
|
&self,
|
||||||
|
qstate: *mut bindings::module_qstate,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
);
|
||||||
|
unsafe fn internal_get_mem(
|
||||||
|
&self,
|
||||||
|
env: *mut bindings::module_env,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
) -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: UnboundMod> SealedUnboundMod for T {
|
||||||
|
unsafe fn internal_deinit(
|
||||||
|
self: Box<Self>,
|
||||||
|
env: *mut bindings::module_env,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
) {
|
||||||
|
std::panic::catch_unwind(|| {
|
||||||
|
self.deinit(&mut unbound::ModuleEnv(env, id, Default::default()))
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
}
|
||||||
|
unsafe fn internal_operate(
|
||||||
|
&self,
|
||||||
|
qstate: *mut bindings::module_qstate,
|
||||||
|
event: bindings::module_ev,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
entry: *mut bindings::outbound_entry,
|
||||||
|
) {
|
||||||
|
std::panic::catch_unwind(|| {
|
||||||
|
self.operate(
|
||||||
|
&mut unbound::ModuleQstate(qstate, id, Default::default()),
|
||||||
|
event.into(),
|
||||||
|
&mut unbound::OutboundEntryMut(entry, Default::default()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
}
|
||||||
|
unsafe fn internal_inform_super(
|
||||||
|
&self,
|
||||||
|
qstate: *mut bindings::module_qstate,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
super_qstate: *mut bindings::module_qstate,
|
||||||
|
) {
|
||||||
|
std::panic::catch_unwind(|| {
|
||||||
|
self.inform_super(
|
||||||
|
&mut unbound::ModuleQstate(qstate, id, Default::default()),
|
||||||
|
&mut unbound::ModuleQstate(super_qstate, -1, Default::default()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
}
|
||||||
|
unsafe fn internal_clear(
|
||||||
|
&self,
|
||||||
|
qstate: *mut bindings::module_qstate,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
) {
|
||||||
|
std::panic::catch_unwind(|| {
|
||||||
|
self.clear(&mut unbound::ModuleQstate(qstate, id, Default::default()))
|
||||||
|
})
|
||||||
|
.unwrap_or(());
|
||||||
|
}
|
||||||
|
unsafe fn internal_get_mem(
|
||||||
|
&self,
|
||||||
|
env: *mut bindings::module_env,
|
||||||
|
id: ::std::os::raw::c_int,
|
||||||
|
) -> usize {
|
||||||
|
std::panic::catch_unwind(|| {
|
||||||
|
self.get_mem(&mut unbound::ModuleEnv(env, id, Default::default()))
|
||||||
|
})
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut MODULE: std::sync::OnceLock<Box<dyn SealedUnboundMod>> = std::sync::OnceLock::new();
|
||||||
|
unsafe fn module() -> Option<&'static dyn SealedUnboundMod> {
|
||||||
|
MODULE.get().map(|x| &**x)
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut MODULE_FACTORY: std::sync::OnceLock<
|
||||||
|
Box<
|
||||||
|
dyn Sync
|
||||||
|
+ Send
|
||||||
|
+ FnOnce(*mut bindings::module_env, ::std::os::raw::c_int) -> ::std::os::raw::c_int,
|
||||||
|
>,
|
||||||
|
> = std::sync::OnceLock::new();
|
||||||
|
pub fn set_unbound_mod<T: 'static + UnboundMod>() {
|
||||||
|
unsafe {
|
||||||
|
MODULE_FACTORY
|
||||||
|
.set(Box::new(|env, id| {
|
||||||
|
std::panic::catch_unwind(|| {
|
||||||
|
if let Ok(module) =
|
||||||
|
T::init(&mut unbound::ModuleEnv(env, id, Default::default()))
|
||||||
|
{
|
||||||
|
MODULE.set(Box::new(module)).map_err(|_| ()).unwrap();
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or(0)
|
||||||
|
}))
|
||||||
|
.map_err(|_| "set_unbound_mod failed")
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
767
src/unbound.rs
Normal file
767
src/unbound.rs
Normal file
|
@ -0,0 +1,767 @@
|
||||||
|
#![allow(dead_code)]
|
||||||
|
use crate::bindings::{
|
||||||
|
config_file, dns_msg, in6_addr, in6_addr__bindgen_ty_1, in_addr, infra_cache, key_cache,
|
||||||
|
lruhash_entry, module_env, module_ev, module_qstate, outbound_entry, packed_rrset_data,
|
||||||
|
packed_rrset_key, query_info, reply_info, rrset_cache, rrset_id_type, rrset_trust, sec_status,
|
||||||
|
slabhash, sldns_enum_ede_code, sockaddr_in, sockaddr_in6, sockaddr_storage,
|
||||||
|
ub_packed_rrset_key, AF_INET, AF_INET6,
|
||||||
|
};
|
||||||
|
use std::{ffi::CStr, marker::PhantomData, net::SocketAddr, os::raw::c_char, ptr, time::Duration};
|
||||||
|
|
||||||
|
pub struct ConfigFileMut<'a>(
|
||||||
|
pub(crate) *mut config_file,
|
||||||
|
PhantomData<&'a mut config_file>,
|
||||||
|
);
|
||||||
|
pub struct SlabHashMut<'a>(pub(crate) *mut slabhash, PhantomData<&'a mut slabhash>);
|
||||||
|
pub struct RrsetCacheMut<'a>(
|
||||||
|
pub(crate) *mut rrset_cache,
|
||||||
|
PhantomData<&'a mut rrset_cache>,
|
||||||
|
);
|
||||||
|
pub struct InfraCacheMut<'a>(
|
||||||
|
pub(crate) *mut infra_cache,
|
||||||
|
PhantomData<&'a mut infra_cache>,
|
||||||
|
);
|
||||||
|
pub struct KeyCacheMut<'a>(pub(crate) *mut key_cache, PhantomData<&'a mut key_cache>);
|
||||||
|
pub struct ModuleEnv<T>(
|
||||||
|
pub(crate) *mut module_env,
|
||||||
|
pub(crate) std::ffi::c_int,
|
||||||
|
pub(crate) PhantomData<T>,
|
||||||
|
);
|
||||||
|
pub struct ModuleQstate<'a, T>(
|
||||||
|
pub(crate) *mut module_qstate,
|
||||||
|
pub(crate) std::ffi::c_int,
|
||||||
|
pub(crate) PhantomData<&'a mut T>,
|
||||||
|
);
|
||||||
|
pub struct OutboundEntryMut<'a>(
|
||||||
|
pub(crate) *mut outbound_entry,
|
||||||
|
pub(crate) PhantomData<&'a mut outbound_entry>,
|
||||||
|
);
|
||||||
|
pub struct QueryInfoMut<'a>(
|
||||||
|
pub(crate) *mut query_info,
|
||||||
|
pub(crate) PhantomData<&'a mut query_info>,
|
||||||
|
);
|
||||||
|
pub struct DnsMsgMut<'a>(
|
||||||
|
pub(crate) *mut dns_msg,
|
||||||
|
pub(crate) PhantomData<&'a mut dns_msg>,
|
||||||
|
);
|
||||||
|
pub struct ReplyInfo<'a>(
|
||||||
|
pub(crate) *mut reply_info,
|
||||||
|
pub(crate) PhantomData<&'a mut reply_info>,
|
||||||
|
);
|
||||||
|
pub struct UbPackedRrsetKey<'a>(
|
||||||
|
pub(crate) *mut ub_packed_rrset_key,
|
||||||
|
pub(crate) PhantomData<&'a mut ub_packed_rrset_key>,
|
||||||
|
);
|
||||||
|
pub struct LruHashEntry<'a>(
|
||||||
|
pub(crate) *mut lruhash_entry,
|
||||||
|
pub(crate) PhantomData<&'a mut lruhash_entry>,
|
||||||
|
);
|
||||||
|
pub struct PackedRrsetKey<'a>(
|
||||||
|
pub(crate) *mut packed_rrset_key,
|
||||||
|
pub(crate) PhantomData<&'a mut packed_rrset_key>,
|
||||||
|
);
|
||||||
|
pub struct PackedRrsetData<'a>(
|
||||||
|
pub(crate) *mut packed_rrset_data,
|
||||||
|
pub(crate) PhantomData<&'a mut packed_rrset_data>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<'a> QueryInfoMut<'a> {
|
||||||
|
pub fn qname(&self) -> &CStr {
|
||||||
|
unsafe { CStr::from_ptr((*self.0).qname as *const c_char) }
|
||||||
|
}
|
||||||
|
pub fn qtype(&self) -> u16 {
|
||||||
|
unsafe { (*self.0).qtype }
|
||||||
|
}
|
||||||
|
pub fn qclass(&self) -> u16 {
|
||||||
|
unsafe { (*self.0).qclass }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ModuleEnv<T> {
|
||||||
|
pub fn config_file_mut(&mut self) -> ConfigFileMut<'_> {
|
||||||
|
ConfigFileMut(unsafe { (*self.0).cfg }, Default::default())
|
||||||
|
}
|
||||||
|
pub fn msg_cache_mut(&mut self) -> SlabHashMut<'_> {
|
||||||
|
SlabHashMut(unsafe { (*self.0).msg_cache }, Default::default())
|
||||||
|
}
|
||||||
|
pub fn rrset_cache_mut(&mut self) -> RrsetCacheMut<'_> {
|
||||||
|
RrsetCacheMut(unsafe { (*self.0).rrset_cache }, Default::default())
|
||||||
|
}
|
||||||
|
pub fn infra_cache_mut(&mut self) -> InfraCacheMut<'_> {
|
||||||
|
InfraCacheMut(unsafe { (*self.0).infra_cache }, Default::default())
|
||||||
|
}
|
||||||
|
pub fn key_cache_mut(&mut self) -> KeyCacheMut<'_> {
|
||||||
|
KeyCacheMut(unsafe { (*self.0).key_cache }, Default::default())
|
||||||
|
}
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn send_query<Y>(
|
||||||
|
&mut self,
|
||||||
|
qinfo: &QueryInfoMut,
|
||||||
|
flags: u16,
|
||||||
|
dnssec: u32,
|
||||||
|
want_dnssec: bool,
|
||||||
|
nocaps: bool,
|
||||||
|
check_ratelimit: bool,
|
||||||
|
addr: SocketAddr,
|
||||||
|
zone: &[u8],
|
||||||
|
tcp_upstream: bool,
|
||||||
|
ssl_upstream: bool,
|
||||||
|
tls_auth_name: Option<&CStr>,
|
||||||
|
q: &mut ModuleQstate<Y>,
|
||||||
|
) -> (Option<OutboundEntryMut<'_>>, bool) {
|
||||||
|
let mut was_ratelimited = 0;
|
||||||
|
let ret = unsafe {
|
||||||
|
let mut addr4 = sockaddr_in {
|
||||||
|
sin_port: 0,
|
||||||
|
sin_addr: in_addr { s_addr: 0 },
|
||||||
|
sin_zero: [0u8; 8],
|
||||||
|
sin_family: AF_INET as u16,
|
||||||
|
};
|
||||||
|
let mut addr6 = sockaddr_in6 {
|
||||||
|
sin6_port: 0,
|
||||||
|
sin6_addr: in6_addr {
|
||||||
|
__in6_u: in6_addr__bindgen_ty_1 {
|
||||||
|
__u6_addr8: [0u8; 16],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sin6_family: AF_INET6 as u16,
|
||||||
|
sin6_flowinfo: 0,
|
||||||
|
sin6_scope_id: 0,
|
||||||
|
};
|
||||||
|
let (addr, addr_len) = match addr {
|
||||||
|
SocketAddr::V4(x) => {
|
||||||
|
addr4.sin_port = x.port();
|
||||||
|
addr4.sin_addr.s_addr = (*x.ip()).into();
|
||||||
|
(
|
||||||
|
&addr4 as *const _ as *const sockaddr_storage,
|
||||||
|
std::mem::size_of_val(&addr4),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SocketAddr::V6(x) => {
|
||||||
|
addr6.sin6_addr.__in6_u.__u6_addr8 = x.ip().octets();
|
||||||
|
addr6.sin6_flowinfo = x.flowinfo();
|
||||||
|
addr6.sin6_scope_id = x.scope_id();
|
||||||
|
(
|
||||||
|
&addr6 as *const _ as *const sockaddr_storage,
|
||||||
|
std::mem::size_of_val(&addr6),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
((*self.0).send_query.unwrap_unchecked())(
|
||||||
|
&qinfo.0 as *const _ as *mut _,
|
||||||
|
flags,
|
||||||
|
dnssec as i32,
|
||||||
|
want_dnssec.into(),
|
||||||
|
nocaps.into(),
|
||||||
|
check_ratelimit.into(),
|
||||||
|
addr as *mut _,
|
||||||
|
addr_len as u32,
|
||||||
|
zone.as_ptr() as *mut _,
|
||||||
|
zone.len(),
|
||||||
|
tcp_upstream.into(),
|
||||||
|
ssl_upstream.into(),
|
||||||
|
tls_auth_name
|
||||||
|
.map(|x| x.as_ptr() as *mut _)
|
||||||
|
.unwrap_or(ptr::null_mut()),
|
||||||
|
q.0,
|
||||||
|
&mut was_ratelimited as *mut _,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ret.is_null() {
|
||||||
|
(None, was_ratelimited != 0)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
Some(OutboundEntryMut(ret, Default::default())),
|
||||||
|
was_ratelimited != 0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn detach_subs<Y>(&mut self, qstate: &mut ModuleQstate<Y>) {
|
||||||
|
unsafe { (*self.0).detach_subs.unwrap_unchecked()(qstate.0) }
|
||||||
|
}
|
||||||
|
unsafe fn attach_sub<Y>(
|
||||||
|
&mut self,
|
||||||
|
qstate: &mut ModuleQstate<Y>,
|
||||||
|
qinfo: &QueryInfoMut,
|
||||||
|
qflags: u16,
|
||||||
|
prime: bool,
|
||||||
|
valrec: bool,
|
||||||
|
init_sub: impl FnOnce(*mut module_qstate) -> Result<(), ()>,
|
||||||
|
) -> Result<Option<ModuleQstate<'_, ()>>, ()> {
|
||||||
|
let mut newq: *mut module_qstate = ptr::null_mut();
|
||||||
|
let res = unsafe {
|
||||||
|
((*self.0).attach_sub.unwrap_unchecked())(
|
||||||
|
qstate.0,
|
||||||
|
&qinfo.0 as *const _ as *mut _,
|
||||||
|
qflags,
|
||||||
|
prime.into(),
|
||||||
|
valrec.into(),
|
||||||
|
&mut newq as _,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if res != 0 {
|
||||||
|
Ok(if newq.is_null() {
|
||||||
|
None
|
||||||
|
} else if init_sub(newq).is_ok() {
|
||||||
|
Some(ModuleQstate(newq, qstate.1, Default::default()))
|
||||||
|
} else {
|
||||||
|
unsafe { ((*self.0).kill_sub.unwrap_unchecked())(newq) }
|
||||||
|
return Err(());
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add_sub: TODO similar to above
|
||||||
|
// detect_cycle: TODO
|
||||||
|
// (note that &mut T is wrapped in dynmod stuff)
|
||||||
|
// fn modinfo_mut(&mut self) -> Option<&mut T> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ModuleQstate<'_, T> {
|
||||||
|
pub fn qinfo_mut(&mut self) -> QueryInfoMut<'_> {
|
||||||
|
QueryInfoMut(
|
||||||
|
unsafe { &mut (*self.0).qinfo as *mut query_info },
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn return_msg_mut(&mut self) -> Option<DnsMsgMut<'_>> {
|
||||||
|
if unsafe { (*self.0).return_msg.is_null() } {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(DnsMsgMut(
|
||||||
|
unsafe { (*self.0).return_msg },
|
||||||
|
Default::default(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsMsgMut<'_> {
|
||||||
|
pub fn rep(&self) -> Option<ReplyInfo<'_>> {
|
||||||
|
if unsafe { (*self.0).rep.is_null() } {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ReplyInfo(unsafe { (*self.0).rep }, Default::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReplyInfo<'_> {
|
||||||
|
pub fn flags(&self) -> u16 {
|
||||||
|
unsafe { (*self.0).flags }
|
||||||
|
}
|
||||||
|
pub fn authoritative(&self) -> bool {
|
||||||
|
unsafe { (*self.0).authoritative != 0 }
|
||||||
|
}
|
||||||
|
pub fn qdcount(&self) -> u8 {
|
||||||
|
unsafe { (*self.0).qdcount }
|
||||||
|
}
|
||||||
|
pub fn padding(&self) -> u32 {
|
||||||
|
unsafe { (*self.0).padding }
|
||||||
|
}
|
||||||
|
pub fn ttl(&self) -> Option<Duration> {
|
||||||
|
(unsafe { (*self.0).ttl })
|
||||||
|
.try_into()
|
||||||
|
.map(Duration::from_secs)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
pub fn prefetch_ttl(&self) -> Option<Duration> {
|
||||||
|
(unsafe { (*self.0).prefetch_ttl })
|
||||||
|
.try_into()
|
||||||
|
.map(Duration::from_secs)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
pub fn serve_expired_ttl(&self) -> Option<Duration> {
|
||||||
|
(unsafe { (*self.0).serve_expired_ttl })
|
||||||
|
.try_into()
|
||||||
|
.map(Duration::from_secs)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
pub fn security(&self) -> SecStatus {
|
||||||
|
SecStatus::from(unsafe { (*self.0).security })
|
||||||
|
}
|
||||||
|
pub fn reason_bogus(&self) -> SldnsEdeCode {
|
||||||
|
SldnsEdeCode::from(unsafe { (*self.0).reason_bogus })
|
||||||
|
}
|
||||||
|
pub fn reason_bogus_str(&self) -> Option<&CStr> {
|
||||||
|
if unsafe { (*self.0).reason_bogus_str.is_null() } {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(unsafe { CStr::from_ptr((*self.0).reason_bogus_str) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn an_numrrsets(&self) -> usize {
|
||||||
|
unsafe { (*self.0).an_numrrsets }
|
||||||
|
}
|
||||||
|
pub fn ns_numrrsets(&self) -> usize {
|
||||||
|
unsafe { (*self.0).ns_numrrsets }
|
||||||
|
}
|
||||||
|
pub fn ar_numrrsets(&self) -> usize {
|
||||||
|
unsafe { (*self.0).ar_numrrsets }
|
||||||
|
}
|
||||||
|
pub fn rrset_count(&self) -> usize {
|
||||||
|
unsafe { (*self.0).rrset_count }
|
||||||
|
}
|
||||||
|
pub fn rrsets(&self) -> impl '_ + Iterator<Item = UbPackedRrsetKey<'_>> {
|
||||||
|
let total = self.rrset_count();
|
||||||
|
let rrsets = unsafe { (*self.0).rrsets };
|
||||||
|
(0..total).map(move |i| UbPackedRrsetKey(unsafe { *rrsets.add(i) }, Default::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UbPackedRrsetKey<'_> {
|
||||||
|
pub fn entry(&self) -> LruHashEntry<'_> {
|
||||||
|
LruHashEntry(
|
||||||
|
unsafe { &mut (*self.0).entry as *mut _ },
|
||||||
|
Default::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn id(&self) -> RrsetIdType {
|
||||||
|
unsafe { (*self.0).id }
|
||||||
|
}
|
||||||
|
pub fn rk(&self) -> PackedRrsetKey<'_> {
|
||||||
|
PackedRrsetKey(unsafe { &mut (*self.0).rk as *mut _ }, Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackedRrsetKey<'_> {
|
||||||
|
pub fn dname(&self) -> Option<&'_ CStr> {
|
||||||
|
if unsafe { (*self.0).dname.is_null() } {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(unsafe { CStr::from_ptr((*self.0).dname as *const c_char) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn flags(&self) -> u32 {
|
||||||
|
unsafe { (*self.0).flags }
|
||||||
|
}
|
||||||
|
pub fn type_(&self) -> u16 {
|
||||||
|
u16::from_be(unsafe { (*self.0).type_ })
|
||||||
|
}
|
||||||
|
pub fn rrset_class(&self) -> u16 {
|
||||||
|
u16::from_be(unsafe { (*self.0).rrset_class })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LruHashEntry<'_> {
|
||||||
|
pub fn data(&self) -> PackedRrsetData<'_> {
|
||||||
|
// FIXME: shouldnt pthread lock be used here?
|
||||||
|
unsafe { PackedRrsetData((*self.0).data as *mut packed_rrset_data, Default::default()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackedRrsetData<'_> {
|
||||||
|
pub fn ttl_add(&self) -> Option<Duration> {
|
||||||
|
(unsafe { (*self.0).ttl_add })
|
||||||
|
.try_into()
|
||||||
|
.map(Duration::from_secs)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
pub fn ttl(&self) -> Option<Duration> {
|
||||||
|
(unsafe { (*self.0).ttl })
|
||||||
|
.try_into()
|
||||||
|
.map(Duration::from_secs)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
pub fn count(&self) -> usize {
|
||||||
|
unsafe { (*self.0).count }
|
||||||
|
}
|
||||||
|
pub fn rrsig_count(&self) -> usize {
|
||||||
|
unsafe { (*self.0).rrsig_count }
|
||||||
|
}
|
||||||
|
pub fn trust(&self) -> RrsetTrust {
|
||||||
|
RrsetTrust::from(unsafe { (*self.0).trust })
|
||||||
|
}
|
||||||
|
pub fn security(&self) -> SecStatus {
|
||||||
|
SecStatus::from(unsafe { (*self.0).security })
|
||||||
|
}
|
||||||
|
pub fn rr_data(&self) -> impl '_ + Iterator<Item = (&[u8], Option<Duration>)> {
|
||||||
|
let total = self.count();
|
||||||
|
let ttl = unsafe { (*self.0).rr_ttl };
|
||||||
|
let len = unsafe { (*self.0).rr_len };
|
||||||
|
let data = unsafe { (*self.0).rr_data };
|
||||||
|
(0..total).map(move |i| unsafe {
|
||||||
|
(
|
||||||
|
std::slice::from_raw_parts(*data.add(i), *len.add(i)),
|
||||||
|
TryFrom::try_from(*ttl.add(i)).map(Duration::from_secs).ok(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn rrsig_data(&self) -> impl '_ + Iterator<Item = &[u8]> {
|
||||||
|
let total = self.count();
|
||||||
|
let total2 = self.rrsig_count();
|
||||||
|
let len = unsafe { (*self.0).rr_len };
|
||||||
|
let data = unsafe { (*self.0).rr_data };
|
||||||
|
(total..total + total2)
|
||||||
|
.map(move |i| unsafe { std::slice::from_raw_parts(*data.add(i), *len.add(i)) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RrsetIdType = rrset_id_type;
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ModuleEvent {
|
||||||
|
/// new query
|
||||||
|
New = 0,
|
||||||
|
/// query passed by other module
|
||||||
|
Pass = 1,
|
||||||
|
/// reply inbound from server
|
||||||
|
Reply = 2,
|
||||||
|
/// no reply, timeout or other error
|
||||||
|
NoReply = 3,
|
||||||
|
/// reply is there, but capitalisation check failed
|
||||||
|
CapsFail = 4,
|
||||||
|
/// next module is done, and its reply is awaiting you
|
||||||
|
ModDone = 5,
|
||||||
|
/// error
|
||||||
|
Error = 6,
|
||||||
|
Unknown = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<module_ev> for ModuleEvent {
|
||||||
|
fn from(value: module_ev) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => Self::New,
|
||||||
|
1 => Self::Pass,
|
||||||
|
2 => Self::Reply,
|
||||||
|
3 => Self::NoReply,
|
||||||
|
4 => Self::CapsFail,
|
||||||
|
5 => Self::ModDone,
|
||||||
|
6 => Self::Error,
|
||||||
|
_ => Self::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SecStatus {
|
||||||
|
/// UNCHECKED means that object has yet to be validated.
|
||||||
|
Unchecked = 0,
|
||||||
|
/// BOGUS means that the object (RRset or message) failed to validate\n (according to local policy), but should have validated.
|
||||||
|
Bogus = 1,
|
||||||
|
/// INDETERMINATE means that the object is insecure, but not\n authoritatively so. Generally this means that the RRset is not\n below a configured trust anchor.
|
||||||
|
Indeterminate = 2,
|
||||||
|
/// INSECURE means that the object is authoritatively known to be\n insecure. Generally this means that this RRset is below a trust\n anchor, but also below a verified, insecure delegation.
|
||||||
|
Insecure = 3,
|
||||||
|
/// SECURE_SENTINEL_FAIL means that the object (RRset or message)\n validated according to local policy but did not succeed in the root\n KSK sentinel test (draft-ietf-dnsop-kskroll-sentinel).
|
||||||
|
SecureSentinelFail = 4,
|
||||||
|
/// SECURE means that the object (RRset or message) validated\n according to local policy.
|
||||||
|
Secure = 5,
|
||||||
|
Unknown = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sec_status> for SecStatus {
|
||||||
|
fn from(value: module_ev) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => Self::Unchecked,
|
||||||
|
1 => Self::Bogus,
|
||||||
|
2 => Self::Indeterminate,
|
||||||
|
3 => Self::Insecure,
|
||||||
|
4 => Self::SecureSentinelFail,
|
||||||
|
5 => Self::Secure,
|
||||||
|
_ => Self::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SldnsEdeCode {
|
||||||
|
None = -1,
|
||||||
|
Other = 0,
|
||||||
|
UnsupportedDnskeyAlg = 1,
|
||||||
|
UnsupportedDsDigest = 2,
|
||||||
|
StaleAnswer = 3,
|
||||||
|
ForgedAnswer = 4,
|
||||||
|
DnssecIndeterminate = 5,
|
||||||
|
DnssecBogus = 6,
|
||||||
|
SignatureExpired = 7,
|
||||||
|
SignatureNotYetValid = 8,
|
||||||
|
DnskeyMissing = 9,
|
||||||
|
RrsigsMissing = 10,
|
||||||
|
NoZoneKeyBitSet = 11,
|
||||||
|
NsecMissing = 12,
|
||||||
|
CachedError = 13,
|
||||||
|
NotReady = 14,
|
||||||
|
Blocked = 15,
|
||||||
|
Censored = 16,
|
||||||
|
Filtered = 17,
|
||||||
|
Prohibited = 18,
|
||||||
|
StaleNxdomainAnswer = 19,
|
||||||
|
NotAuthoritative = 20,
|
||||||
|
NotSupported = 21,
|
||||||
|
NoReachableAuthority = 22,
|
||||||
|
NetworkError = 23,
|
||||||
|
InvalidData = 24,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<sldns_enum_ede_code> for SldnsEdeCode {
|
||||||
|
fn from(value: sldns_enum_ede_code) -> Self {
|
||||||
|
match value {
|
||||||
|
-1 => Self::None,
|
||||||
|
0 => Self::Other,
|
||||||
|
1 => Self::UnsupportedDnskeyAlg,
|
||||||
|
2 => Self::UnsupportedDsDigest,
|
||||||
|
3 => Self::StaleAnswer,
|
||||||
|
4 => Self::ForgedAnswer,
|
||||||
|
5 => Self::DnssecIndeterminate,
|
||||||
|
6 => Self::DnssecBogus,
|
||||||
|
7 => Self::SignatureExpired,
|
||||||
|
8 => Self::SignatureNotYetValid,
|
||||||
|
9 => Self::DnskeyMissing,
|
||||||
|
10 => Self::RrsigsMissing,
|
||||||
|
11 => Self::NoZoneKeyBitSet,
|
||||||
|
12 => Self::NsecMissing,
|
||||||
|
13 => Self::CachedError,
|
||||||
|
14 => Self::NotReady,
|
||||||
|
15 => Self::Blocked,
|
||||||
|
16 => Self::Censored,
|
||||||
|
17 => Self::Filtered,
|
||||||
|
18 => Self::Prohibited,
|
||||||
|
19 => Self::StaleNxdomainAnswer,
|
||||||
|
20 => Self::NotAuthoritative,
|
||||||
|
21 => Self::NotSupported,
|
||||||
|
22 => Self::NoReachableAuthority,
|
||||||
|
23 => Self::NetworkError,
|
||||||
|
24 => Self::InvalidData,
|
||||||
|
_ => Self::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum RrsetTrust {
|
||||||
|
/// Initial value for trust
|
||||||
|
None = 0,
|
||||||
|
/// Additional information from non-authoritative answers
|
||||||
|
AddNoAa = 1,
|
||||||
|
/// Data from the authority section of a non-authoritative answer
|
||||||
|
AuthNoAa = 2,
|
||||||
|
/// Additional information from an authoritative answer
|
||||||
|
AddAa = 3,
|
||||||
|
/// non-authoritative data from the answer section of authoritative answers
|
||||||
|
NonauthAnsAa = 4,
|
||||||
|
/// Data from the answer section of a non-authoritative answer
|
||||||
|
AnsNoAa = 5,
|
||||||
|
/// Glue from a primary zone, or glue from a zone transfer
|
||||||
|
Glue = 6,
|
||||||
|
/// Data from the authority section of an authoritative answer
|
||||||
|
AuthAa = 7,
|
||||||
|
/// The authoritative data included in the answer section of an\n authoritative reply
|
||||||
|
AnsAa = 8,
|
||||||
|
/// Data from a zone transfer, other than glue
|
||||||
|
SecNoglue = 9,
|
||||||
|
/// Data from a primary zone file, other than glue data
|
||||||
|
PrimNoglue = 10,
|
||||||
|
/// DNSSEC(rfc4034) validated with trusted keys
|
||||||
|
Validated = 11,
|
||||||
|
/// Ultimately trusted, no more trust is possible,
|
||||||
|
/// trusted keys from the unbound configuration setup.
|
||||||
|
Ultimate = 12,
|
||||||
|
Unknown = 13,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rrset_trust> for RrsetTrust {
|
||||||
|
fn from(value: rrset_trust) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => Self::None,
|
||||||
|
1 => Self::AddNoAa,
|
||||||
|
2 => Self::AuthNoAa,
|
||||||
|
3 => Self::AddAa,
|
||||||
|
4 => Self::NonauthAnsAa,
|
||||||
|
5 => Self::AnsNoAa,
|
||||||
|
6 => Self::Glue,
|
||||||
|
7 => Self::AuthAa,
|
||||||
|
8 => Self::AnsAa,
|
||||||
|
9 => Self::SecNoglue,
|
||||||
|
10 => Self::PrimNoglue,
|
||||||
|
11 => Self::Validated,
|
||||||
|
12 => Self::Ultimate,
|
||||||
|
_ => Self::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod rr_class {
|
||||||
|
/// the Internet
|
||||||
|
pub const IN: u16 = 1;
|
||||||
|
/// Chaos class
|
||||||
|
pub const CH: u16 = 3;
|
||||||
|
/// Hesiod (Dyer 87)
|
||||||
|
pub const HS: u16 = 4;
|
||||||
|
/// None class, dynamic update
|
||||||
|
pub const NONE: u16 = 254;
|
||||||
|
/// Any class
|
||||||
|
pub const ANY: u16 = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod rr_type {
|
||||||
|
pub const A: u16 = 1;
|
||||||
|
/// An authoritative name server
|
||||||
|
pub const NS: u16 = 2;
|
||||||
|
/// A mail destination (Obsolete - use MX)
|
||||||
|
pub const MD: u16 = 3;
|
||||||
|
/// A mail forwarder (Obsolete - use MX)
|
||||||
|
pub const MF: u16 = 4;
|
||||||
|
/// The canonical name for an alias
|
||||||
|
pub const CNAME: u16 = 5;
|
||||||
|
/// Marks the start of a zone of authority
|
||||||
|
pub const SOA: u16 = 6;
|
||||||
|
/// A mailbox domain name (EXPERIMENTAL)
|
||||||
|
pub const MB: u16 = 7;
|
||||||
|
/// A mail group member (EXPERIMENTAL)
|
||||||
|
pub const MG: u16 = 8;
|
||||||
|
/// A mail rename domain name (EXPERIMENTAL)
|
||||||
|
pub const MR: u16 = 9;
|
||||||
|
/// A null RR (EXPERIMENTAL)
|
||||||
|
pub const NULL: u16 = 10;
|
||||||
|
/// A well known service description
|
||||||
|
pub const WKS: u16 = 11;
|
||||||
|
/// A domain name pointer
|
||||||
|
pub const PTR: u16 = 12;
|
||||||
|
/// Host information
|
||||||
|
pub const HINFO: u16 = 13;
|
||||||
|
/// Mailbox or mail list information
|
||||||
|
pub const MINFO: u16 = 14;
|
||||||
|
/// Mail exchange
|
||||||
|
pub const MX: u16 = 15;
|
||||||
|
/// Text strings
|
||||||
|
pub const TXT: u16 = 16;
|
||||||
|
/// rFC1183
|
||||||
|
pub const RP: u16 = 17;
|
||||||
|
/// rFC1183
|
||||||
|
pub const AFSDB: u16 = 18;
|
||||||
|
/// rFC1183
|
||||||
|
pub const X25: u16 = 19;
|
||||||
|
/// rFC1183
|
||||||
|
pub const ISDN: u16 = 20;
|
||||||
|
/// rFC1183
|
||||||
|
pub const RT: u16 = 21;
|
||||||
|
/// rFC1706
|
||||||
|
pub const NSAP: u16 = 22;
|
||||||
|
/// rFC1348
|
||||||
|
pub const NSAP_PTR: u16 = 23;
|
||||||
|
/// 2535typecode
|
||||||
|
pub const SIG: u16 = 24;
|
||||||
|
/// 2535typecode
|
||||||
|
pub const KEY: u16 = 25;
|
||||||
|
/// rFC2163
|
||||||
|
pub const PX: u16 = 26;
|
||||||
|
/// rFC1712
|
||||||
|
pub const GPOS: u16 = 27;
|
||||||
|
/// Ipv6 address
|
||||||
|
pub const AAAA: u16 = 28;
|
||||||
|
/// lOC record RFC1876
|
||||||
|
pub const LOC: u16 = 29;
|
||||||
|
/// 2535typecode
|
||||||
|
pub const NXT: u16 = 30;
|
||||||
|
/// Draft-ietf-nimrod-dns-01.txt
|
||||||
|
pub const EID: u16 = 31;
|
||||||
|
/// Draft-ietf-nimrod-dns-01.txt
|
||||||
|
pub const NIMLOC: u16 = 32;
|
||||||
|
/// sRV record RFC2782
|
||||||
|
pub const SRV: u16 = 33;
|
||||||
|
/// Http://www.jhsoft.com/rfc/af-saa-0069.000.rtf
|
||||||
|
pub const ATMA: u16 = 34;
|
||||||
|
/// rFC2915
|
||||||
|
pub const NAPTR: u16 = 35;
|
||||||
|
/// rFC2230
|
||||||
|
pub const KX: u16 = 36;
|
||||||
|
/// rFC2538
|
||||||
|
pub const CERT: u16 = 37;
|
||||||
|
/// rFC2874
|
||||||
|
pub const A6: u16 = 38;
|
||||||
|
/// rFC2672
|
||||||
|
pub const DNAME: u16 = 39;
|
||||||
|
/// Dnsind-kitchen-sink-02.txt
|
||||||
|
pub const SINK: u16 = 40;
|
||||||
|
/// pseudo OPT record...
|
||||||
|
pub const OPT: u16 = 41;
|
||||||
|
/// rFC3123
|
||||||
|
pub const APL: u16 = 42;
|
||||||
|
/// rFC4034, RFC3658
|
||||||
|
pub const DS: u16 = 43;
|
||||||
|
/// sSH Key Fingerprint
|
||||||
|
pub const SSHFP: u16 = 44;
|
||||||
|
/// iPsec Key
|
||||||
|
pub const IPSECKEY: u16 = 45;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const RRSIG: u16 = 46;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const NSEC: u16 = 47;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const DNSKEY: u16 = 48;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const DHCID: u16 = 49;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const NSEC3: u16 = 50;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const NSEC3PARAM: u16 = 51;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const NSEC3PARAMS: u16 = 51;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const TLSA: u16 = 52;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const SMIMEA: u16 = 53;
|
||||||
|
/// dNSSEC
|
||||||
|
pub const HIP: u16 = 55;
|
||||||
|
///draft-reid-dnsext-zs
|
||||||
|
pub const NINFO: u16 = 56;
|
||||||
|
///draft-reid-dnsext-rkey
|
||||||
|
pub const RKEY: u16 = 57;
|
||||||
|
///draft-ietf-dnsop-trust-history
|
||||||
|
pub const TALINK: u16 = 58;
|
||||||
|
///draft-ietf-dnsop-trust-history
|
||||||
|
pub const CDS: u16 = 59;
|
||||||
|
///RFC 7344
|
||||||
|
pub const CDNSKEY: u16 = 60;
|
||||||
|
///RFC 7344
|
||||||
|
pub const OPENPGPKEY: u16 = 61;
|
||||||
|
///RFC 7344
|
||||||
|
pub const CSYNC: u16 = 62;
|
||||||
|
///RFC 7344
|
||||||
|
pub const ZONEMD: u16 = 63;
|
||||||
|
///RFC 7344
|
||||||
|
pub const SVCB: u16 = 64;
|
||||||
|
///RFC 7344
|
||||||
|
pub const HTTPS: u16 = 65;
|
||||||
|
///RFC 7344
|
||||||
|
pub const SPF: u16 = 99;
|
||||||
|
///RFC 7344
|
||||||
|
pub const UINFO: u16 = 100;
|
||||||
|
///RFC 7344
|
||||||
|
pub const UID: u16 = 101;
|
||||||
|
///RFC 7344
|
||||||
|
pub const GID: u16 = 102;
|
||||||
|
///RFC 7344
|
||||||
|
pub const UNSPEC: u16 = 103;
|
||||||
|
///RFC 7344
|
||||||
|
pub const NID: u16 = 104;
|
||||||
|
///RFC 7344
|
||||||
|
pub const L32: u16 = 105;
|
||||||
|
///RFC 7344
|
||||||
|
pub const L64: u16 = 106;
|
||||||
|
///RFC 7344
|
||||||
|
pub const LP: u16 = 107;
|
||||||
|
///draft-jabley-dnsext-eui48-eui64-rrtypes
|
||||||
|
pub const EUI48: u16 = 108;
|
||||||
|
///draft-jabley-dnsext-eui48-eui64-rrtypes
|
||||||
|
pub const EUI64: u16 = 109;
|
||||||
|
///draft-jabley-dnsext-eui48-eui64-rrtypes
|
||||||
|
pub const TKEY: u16 = 249;
|
||||||
|
///draft-jabley-dnsext-eui48-eui64-rrtypes
|
||||||
|
pub const TSIG: u16 = 250;
|
||||||
|
///draft-jabley-dnsext-eui48-eui64-rrtypes
|
||||||
|
pub const IXFR: u16 = 251;
|
||||||
|
///draft-jabley-dnsext-eui48-eui64-rrtypes
|
||||||
|
pub const AXFR: u16 = 252;
|
||||||
|
/// a request for mailbox-related records (MB, MG or MR)
|
||||||
|
pub const MAILB: u16 = 253;
|
||||||
|
/// a request for mail agent RRs (Obsolete - see MX)
|
||||||
|
pub const MAILA: u16 = 254;
|
||||||
|
/// Any type (wildcard)
|
||||||
|
pub const ANY: u16 = 255;
|
||||||
|
pub const URI: u16 = 256;
|
||||||
|
pub const CAA: u16 = 257;
|
||||||
|
pub const AVC: u16 = 258;
|
||||||
|
///DNSSEC trust Authorities
|
||||||
|
pub const TA: u16 = 32768;
|
||||||
|
pub const DLV: u16 = 32769;
|
||||||
|
}
|
Loading…
Reference in a new issue