bindy/bind9/records/
cname.rs1use super::super::types::RndcKeyData;
7use super::{
8 build_authenticated_client, build_record_fqdn, effective_record_ttl, rrset_ttl_matches,
9 should_update_record,
10};
11use anyhow::Result;
12use hickory_net::client::ClientHandle;
13use hickory_proto::op::ResponseCode;
14use hickory_proto::rr::{rdata, DNSClass, Name, RData, Record, RecordType};
15use std::str::FromStr;
16use tracing::info;
17
18fn compare_cname_rrset(existing_records: &[Record], target: &str, desired_ttl: u32) -> bool {
31 if existing_records.len() != 1 {
32 return false;
33 }
34 if !rrset_ttl_matches(existing_records, desired_ttl) {
35 return false;
36 }
37 let RData::CNAME(existing_cname) = &existing_records[0].data else {
38 return false;
39 };
40 existing_cname.0.to_string() == target
41}
42
43#[allow(clippy::too_many_arguments)]
49pub async fn add_cname_record(
50 zone_name: &str,
51 name: &str,
52 target: &str,
53 ttl: Option<i32>,
54 server: &str,
55 key_data: &RndcKeyData,
56) -> Result<()> {
57 let ttl_value = effective_record_ttl(ttl);
58 let should_update = should_update_record(
59 zone_name,
60 name,
61 RecordType::CNAME,
62 "CNAME",
63 server,
64 |existing_records| compare_cname_rrset(existing_records, target, ttl_value),
65 )
66 .await?;
67
68 if !should_update {
69 return Ok(());
70 }
71
72 let zone = Name::from_str(zone_name)?;
73 let fqdn = build_record_fqdn(zone_name, name)?;
74 let target_name = Name::from_str(target)?;
75
76 let mut record = Record::from_rdata(
77 fqdn.clone(),
78 ttl_value,
79 RData::CNAME(rdata::CNAME(target_name)),
80 );
81 record.dns_class = DNSClass::IN;
82
83 info!(
84 "Adding CNAME record: {} -> {} (TTL: {})",
85 record.name, target, ttl_value
86 );
87
88 let mut client = build_authenticated_client(server, key_data).await?;
89 let response = client.append(record, zone, false).await?;
90
91 match response.metadata.response_code {
92 ResponseCode::NoError => {
93 info!("Successfully added CNAME record: {} -> {}", name, target);
94 Ok(())
95 }
96 code => Err(anyhow::anyhow!(
97 "DNS update failed with response code: {code:?}"
98 )),
99 }
100}
101
102#[cfg(test)]
103#[path = "cname_tests.rs"]
104mod cname_tests;