1use crate::{DnsRecord, IntoFqdn, NamedDnsRecord};
13use std::fmt::Write;
14
15pub struct BindSerializer;
16
17impl BindSerializer {
18 pub fn serialize(records: &[NamedDnsRecord]) -> String {
19 let mut output = String::new();
20 for named in records {
21 let name = &named.name;
22 match &named.record {
23 DnsRecord::A(addr) => {
24 writeln!(output, "{name} IN A {addr}").unwrap();
25 }
26 DnsRecord::AAAA(addr) => {
27 writeln!(output, "{name} IN AAAA {addr}").unwrap();
28 }
29 DnsRecord::CNAME(cname) => {
30 writeln!(output, "{name} IN CNAME {}", cname.into_fqdn()).unwrap();
31 }
32 DnsRecord::NS(ns) => {
33 writeln!(output, "{name} IN NS {}", ns.into_fqdn()).unwrap();
34 }
35 DnsRecord::MX(mx) => {
36 writeln!(
37 output,
38 "{name} IN MX {} {}",
39 mx.priority,
40 (&mx.exchange).into_fqdn()
41 )
42 .unwrap();
43 }
44 DnsRecord::TXT(text) => {
45 write!(output, "{name} IN TXT ").unwrap();
46 if text.len() <= 255 {
47 writeln!(output, "\"{}\"", escape_txt(text)).unwrap();
48 } else {
49 writeln!(output, "(").unwrap();
50 for chunk in text.as_bytes().chunks(255) {
51 let chunk_str = String::from_utf8_lossy(chunk);
52 writeln!(output, " \"{}\"", escape_txt(&chunk_str)).unwrap();
53 }
54 writeln!(output, ")").unwrap();
55 }
56 }
57 DnsRecord::SRV(srv) => {
58 writeln!(
59 output,
60 "{name} IN SRV {} {} {} {}",
61 srv.priority,
62 srv.weight,
63 srv.port,
64 (&srv.target).into_fqdn()
65 )
66 .unwrap();
67 }
68 DnsRecord::TLSA(tlsa) => {
69 writeln!(output, "{name} IN TLSA {tlsa}").unwrap();
70 }
71 DnsRecord::CAA(caa) => {
72 writeln!(output, "{name} IN CAA {caa}").unwrap();
73 }
74 }
75 }
76 output
77 }
78}
79
80#[inline(always)]
81fn escape_txt(s: &str) -> String {
82 s.replace('\\', "\\\\").replace('"', "\\\"")
83}