Skip to main content

dns_update/
bind.rs

1/*
2 * Copyright Stalwart Labs LLC See the COPYING
3 * file at the top-level directory of this distribution.
4 *
5 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8 * option. This file may not be copied, modified, or distributed
9 * except according to those terms.
10 */
11
12use 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}