bindcar/lib.rs
1// Copyright (c) 2025 Erick Bourgeois, firestoned
2// SPDX-License-Identifier: MIT
3
4//! bindcar - HTTP REST API for managing BIND9 zones via RNDC
5//!
6//! A lightweight library that provides programmatic control over BIND9 DNS zones
7//! using the RNDC (Remote Name Daemon Control) protocol.
8//!
9//! # Features
10//!
11//! - Create, delete, and manage BIND9 zones dynamically
12//! - Execute RNDC commands asynchronously
13//! - Zone file generation and management
14//! - Shared request/response types for API operations
15//! - Authentication support (Bearer tokens and Kubernetes ServiceAccounts)
16//! - Prometheus metrics integration
17//!
18//! # Usage
19//!
20//! This crate can be used as both a library and a standalone binary:
21//!
22//! ## As a Library
23//!
24//! ### Using RNDC Executor
25//!
26//! ```rust,no_run
27//! use bindcar::RndcExecutor;
28//!
29//! #[tokio::main]
30//! async fn main() -> anyhow::Result<()> {
31//! let executor = RndcExecutor::new(
32//! "127.0.0.1:953".to_string(),
33//! "sha256".to_string(),
34//! "dGVzdC1zZWNyZXQtaGVyZQ==".to_string(), // base64 encoded secret
35//! )?;
36//!
37//! // Execute RNDC commands
38//! let status = executor.status().await?;
39//! println!("BIND9 Status: {}", status);
40//!
41//! Ok(())
42//! }
43//! ```
44//!
45//! ### Using Shared Types (for API clients)
46//!
47//! ```rust
48//! use bindcar::{CreateZoneRequest, ZoneConfig, SoaRecord, DnsRecord};
49//! use std::collections::HashMap;
50//!
51//! // Create nameserver glue records
52//! let mut ns_ips = HashMap::new();
53//! ns_ips.insert("ns1.example.com.".to_string(), "192.0.2.10".to_string());
54//!
55//! // Create a zone creation request
56//! let request = CreateZoneRequest {
57//! zone_name: "example.com".to_string(),
58//! zone_type: "primary".to_string(),
59//! zone_config: ZoneConfig {
60//! ttl: 3600,
61//! soa: SoaRecord {
62//! primary_ns: "ns1.example.com.".to_string(),
63//! admin_email: "admin.example.com.".to_string(),
64//! serial: 2025010101,
65//! refresh: 3600,
66//! retry: 600,
67//! expire: 604800,
68//! negative_ttl: 86400,
69//! },
70//! name_servers: vec!["ns1.example.com.".to_string()],
71//! name_server_ips: ns_ips,
72//! records: vec![
73//! DnsRecord {
74//! name: "@".to_string(),
75//! record_type: "A".to_string(),
76//! value: "192.0.2.1".to_string(),
77//! ttl: None,
78//! priority: None,
79//! },
80//! ],
81//! also_notify: None,
82//! allow_transfer: None,
83//! primaries: None,
84//! dnssec_policy: None,
85//! inline_signing: None,
86//! },
87//! update_key_name: None,
88//! };
89//!
90//! // Serialize to JSON for API requests
91//! let json = serde_json::to_string(&request).unwrap();
92//! ```
93//!
94//! ## As a Binary
95//!
96//! ```bash
97//! cargo install bindcar
98//! bindcar
99//! ```
100//!
101//! ## Zone File Generation
102//!
103//! The `serial` field in SoaRecord will auto-generate in YYYYMMDD01 format if omitted from JSON.
104//!
105//! ```rust
106//! use bindcar::{ZoneConfig, SoaRecord, DnsRecord};
107//! use std::collections::HashMap;
108//!
109//! // Example JSON can omit serial for auto-generation:
110//! let json = r#"{
111//! "ttl": 3600,
112//! "soa": {
113//! "primaryNs": "ns1.example.com.",
114//! "adminEmail": "admin.example.com."
115//! },
116//! "nameServers": ["ns1.example.com."],
117//! "nameServerIps": {
118//! "ns1.example.com.": "192.0.2.10"
119//! },
120//! "records": []
121//! }"#;
122//!
123//! let zone_config: ZoneConfig = serde_json::from_str(json).unwrap();
124//! let zone_file_content = zone_config.to_zone_file();
125//! println!("{}", zone_file_content);
126//! ```
127//!
128//! # Integration with Other Projects
129//!
130//! This library is designed to be used by other projects (like bindy) that need to
131//! interact with the bindcar API. By importing this crate, you get:
132//!
133//! - Type-safe request/response structures
134//! - Automatic JSON serialization/deserialization
135//! - OpenAPI schema compatibility
136//! - No need to maintain duplicate type definitions
137
138// Re-export public modules
139#[cfg(feature = "server")]
140pub mod auth;
141pub mod cli;
142pub mod dnssec;
143pub mod metrics;
144#[cfg(feature = "server")]
145pub mod middleware;
146pub mod nsupdate;
147pub mod rate_limit;
148#[cfg(feature = "server")]
149pub mod records;
150pub mod records_types;
151pub mod rndc;
152pub mod rndc_conf_parser;
153pub mod rndc_conf_types;
154pub mod rndc_parser;
155pub mod rndc_types;
156pub mod tls;
157#[cfg(feature = "server")]
158pub mod types;
159#[cfg(feature = "server")]
160pub mod zones;
161pub mod zones_types;
162
163// Re-export commonly used types
164
165// RNDC executor
166pub use rndc::RndcExecutor;
167
168// DNSSEC status and DS types (ADR-0001)
169pub use dnssec::{DnskeyRecord, DnssecKeyStatus, DnssecStatus, DsRecord};
170
171// nsupdate executor
172pub use nsupdate::NsupdateExecutor;
173
174// Error types — axum-bound, so server-only
175#[cfg(feature = "server")]
176pub use types::{ApiError, AppState, ErrorResponse};
177
178// Zone configuration types. Re-exported from `zones_types` rather than `zones`
179// so these paths keep resolving in a `default-features = false` build, which is
180// the whole point of the type split (roadmap 02 phase 1).
181pub use zones_types::{DnsRecord, SoaRecord, ZoneConfig};
182
183// Request/Response types for API operations
184pub use zones_types::{
185 CreateZoneRequest, ServerStatusResponse, ZoneInfo, ZoneListResponse, ZoneResponse,
186 ZONE_TYPE_PRIMARY, ZONE_TYPE_SECONDARY,
187};
188
189// Record management types
190pub use records_types::{
191 AddRecordRequest, RecordResponse, RemoveRecordRequest, UpdateRecordRequest,
192};
193
194// RNDC configuration
195pub use rndc::{parse_rndc_conf, RndcConfig};
196
197// RNDC configuration parser
198pub use rndc_conf_parser::{parse_rndc_conf_file, parse_rndc_conf_str};
199
200// RNDC configuration types
201pub use rndc_conf_types::{KeyBlock, OptionsBlock, RndcConfFile, ServerAddress, ServerBlock};
202
203// Test modules
204#[cfg(all(test, feature = "server"))]
205mod auth_test;
206#[cfg(test)]
207mod cli_test;
208#[cfg(test)]
209mod dnssec_test;
210#[cfg(test)]
211mod metrics_test;
212#[cfg(all(test, feature = "server"))]
213mod middleware_test;
214#[cfg(test)]
215mod nsupdate_test;
216#[cfg(test)]
217mod rate_limit_test;
218#[cfg(all(test, feature = "server"))]
219mod records_test;
220#[cfg(test)]
221mod rndc_parser_tests;
222#[cfg(test)]
223mod rndc_test;
224#[cfg(test)]
225mod rndc_types_tests;
226#[cfg(all(test, feature = "tls"))]
227mod tls_test;
228#[cfg(all(test, feature = "server"))]
229mod types_test;
230#[cfg(all(test, feature = "server"))]
231mod zones_test;