bindy/bind9/
types.rs

1// Copyright (c) 2025 Erick Bourgeois, firestoned
2// SPDX-License-Identifier: MIT
3
4//! Types and constants for BIND9 management.
5
6/// RNDC key data for authentication.
7#[derive(Debug, Clone)]
8pub struct RndcKeyData {
9    /// Key name (typically the instance name)
10    pub name: String,
11    /// HMAC algorithm
12    pub algorithm: crate::crd::RndcAlgorithm,
13    /// Base64-encoded secret key
14    pub secret: String,
15}
16
17/// RNDC command error with structured information.
18///
19/// Parses BIND9 RNDC error responses in the format:
20/// ```text
21/// rndc: 'command' failed: error_type
22/// error details
23/// ```
24#[derive(Debug, Clone, thiserror::Error)]
25#[error("RNDC command '{command}' failed: {error}")]
26pub struct RndcError {
27    /// The RNDC command that failed (e.g., "zonestatus", "addzone")
28    pub command: String,
29    /// The error type (e.g., "not found", "already exists")
30    pub error: String,
31    /// Additional error details from BIND9
32    pub details: Option<String>,
33}
34
35impl RndcError {
36    /// Parse an RNDC error response.
37    ///
38    /// Expected format:
39    /// ```text
40    /// rndc: 'zonestatus' failed: not found
41    /// no matching zone 'example.com' in any view
42    /// ```
43    #[must_use]
44    pub fn parse(response: &str) -> Option<Self> {
45        // Parse first line: rndc: 'command' failed: error
46        let lines: Vec<&str> = response.lines().collect();
47        let first_line = lines.first()?;
48
49        if !first_line.starts_with("rndc:") {
50            return None;
51        }
52
53        // Extract command from 'command'
54        let command_start = first_line.find('\'')?;
55        let command_end = first_line[command_start + 1..].find('\'')?;
56        let command = first_line[command_start + 1..command_start + 1 + command_end].to_string();
57
58        // Extract error after "failed: "
59        let failed_pos = first_line.find("failed:")?;
60        let error = first_line[failed_pos + 7..].trim().to_string();
61
62        // Remaining lines are details
63        let details = if lines.len() > 1 {
64            Some(lines[1..].join("\n").trim().to_string())
65        } else {
66            None
67        };
68
69        Some(Self {
70            command,
71            error,
72            details,
73        })
74    }
75}
76
77/// Path to the `ServiceAccount` token file in Kubernetes pods
78pub const SERVICE_ACCOUNT_TOKEN_PATH: &str = "/var/run/secrets/kubernetes.io/serviceaccount/token";
79
80/// Parameters for creating SRV records.
81///
82/// Contains the priority, weight, port, and target required for SRV records.
83#[derive(Clone)]
84pub struct SRVRecordData {
85    /// Priority of the target host (lower is higher priority)
86    pub priority: i32,
87    /// Relative weight for records with the same priority
88    pub weight: i32,
89    /// TCP or UDP port on which the service is found
90    pub port: i32,
91    /// Canonical hostname of the machine providing the service
92    pub target: String,
93    /// Time to live in seconds
94    pub ttl: Option<i32>,
95}
96
97#[cfg(test)]
98#[path = "types_tests.rs"]
99mod types_tests;