parent
2abb8268f9
commit
e77fd41635
385
Cargo.lock
generated
385
Cargo.lock
generated
|
@ -2,6 +2,33 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -14,12 +41,40 @@ version = "2.4.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "conformance-tests"
|
||||
version = "0.1.0"
|
||||
|
@ -27,14 +82,74 @@ dependencies = [
|
|||
"dns-test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dns-test"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"minijinja",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
|
@ -51,6 +166,96 @@ version = "2.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
|
@ -63,6 +268,12 @@ version = "0.4.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "minijinja"
|
||||
version = "1.0.12"
|
||||
|
@ -72,6 +283,33 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
|
@ -112,6 +350,12 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.196"
|
||||
|
@ -132,6 +376,53 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.2.3",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
|
@ -156,12 +447,106 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
|
|
@ -7,6 +7,9 @@ version = "0.1.0"
|
|||
|
||||
[dependencies]
|
||||
minijinja = "1.0.12"
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
serde_json = "1.0.113"
|
||||
serde_with = "3.6.1"
|
||||
tempfile = "3.9.0"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -17,6 +17,10 @@ impl Client {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
||||
self.inner.ipv4_addr()
|
||||
}
|
||||
|
||||
pub fn delv(
|
||||
&self,
|
||||
server: Ipv4Addr,
|
||||
|
|
|
@ -3,17 +3,17 @@ mod network;
|
|||
use core::str;
|
||||
use std::fs;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::process::{self, ExitStatus};
|
||||
use std::process::{self, ChildStdout, ExitStatus};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::{atomic, Arc};
|
||||
|
||||
use tempfile::{NamedTempFile, TempDir};
|
||||
|
||||
pub use crate::container::network::Network;
|
||||
use crate::{Error, Implementation, Result};
|
||||
|
||||
pub use crate::container::network::Network;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Container {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
@ -52,9 +52,18 @@ impl Container {
|
|||
let count = container_count();
|
||||
let name = format!("{PACKAGE_NAME}-{implementation}-{pid}-{count}");
|
||||
command
|
||||
.args(["run", "--rm", "--detach", "--name", &name])
|
||||
.arg("-it")
|
||||
.args(["--network", network.name()])
|
||||
.args([
|
||||
"run",
|
||||
"--rm",
|
||||
"--detach",
|
||||
"--cap-add=NET_RAW",
|
||||
"--cap-add=NET_ADMIN",
|
||||
"--network",
|
||||
network.name(),
|
||||
"--name",
|
||||
&name,
|
||||
"-it",
|
||||
])
|
||||
.arg(image_tag)
|
||||
.args(["sleep", "infinity"]);
|
||||
|
||||
|
@ -187,6 +196,17 @@ pub struct Child {
|
|||
}
|
||||
|
||||
impl Child {
|
||||
/// Returns a handle to the child's stdout
|
||||
///
|
||||
/// This method will succeed at most once
|
||||
pub fn stdout(&mut self) -> Result<ChildStdout> {
|
||||
Ok(self
|
||||
.inner
|
||||
.as_mut()
|
||||
.and_then(|child| child.stdout.take())
|
||||
.ok_or("could not retrieve child's stdout")?)
|
||||
}
|
||||
|
||||
pub fn wait(mut self) -> Result<Output> {
|
||||
let output = self.inner.take().expect("unreachable").wait_with_output()?;
|
||||
output.try_into()
|
||||
|
|
|
@ -18,6 +18,7 @@ pub mod name_server;
|
|||
pub mod record;
|
||||
mod resolver;
|
||||
mod trust_anchor;
|
||||
pub mod tshark;
|
||||
pub mod zone_file;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
|
@ -2,6 +2,7 @@ use core::sync::atomic::{self, AtomicUsize};
|
|||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::container::{Child, Container, Network};
|
||||
use crate::tshark::Tshark;
|
||||
use crate::zone_file::{self, SoaSettings, ZoneFile, DNSKEY, DS};
|
||||
use crate::{Implementation, Result, FQDN};
|
||||
|
||||
|
@ -214,6 +215,11 @@ impl<'a> NameServer<'a, Signed> {
|
|||
}
|
||||
|
||||
impl<'a> NameServer<'a, Running> {
|
||||
/// Starts a `tshark` instance that captures DNS messages flowing through this network node
|
||||
pub fn eavesdrop(&self) -> Result<Tshark> {
|
||||
self.container.eavesdrop()
|
||||
}
|
||||
|
||||
/// gracefully terminates the name server collecting all logs
|
||||
pub fn terminate(self) -> Result<String> {
|
||||
let pidfile = "/run/nsd/nsd.pid";
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::net::Ipv4Addr;
|
|||
|
||||
use crate::container::{Child, Container, Network};
|
||||
use crate::trust_anchor::TrustAnchor;
|
||||
use crate::tshark::Tshark;
|
||||
use crate::zone_file::Root;
|
||||
use crate::{Implementation, Result};
|
||||
|
||||
|
@ -72,6 +73,10 @@ impl Resolver {
|
|||
Ok(Self { child, container })
|
||||
}
|
||||
|
||||
pub fn eavesdrop(&self) -> Result<Tshark> {
|
||||
self.container.eavesdrop()
|
||||
}
|
||||
|
||||
pub fn ipv4_addr(&self) -> Ipv4Addr {
|
||||
self.container.ipv4_addr()
|
||||
}
|
||||
|
|
344
packages/dns-test/src/tshark.rs
Normal file
344
packages/dns-test/src/tshark.rs
Normal file
|
@ -0,0 +1,344 @@
|
|||
//! `tshark` JSON output parser
|
||||
|
||||
use core::result::Result as CoreResult;
|
||||
use std::io::{BufRead, BufReader, Lines};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::process::ChildStdout;
|
||||
use std::sync::atomic::{self, AtomicUsize};
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde_with::{serde_as, DisplayFromStr};
|
||||
|
||||
use crate::container::{Child, Container};
|
||||
use crate::Result;
|
||||
|
||||
static ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
const UDP_PORT: u16 = 53;
|
||||
|
||||
impl Container {
|
||||
pub fn eavesdrop(&self) -> Result<Tshark> {
|
||||
let id = ID.fetch_add(1, atomic::Ordering::Relaxed);
|
||||
let pidfile = pid_file(id);
|
||||
let capture_file = capture_file(id);
|
||||
|
||||
// `docker exec $child` merges the child's stderr and stdout streams and pipes them into
|
||||
// its stdout. as we cannot tell stdout (JSON) from stderr (log message) from the host side,
|
||||
// we'll redirect the JSON output to a file inside the container and read the log messages
|
||||
// from the host side
|
||||
// --log-level info --log-domain main
|
||||
let tshark = format!(
|
||||
"echo $$ > {pidfile}
|
||||
exec tshark --log-level debug --log-domain main,capture -l -i eth0 -T json -O dns -f 'udp port {UDP_PORT}' > {capture_file}"
|
||||
);
|
||||
let mut child = self.spawn(&["sh", "-c", &tshark])?;
|
||||
|
||||
let stdout = child.stdout()?;
|
||||
let mut stdout = BufReader::new(stdout).lines();
|
||||
|
||||
for res in stdout.by_ref() {
|
||||
let line = res?;
|
||||
|
||||
if line.contains("Capture started") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Tshark {
|
||||
container: self.clone(),
|
||||
child,
|
||||
stdout,
|
||||
id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn pid_file(id: usize) -> String {
|
||||
format!("/tmp/tshark{id}.pid")
|
||||
}
|
||||
|
||||
fn capture_file(id: usize) -> String {
|
||||
format!("/tmp/tshark{id}.json")
|
||||
}
|
||||
|
||||
pub struct Tshark {
|
||||
child: Child,
|
||||
container: Container,
|
||||
id: usize,
|
||||
stdout: Lines<BufReader<ChildStdout>>,
|
||||
}
|
||||
|
||||
impl Tshark {
|
||||
/// Blocks until `tshark` reports that it has captured new DNS messages
|
||||
///
|
||||
/// This method returns the number of newly captured messages
|
||||
// XXX maybe do this automatically / always in `terminate`?
|
||||
pub fn wait_for_capture(&mut self) -> Result<usize> {
|
||||
// sync_pipe_input_cb(): new packets NN
|
||||
for res in self.stdout.by_ref() {
|
||||
let line = res?;
|
||||
|
||||
if line.contains(": new packets ") {
|
||||
let (_rest, count) = line.rsplit_once(' ').unwrap();
|
||||
return Ok(count.parse()?);
|
||||
}
|
||||
}
|
||||
|
||||
Err("unexpected EOF".into())
|
||||
}
|
||||
|
||||
pub fn terminate(self) -> Result<Vec<Message>> {
|
||||
let pidfile = pid_file(self.id);
|
||||
let kill = format!("test -f {pidfile} || sleep 1; kill $(cat {pidfile})");
|
||||
|
||||
self.container.status_ok(&["sh", "-c", &kill])?;
|
||||
let output = self.child.wait()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err("could not terminate the `tshark` process".into());
|
||||
}
|
||||
|
||||
// wait until the message "NN packets captured" appears
|
||||
// wireshark will close stderr after printing that so exhausting
|
||||
// the file descriptor produces the same result
|
||||
for res in self.stdout {
|
||||
res?;
|
||||
}
|
||||
|
||||
let capture_file = capture_file(self.id);
|
||||
let output = self.container.stdout(&["cat", &capture_file])?;
|
||||
|
||||
let mut messages = vec![];
|
||||
let entries: Vec<Entry> = serde_json::from_str(&output)?;
|
||||
|
||||
let own_addr = self.container.ipv4_addr();
|
||||
for entry in entries {
|
||||
let Layers { ip, dns } = entry._source.layers;
|
||||
|
||||
let direction = if ip.dst == own_addr {
|
||||
Direction::Incoming { source: ip.src }
|
||||
} else if ip.src == own_addr {
|
||||
Direction::Outgoing {
|
||||
destination: ip.dst,
|
||||
}
|
||||
} else {
|
||||
return Err(
|
||||
format!("unexpected IP packet found in wireshark trace: {ip:?}").into(),
|
||||
);
|
||||
};
|
||||
|
||||
messages.push(Message {
|
||||
contents: dns,
|
||||
direction,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(messages)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Message {
|
||||
// TODO this should be more "cooked", i.e. be deserialized into a `struct`
|
||||
pub contents: serde_json::Value,
|
||||
pub direction: Direction,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Direction {
|
||||
Incoming { source: Ipv4Addr },
|
||||
Outgoing { destination: Ipv4Addr },
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
pub fn try_into_incoming(self) -> CoreResult<Ipv4Addr, Self> {
|
||||
if let Self::Incoming { source } = self {
|
||||
Ok(source)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_into_outgoing(self) -> CoreResult<Ipv4Addr, Self> {
|
||||
if let Self::Outgoing { destination } = self {
|
||||
Ok(destination)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Entry {
|
||||
_source: Source,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Source {
|
||||
layers: Layers,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Layers {
|
||||
ip: Ip,
|
||||
dns: serde_json::Value,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Ip {
|
||||
#[serde(rename = "ip.src")]
|
||||
#[serde_as(as = "DisplayFromStr")]
|
||||
src: Ipv4Addr,
|
||||
|
||||
#[serde(rename = "ip.dst")]
|
||||
#[serde_as(as = "DisplayFromStr")]
|
||||
dst: Ipv4Addr,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::client::{Client, Dnssec, Recurse};
|
||||
use crate::name_server::NameServer;
|
||||
use crate::record::RecordType;
|
||||
use crate::zone_file::Root;
|
||||
use crate::{Implementation, Network, Resolver, TrustAnchor, FQDN};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn nameserver() -> Result<()> {
|
||||
let network = &Network::new()?;
|
||||
let ns = NameServer::new(FQDN::ROOT, network)?.start()?;
|
||||
let mut tshark = ns.eavesdrop()?;
|
||||
|
||||
let client = Client::new(network)?;
|
||||
let resp = client.dig(
|
||||
Recurse::No,
|
||||
Dnssec::No,
|
||||
ns.ipv4_addr(),
|
||||
RecordType::SOA,
|
||||
&FQDN::ROOT,
|
||||
)?;
|
||||
|
||||
assert!(resp.status.is_noerror());
|
||||
|
||||
let captured = tshark.wait_for_capture()?;
|
||||
assert_eq!(2, captured);
|
||||
|
||||
let messages = tshark.terminate()?;
|
||||
|
||||
let [first, second] = messages.try_into().expect("2 DNS messages");
|
||||
assert_eq!(
|
||||
client.ipv4_addr(),
|
||||
first.direction.try_into_incoming().unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
client.ipv4_addr(),
|
||||
second.direction.try_into_outgoing().unwrap()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolver() -> Result<()> {
|
||||
let network = &Network::new()?;
|
||||
let mut root_ns = NameServer::new(FQDN::ROOT, network)?;
|
||||
let mut com_ns = NameServer::new(FQDN::COM, network)?;
|
||||
|
||||
let mut nameservers_ns = NameServer::new(FQDN("nameservers.com.")?, network)?;
|
||||
nameservers_ns
|
||||
.a(root_ns.fqdn().clone(), root_ns.ipv4_addr())
|
||||
.a(com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
||||
let nameservers_ns = nameservers_ns.start()?;
|
||||
|
||||
com_ns.referral(
|
||||
nameservers_ns.zone().clone(),
|
||||
nameservers_ns.fqdn().clone(),
|
||||
nameservers_ns.ipv4_addr(),
|
||||
);
|
||||
let com_ns = com_ns.start()?;
|
||||
|
||||
root_ns.referral(FQDN::COM, com_ns.fqdn().clone(), com_ns.ipv4_addr());
|
||||
let root_ns = root_ns.start()?;
|
||||
|
||||
let roots = &[Root::new(root_ns.fqdn().clone(), root_ns.ipv4_addr())];
|
||||
let resolver = Resolver::start(
|
||||
Implementation::Unbound,
|
||||
roots,
|
||||
&TrustAnchor::empty(),
|
||||
network,
|
||||
)?;
|
||||
let mut tshark = resolver.eavesdrop()?;
|
||||
let resolver_addr = resolver.ipv4_addr();
|
||||
|
||||
let client = Client::new(network)?;
|
||||
let output = client.dig(
|
||||
Recurse::Yes,
|
||||
Dnssec::No,
|
||||
dbg!(resolver_addr),
|
||||
RecordType::A,
|
||||
root_ns.fqdn(),
|
||||
)?;
|
||||
|
||||
assert!(output.status.is_noerror());
|
||||
|
||||
let count = tshark.wait_for_capture()?;
|
||||
dbg!(count);
|
||||
|
||||
let messages = tshark.terminate()?;
|
||||
assert!(messages.len() > 2);
|
||||
|
||||
let ns_addrs = dbg!([
|
||||
root_ns.ipv4_addr(),
|
||||
com_ns.ipv4_addr(),
|
||||
nameservers_ns.ipv4_addr(),
|
||||
]);
|
||||
let client_addr = dbg!(client.ipv4_addr());
|
||||
|
||||
let mut from_client_count = 0;
|
||||
let mut to_client_count = 0;
|
||||
let mut to_ns_count = 0;
|
||||
let mut from_ns_count = 0;
|
||||
for message in messages {
|
||||
match message.direction {
|
||||
Direction::Incoming { source } => {
|
||||
if source == client_addr {
|
||||
from_client_count += 1;
|
||||
} else if ns_addrs.contains(&source) {
|
||||
from_ns_count += 1;
|
||||
} else {
|
||||
panic!(
|
||||
"found packet coming from {source} which is outside the network graph"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Direction::Outgoing { destination } => {
|
||||
if destination == client_addr {
|
||||
to_client_count += 1;
|
||||
} else if ns_addrs.contains(&destination) {
|
||||
to_ns_count += 1;
|
||||
} else {
|
||||
panic!(
|
||||
"found packet going to {destination} which is outside the network graph"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// query from client (dig)
|
||||
assert_eq!(1, from_client_count);
|
||||
|
||||
// answer to client (dig)
|
||||
assert_eq!(1, to_client_count);
|
||||
|
||||
// check that all queries sent to nameservers were answered
|
||||
assert_eq!(to_ns_count, from_ns_count);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user