bindcar/
rndc_conf_types.rs1use std::collections::HashMap;
28use std::net::IpAddr;
29use std::path::PathBuf;
30
31#[derive(Debug, Clone, PartialEq)]
33pub struct RndcConfFile {
34 pub keys: HashMap<String, KeyBlock>,
36
37 pub servers: HashMap<String, ServerBlock>,
39
40 pub options: OptionsBlock,
42
43 pub includes: Vec<PathBuf>,
45}
46
47impl RndcConfFile {
48 pub fn new() -> Self {
50 Self {
51 keys: HashMap::new(),
52 servers: HashMap::new(),
53 options: OptionsBlock::default(),
54 includes: Vec::new(),
55 }
56 }
57
58 pub fn get_default_key(&self) -> Option<&KeyBlock> {
60 let key_name = self.options.default_key.as_ref()?;
61 self.keys.get(key_name)
62 }
63
64 pub fn get_default_server(&self) -> Option<String> {
66 self.options.default_server.clone()
67 }
68
69 pub fn to_conf_file(&self) -> String {
71 let mut output = String::new();
72
73 for include_path in &self.includes {
75 output.push_str(&format!("include \"{}\";\n", include_path.display()));
76 }
77
78 for (name, key) in &self.keys {
80 output.push_str(&format!("\nkey \"{}\" {}\n", name, key.to_conf_block()));
81 }
82
83 for (addr, server) in &self.servers {
85 output.push_str(&format!("\nserver {} {}\n", addr, server.to_conf_block()));
86 }
87
88 if !self.options.is_empty() {
90 output.push_str(&format!("\noptions {}\n", self.options.to_conf_block()));
91 }
92
93 output
94 }
95}
96
97impl Default for RndcConfFile {
98 fn default() -> Self {
99 Self::new()
100 }
101}
102
103#[derive(Clone, PartialEq, Eq)]
105pub struct KeyBlock {
106 pub name: String,
107 pub algorithm: String,
108 pub secret: String,
109}
110
111impl std::fmt::Debug for KeyBlock {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 f.debug_struct("KeyBlock")
117 .field("name", &self.name)
118 .field("algorithm", &self.algorithm)
119 .field("secret", &"[REDACTED]")
120 .finish()
121 }
122}
123
124impl KeyBlock {
125 pub fn new(name: String, algorithm: String, secret: String) -> Self {
127 Self {
128 name,
129 algorithm,
130 secret,
131 }
132 }
133
134 pub fn to_conf_block(&self) -> String {
144 format!(
145 "{{\n algorithm {};\n secret \"{}\";\n}};",
146 self.algorithm, self.secret
147 )
148 }
149}
150
151#[derive(Debug, Clone, PartialEq)]
153pub struct ServerBlock {
154 pub address: ServerAddress,
155 pub key: Option<String>,
156 pub port: Option<u16>,
157 pub addresses: Option<Vec<IpAddr>>,
158}
159
160impl ServerBlock {
161 pub fn new(address: ServerAddress) -> Self {
163 Self {
164 address,
165 key: None,
166 port: None,
167 addresses: None,
168 }
169 }
170
171 pub fn to_conf_block(&self) -> String {
181 let mut parts = Vec::new();
182
183 if let Some(ref key) = self.key {
184 parts.push(format!(" key \"{}\";", key));
185 }
186
187 if let Some(port) = self.port {
188 parts.push(format!(" port {};", port));
189 }
190
191 if let Some(ref addrs) = self.addresses {
192 let addr_list = addrs
193 .iter()
194 .map(|ip| format!(" {};", ip))
195 .collect::<Vec<_>>()
196 .join("\n");
197 parts.push(format!(" addresses {{\n{}\n }};", addr_list));
198 }
199
200 if parts.is_empty() {
201 "{ };".to_string()
202 } else {
203 format!("{{\n{}\n}};", parts.join("\n"))
204 }
205 }
206}
207
208#[derive(Debug, Clone, PartialEq, Eq)]
210pub enum ServerAddress {
211 Hostname(String),
212 IpAddr(IpAddr),
213}
214
215impl ServerAddress {
216 pub fn parse(s: &str) -> Self {
218 match s.parse::<IpAddr>() {
219 Ok(addr) => ServerAddress::IpAddr(addr),
220 Err(_) => ServerAddress::Hostname(s.to_string()),
221 }
222 }
223}
224
225impl std::fmt::Display for ServerAddress {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 match self {
228 ServerAddress::Hostname(h) => write!(f, "{}", h),
229 ServerAddress::IpAddr(ip) => write!(f, "{}", ip),
230 }
231 }
232}
233
234#[derive(Debug, Clone, Default, PartialEq, Eq)]
236pub struct OptionsBlock {
237 pub default_server: Option<String>,
238 pub default_key: Option<String>,
239 pub default_port: Option<u16>,
240}
241
242impl OptionsBlock {
243 pub fn new() -> Self {
245 Self::default()
246 }
247
248 pub fn is_empty(&self) -> bool {
250 self.default_server.is_none() && self.default_key.is_none() && self.default_port.is_none()
251 }
252
253 pub fn to_conf_block(&self) -> String {
264 let mut parts = Vec::new();
265
266 if let Some(ref server) = self.default_server {
267 parts.push(format!(" default-server {};", server));
268 }
269
270 if let Some(ref key) = self.default_key {
271 parts.push(format!(" default-key \"{}\";", key));
272 }
273
274 if let Some(port) = self.default_port {
275 parts.push(format!(" default-port {};", port));
276 }
277
278 if parts.is_empty() {
279 "{ };".to_string()
280 } else {
281 format!("{{\n{}\n}};", parts.join("\n"))
282 }
283 }
284}
285
286#[cfg(test)]
287#[path = "rndc_conf_types_tests.rs"]
288mod tests;