1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use futures_util::AsyncWriteExt;
4use nftables::{helper::NftablesError, schema::Nftables};
5use process::Process;
6
7pub const NFT_DEFAULT_PROGRAM: &str = "nft";
9
10pub mod process;
11
12pub async fn apply_ruleset<P: Process>(
14 nftables: Nftables<'_>,
15 program: Option<&str>,
16 args: Option<Vec<&str>>,
17) -> Result<(), NftablesError> {
18 let payload = serde_json::to_string(&nftables).map_err(NftablesError::NftInvalidJson)?;
19 apply_ruleset_raw::<P>(payload, program, args).await
20}
21
22pub async fn apply_ruleset_raw<P: Process>(
24 payload: String,
25 program: Option<&str>,
26 args: Option<Vec<&str>>,
27) -> Result<(), NftablesError> {
28 let program = program.unwrap_or(NFT_DEFAULT_PROGRAM);
29 let mut arg_vec = vec!["-j", "-f", "-"];
30
31 if let Some(args) = args {
32 arg_vec.extend(args);
33 }
34
35 let mut child =
36 P::spawn(program, arg_vec, true).map_err(|err| NftablesError::NftExecution {
37 program: program.to_owned().into(),
38 inner: err,
39 })?;
40
41 let mut stdin = child
42 .take_stdin()
43 .expect("Stdin was piped to the process but could not be retrieved");
44 stdin
45 .write_all(payload.as_bytes())
46 .await
47 .map_err(|err| NftablesError::NftExecution {
48 program: program.to_owned().into(),
49 inner: err,
50 })?;
51 drop(stdin);
52
53 match child.wait_with_output().await {
54 Ok(output) if output.status.success() => Ok(()),
55 Ok(output) => {
56 let stdout = read(program, output.stdout)?;
57 let stderr = read(program, output.stderr)?;
58
59 Err(NftablesError::NftFailed {
60 program: program.to_owned().into(),
61 hint: "applying ruleset".to_string(),
62 stdout,
63 stderr,
64 })
65 }
66 Err(err) => Err(NftablesError::NftExecution {
67 program: program.to_owned().into(),
68 inner: err,
69 }),
70 }
71}
72
73pub async fn get_current_ruleset<P: Process>(
75 program: Option<&str>,
76 args: Option<Vec<&str>>,
77) -> Result<Nftables<'static>, NftablesError> {
78 let output = get_current_ruleset_raw::<P>(program, args).await?;
79 serde_json::from_str(&output).map_err(NftablesError::NftInvalidJson)
80}
81
82pub async fn get_current_ruleset_raw<P: Process>(
84 program: Option<&str>,
85 args: Option<Vec<&str>>,
86) -> Result<String, NftablesError> {
87 let program = program.unwrap_or(NFT_DEFAULT_PROGRAM);
88 let mut arg_vec = vec!["-j", "list", "ruleset"];
89 if let Some(args) = args {
90 arg_vec.extend(args);
91 }
92
93 let output = P::output(program, arg_vec)
94 .await
95 .map_err(|err| NftablesError::NftExecution {
96 program: program.to_owned().into(),
97 inner: err,
98 })?;
99
100 let stdout = read(program, output.stdout)?;
101
102 if !output.status.success() {
103 let stderr = read(program, output.stderr)?;
104
105 return Err(NftablesError::NftFailed {
106 program: program.to_owned().into(),
107 hint: "getting the current ruleset".to_string(),
108 stdout,
109 stderr,
110 });
111 }
112
113 Ok(stdout)
114}
115
116#[inline]
117fn read(program: &str, stream: Vec<u8>) -> Result<String, NftablesError> {
118 String::from_utf8(stream).map_err(|err| NftablesError::NftOutputEncoding {
119 program: program.to_owned().into(),
120 inner: err,
121 })
122}