bindy/bind9/records/
ns.rs1use super::super::types::RndcKeyData;
7use super::{
8 build_authenticated_client, build_delete_rrset_record, build_record_fqdn, effective_record_ttl,
9 rrset_ttl_matches, 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_ns_rrset(existing_records: &[Record], nameserver: &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::NS(existing_ns) = &existing_records[0].data else {
38 return false;
39 };
40 existing_ns.0.to_string() == nameserver
41}
42
43#[allow(clippy::too_many_arguments)]
53pub async fn add_ns_record(
54 zone_name: &str,
55 name: &str,
56 nameserver: &str,
57 ttl: Option<i32>,
58 server: &str,
59 key_data: &RndcKeyData,
60) -> Result<()> {
61 let ttl_value = effective_record_ttl(ttl);
62 let should_update = should_update_record(
63 zone_name,
64 name,
65 RecordType::NS,
66 "NS",
67 server,
68 |existing_records| compare_ns_rrset(existing_records, nameserver, ttl_value),
69 )
70 .await?;
71
72 if !should_update {
73 return Ok(());
74 }
75
76 let zone = Name::from_str(zone_name)?;
77 let fqdn = build_record_fqdn(zone_name, name)?;
78 let ns_name = Name::from_str(nameserver)?;
79
80 let mut record = Record::from_rdata(fqdn.clone(), ttl_value, RData::NS(rdata::NS(ns_name)));
81 record.dns_class = DNSClass::IN;
82
83 info!(
84 "Adding NS record: {} -> {} (TTL: {})",
85 fqdn, nameserver, ttl_value
86 );
87
88 let mut client = build_authenticated_client(server, key_data).await?;
89
90 let delete_record = build_delete_rrset_record(&fqdn, RecordType::NS);
92 let _ = client.delete_rrset(delete_record, zone.clone()).await;
93
94 let response = client.append(record, zone, false).await?;
96
97 match response.metadata.response_code {
98 ResponseCode::NoError => {
99 info!("Successfully added NS record: {} -> {}", name, nameserver);
100 Ok(())
101 }
102 code => Err(anyhow::anyhow!(
103 "DNS update failed with response code: {code:?}"
104 )),
105 }
106}
107
108#[cfg(test)]
109#[path = "ns_tests.rs"]
110mod ns_tests;