public release
This commit is contained in:
parent
76ffd7af5b
commit
da7f82a6dd
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,4 +1,4 @@
|
|||
/target
|
||||
/result
|
||||
/unbound-mod-test-config
|
||||
/unbound-mod-test-data
|
||||
unbound-mod-test-config
|
||||
unbound-mod-test-data
|
||||
|
|
51
Cargo.lock
generated
51
Cargo.lock
generated
|
@ -39,7 +39,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.74",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -62,6 +62,25 @@ dependencies = [
|
|||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"filetime",
|
||||
"ipnet",
|
||||
"iptrie",
|
||||
"libc",
|
||||
"mnl",
|
||||
"nftnl",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
"unbound-mod",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.24"
|
||||
|
@ -273,29 +292,29 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.205"
|
||||
version = "1.0.207"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150"
|
||||
checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.205"
|
||||
version = "1.0.207"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1"
|
||||
checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
"syn 2.0.74",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.122"
|
||||
version = "1.0.124"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
|
||||
checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
|
@ -322,9 +341,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.72"
|
||||
version = "2.0.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af"
|
||||
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -347,19 +366,7 @@ dependencies = [
|
|||
name = "unbound-mod"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"boxcar",
|
||||
"ctor",
|
||||
"filetime",
|
||||
"ipnet",
|
||||
"iptrie",
|
||||
"libc",
|
||||
"mnl",
|
||||
"nftnl",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
45
Cargo.toml
45
Cargo.toml
|
@ -1,42 +1,3 @@
|
|||
[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 = { version = "0.2.5", optional = true }
|
||||
ctor = { version = "0.2.8", optional = true }
|
||||
filetime = { version = "0.2.24", optional = true }
|
||||
ipnet = { version = "2.9.0", features = ["serde"], optional = true }
|
||||
iptrie = { version = "0.8.5", optional = true }
|
||||
libc = { version = "0.2.155", optional = true }
|
||||
mnl = { version = "0.2.2", features = ["mnl-1-0-4"], optional = true }
|
||||
nftnl = { version = "0.6.2", features = ["nftnl-1-1-2"], optional = true }
|
||||
nix = { version = "0.29.0", features = ["poll", "user"], optional = true }
|
||||
radix_trie = { version = "0.2.1", optional = true }
|
||||
serde = { version = "1.0.205", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1.0.122", optional = true }
|
||||
smallvec = { version = "1.13.2", optional = true }
|
||||
|
||||
[features]
|
||||
example = [
|
||||
"boxcar",
|
||||
"ctor",
|
||||
"filetime",
|
||||
"ipnet",
|
||||
"iptrie",
|
||||
"libc",
|
||||
"mnl",
|
||||
"nftnl",
|
||||
"nix",
|
||||
"radix_trie",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
]
|
||||
default = ["example"]
|
||||
[workspace]
|
||||
members = ["example", "unbound-mod"]
|
||||
resolver = "2"
|
||||
|
|
28
LICENSE
Normal file
28
LICENSE
Normal file
|
@ -0,0 +1,28 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, chayleaf
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
13
README.md
Normal file
13
README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# unbound-rust-mod
|
||||
|
||||
This is a library for writing Unbound modules in Rust. See
|
||||
[`example`](./example) for an example module.
|
||||
|
||||
Most of Unbound's features don't have safe bindings, so you might have
|
||||
to write some yourself - in that case, PRs are appreciated.
|
||||
|
||||
To regenerate the bindings, there's a small problem - you actually
|
||||
need to run Unbound's configure script for that. That's why I provide a
|
||||
Nix file to generate them (running `nix build .#bindings` in project
|
||||
root will produce the bindings at the `result` symlink). Alternatively,
|
||||
you may call rust-bindgen manually.
|
24
example/Cargo.toml
Normal file
24
example/Cargo.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "example"
|
||||
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"
|
||||
filetime = "0.2.24"
|
||||
ipnet = { version = "2.9.0", features = ["serde"] }
|
||||
iptrie = "0.8.5"
|
||||
libc = "0.2.155"
|
||||
mnl = { version = "0.2.2", features = ["mnl-1-0-4"] }
|
||||
nftnl = { version = "0.6.2", features = ["nftnl-1-1-2"] }
|
||||
nix = { version = "0.29.0", features = ["poll", "user"] }
|
||||
radix_trie = "0.2.1"
|
||||
serde = { version = "1.0.205", features = ["derive"] }
|
||||
serde_json = "1.0.122"
|
||||
smallvec = "1.13.2"
|
||||
unbound-mod = { path = "../unbound-mod", default-features = false }
|
12
example/README.md
Normal file
12
example/README.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# unbound-rust-mod Example
|
||||
|
||||
This is an example module written using unbound-rust-mod. It
|
||||
automatically populates nft sets using IP and domain info from .json
|
||||
files. On start, it checks various environment variables, then loads
|
||||
the .json files. The IPs are added immediately, but the domains are only
|
||||
added if they're already in the module's cache (stored on the
|
||||
filesystem) or whenever Unbound sends a response. Additionally, it
|
||||
optionally supports live editing certain domain sets by sending a
|
||||
command (that is, a specially formatted DNS request). This could be done
|
||||
using an HTTP server, but that is a holdover from when this was still a
|
||||
Python module (which took 100 seconds to load...)
|
|
@ -109,21 +109,11 @@ mod tests {
|
|||
assert!(!tree.contains([&"a", &"c"]));
|
||||
let mut it = tree.iter();
|
||||
assert!(matches!(
|
||||
it.next()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.copied()
|
||||
.collect::<String>()
|
||||
.as_str(),
|
||||
it.next().unwrap().copied().collect::<String>().as_str(),
|
||||
"ab" | "bcd"
|
||||
));
|
||||
assert!(matches!(
|
||||
it.next()
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.copied()
|
||||
.collect::<String>()
|
||||
.as_str(),
|
||||
it.next().unwrap().copied().collect::<String>().as_str(),
|
||||
"ab" | "bcd"
|
||||
));
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(clippy::type_complexity)]
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::Display,
|
||||
|
@ -12,7 +13,6 @@ use std::{
|
|||
},
|
||||
};
|
||||
|
||||
use ctor::ctor;
|
||||
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
|
||||
use iptrie::IpPrefix;
|
||||
use serde::{
|
||||
|
@ -21,12 +21,15 @@ use serde::{
|
|||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
unbound::{rr_class, rr_type, ModuleEvent, ModuleExtState, ReplyInfo},
|
||||
UnboundMod,
|
||||
};
|
||||
use domain_tree::PrefixSet;
|
||||
use nftables::{nftables_thread, NftData};
|
||||
use unbound_mod::{
|
||||
unbound::{
|
||||
rr_class, rr_type, ModuleEnvMut, ModuleEvent, ModuleExtState, ModuleQstateMut,
|
||||
OutboundEntryMut, ReplyInfo,
|
||||
},
|
||||
UnboundMod,
|
||||
};
|
||||
|
||||
mod domain_tree;
|
||||
mod nftables;
|
||||
|
@ -34,10 +37,7 @@ mod nftables;
|
|||
type Domain = SmallVec<[u8; 32]>;
|
||||
type DomainSeg = SmallVec<[u8; 16]>;
|
||||
|
||||
#[ctor]
|
||||
fn setup() {
|
||||
crate::set_unbound_mod::<ExampleMod>();
|
||||
}
|
||||
unbound_mod::set_module!(ExampleMod);
|
||||
|
||||
struct IpNetDeser(IpNet);
|
||||
struct IpNetVisitor;
|
||||
|
@ -708,15 +708,15 @@ impl UnboundMod for ExampleMod {
|
|||
type EnvData = ();
|
||||
type QstateData = ();
|
||||
|
||||
fn init(_env: &mut crate::unbound::ModuleEnvMut<Self::EnvData>) -> Result<Self, ()> {
|
||||
fn init(_env: &mut ModuleEnvMut<Self::EnvData>) -> Result<Self, ()> {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
qstate: &mut crate::unbound::ModuleQstateMut<Self::QstateData>,
|
||||
qstate: &mut ModuleQstateMut<Self::QstateData>,
|
||||
event: ModuleEvent,
|
||||
_entry: Option<&mut crate::unbound::OutboundEntryMut>,
|
||||
_entry: Option<&mut OutboundEntryMut>,
|
||||
) -> Option<ModuleExtState> {
|
||||
match event {
|
||||
ModuleEvent::New | ModuleEvent::Pass => {
|
||||
|
@ -755,10 +755,8 @@ mod test {
|
|||
use ipnet::IpNet;
|
||||
use smallvec::smallvec;
|
||||
|
||||
use crate::{
|
||||
example::{ignore, ExampleMod, IpCacheKey, IpNetDeser, DATA_PREFIX},
|
||||
unbound::ModuleExtState,
|
||||
};
|
||||
use super::{ignore, ExampleMod, IpCacheKey, IpNetDeser, DATA_PREFIX};
|
||||
use unbound_mod::unbound::ModuleExtState;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
|
@ -9,7 +9,7 @@ use std::{
|
|||
sync::mpsc,
|
||||
};
|
||||
|
||||
use crate::example::{Helper, DATA_PREFIX};
|
||||
use crate::{Helper, DATA_PREFIX};
|
||||
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
|
||||
use iptrie::RTrieSet;
|
||||
use mnl::mnl_sys;
|
14
flake.nix
14
flake.nix
|
@ -19,7 +19,12 @@
|
|||
outputs = [ "out" ];
|
||||
installPhase = ''
|
||||
cp ${./dummy.h} ./dummy.h
|
||||
bindgen ./dummy.h -- -I $PWD > $out
|
||||
opts=()
|
||||
for file in **/*.h; do
|
||||
opts+=(--allowlist-file "$PWD/$file")
|
||||
done
|
||||
|
||||
bindgen --no-layout-tests "''${opts[@]}" dummy.h -- -I "$PWD" > "$out"
|
||||
'';
|
||||
});
|
||||
unbound-mod = let
|
||||
|
@ -28,13 +33,18 @@
|
|||
in craneLib.buildPackage {
|
||||
pname = "unbound-mod";
|
||||
version = "0.1.0";
|
||||
cargoExtraArgs = "--package example";
|
||||
postPatch = ''
|
||||
cp ${bindings} src/bindings.rs
|
||||
ls -la
|
||||
cp ${bindings} unbound-mod/src/bindings.rs
|
||||
'';
|
||||
src = nixpkgs.lib.cleanSourceWith {
|
||||
src = ./.;
|
||||
filter = path: type: lib.hasSuffix ".h" path || craneLib.filterCargoSources path type;
|
||||
};
|
||||
postInstall = ''
|
||||
mv $out/lib/libexample.so $out/lib/libunbound_mod.so
|
||||
'';
|
||||
doCheck = false;
|
||||
LIBMNL_LIB_DIR = "${nixpkgs.lib.getLib pkgs.libmnl}/lib";
|
||||
LIBNFTNL_LIB_DIR = "${nixpkgs.lib.getLib pkgs.libnftnl}/lib";
|
||||
|
|
22040
src/bindings.rs
22040
src/bindings.rs
File diff suppressed because it is too large
Load diff
9
unbound-mod/Cargo.toml
Normal file
9
unbound-mod/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "unbound-mod"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
ctor = "0.2.8"
|
2110
unbound-mod/src/bindings.rs
Normal file
2110
unbound-mod/src/bindings.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
#![allow(clippy::type_complexity)]
|
||||
#![allow(clippy::type_complexity, clippy::missing_safety_doc)]
|
||||
use std::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
use unbound::ModuleExtState;
|
||||
|
@ -13,12 +13,27 @@ use unbound::ModuleExtState;
|
|||
clippy::nursery,
|
||||
clippy::pedantic
|
||||
)]
|
||||
mod bindings;
|
||||
#[doc(hidden)]
|
||||
pub mod bindings;
|
||||
mod combine;
|
||||
#[cfg(feature = "example")]
|
||||
mod example;
|
||||
mod exports;
|
||||
mod unbound;
|
||||
pub mod unbound;
|
||||
|
||||
pub use bindings as sys;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use ctor;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! set_module {
|
||||
($mod:ty) => {
|
||||
use unbound_mod::ctor::ctor;
|
||||
#[ctor]
|
||||
fn _internal_module_setup() {
|
||||
unbound_mod::set_unbound_mod::<$mod>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait UnboundMod: Send + Sync + Sized + RefUnwindSafe + UnwindSafe {
|
||||
type EnvData;
|
|
@ -448,32 +448,32 @@ type RrsetIdType = rrset_id_type;
|
|||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum ModuleEvent {
|
||||
/// new query
|
||||
New = 0,
|
||||
New = bindings::module_ev_module_event_new,
|
||||
/// query passed by other module
|
||||
Pass = 1,
|
||||
Pass = bindings::module_ev_module_event_pass,
|
||||
/// reply inbound from server
|
||||
Reply = 2,
|
||||
Reply = bindings::module_ev_module_event_reply,
|
||||
/// no reply, timeout or other error
|
||||
NoReply = 3,
|
||||
NoReply = bindings::module_ev_module_event_noreply,
|
||||
/// reply is there, but capitalisation check failed
|
||||
CapsFail = 4,
|
||||
CapsFail = bindings::module_ev_module_event_capsfail,
|
||||
/// next module is done, and its reply is awaiting you
|
||||
ModDone = 5,
|
||||
ModDone = bindings::module_ev_module_event_moddone,
|
||||
/// error
|
||||
Error = 6,
|
||||
Unknown = 7,
|
||||
Error = bindings::module_ev_module_event_error,
|
||||
Unknown = 99,
|
||||
}
|
||||
|
||||
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,
|
||||
bindings::module_ev_module_event_new => Self::New,
|
||||
bindings::module_ev_module_event_pass => Self::Pass,
|
||||
bindings::module_ev_module_event_reply => Self::Reply,
|
||||
bindings::module_ev_module_event_noreply => Self::NoReply,
|
||||
bindings::module_ev_module_event_capsfail => Self::CapsFail,
|
||||
bindings::module_ev_module_event_moddone => Self::ModDone,
|
||||
bindings::module_ev_module_event_error => Self::Error,
|
||||
_ => Self::Unknown,
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue