dns.rs 6.34 KB
Newer Older
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
1
use std::net::{Ipv6Addr, Ipv4Addr};
2
use std::fmt;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
3
4

use serde::{Serialize, Deserialize};
5
6
use trust_dns_client::serialize::binary::BinEncoder;
use base64;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
7

8
use super::trust_dns_types;
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
9
10
11
12


#[derive(Deserialize, Serialize)]
#[serde(tag = "Type")]
13
#[serde(rename_all = "UPPERCASE")]
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
14
15
16
17
18
19
20
21
22
pub enum RData {
    #[serde(rename_all = "PascalCase")]
    A {
        address: Ipv4Addr
    },
    #[serde(rename_all = "PascalCase")]
    AAAA {
        address: Ipv6Addr
    },
23
24
25
26
27
28
29
30
31
32
    #[serde(rename_all = "PascalCase")]
    CAA {
        issuer_critical: bool,
        value: String,
        property_tag: String,
    },
    #[serde(rename_all = "PascalCase")]
    CNAME {
        target: String
    },
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
33
34
    // HINFO(HINFO),
    // HTTPS(SVCB),
35
36
37
38
39
    #[serde(rename_all = "PascalCase")]
    MX {
        preference: u16,
        mail_exchanger: String
    },
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
40
    // NAPTR(NAPTR),
41
42
43
44
45
46
47
48
    #[serde(rename_all = "PascalCase")]
    NULL {
        data: String
    },
    #[serde(rename_all = "PascalCase")]
    NS {
        target: String
    },
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
49
50
    // OPENPGPKEY(OPENPGPKEY),
    // OPT(OPT),
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    #[serde(rename_all = "PascalCase")]
    PTR {
        target: String
    },
    #[serde(rename_all = "PascalCase")]
    SOA {
        master_server_name: String,
        maintainer_name: String,
        refresh: i32,
        retry: i32,
        expire: i32,
        minimum: u32,
        serial: u32
    },
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
65
66
67
68
69
    // SRV(SRV),
    // SSHFP(SSHFP),
    // SVCB(SVCB),
    // TLSA(TLSA),
    // TXT(TXT),
70
71
72
73
74

    // TODO: Eventually allow deserialization of DNSSEC records
    #[serde(skip)]
    DNSSEC(trust_dns_types::DNSSECRData),
    #[serde(rename_all = "PascalCase")]
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
75
76
    Unknown {
        code: u16,
77
        data: String,
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
78
79
80
81
82
83
84
85
86
    },
    // ZERO,
}

impl From<trust_dns_types::RData> for RData {
    fn from(rdata: trust_dns_types::RData) -> RData {
        match rdata {
            trust_dns_types::RData::A(address) => RData::A { address },
            trust_dns_types::RData::AAAA(address) => RData::AAAA { address },
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
            // Still a draft, no iana number yet, I don't to put something that is not currently supported so that's why NULL and not unknown.
            // TODO: probably need better error here, I don't know what to do about that as this would require to change the From for something else.
            // (empty data because I'm lazy)
            trust_dns_types::RData::ANAME(_) =>  RData::NULL {
                data: String::new()
            },
            trust_dns_types::RData::CNAME(target) => RData::CNAME {
                target: target.to_utf8()
            },
            trust_dns_types::RData::CAA(caa) => RData::CAA {
                issuer_critical: caa.issuer_critical(),
                value: format!("{}", CAAValue(caa.value())),
                property_tag: caa.tag().as_str().to_string(),
            },
            trust_dns_types::RData::MX(mx) => RData::MX {
                preference: mx.preference(),
                mail_exchanger: mx.exchange().to_utf8()
            },
            trust_dns_types::RData::NULL(null) =>  RData::NULL {
                data: base64::encode(null.anything().map(|data| data.to_vec()).unwrap_or_default())
            },
            trust_dns_types::RData::NS(target) => RData::NS {
                target: target.to_utf8()
            },
            trust_dns_types::RData::PTR(target) => RData::PTR {
                target: target.to_utf8()
            },
            trust_dns_types::RData::SOA(soa) => RData::SOA {
                master_server_name: soa.mname().to_utf8(),
                maintainer_name: soa.rname().to_utf8(),
                refresh: soa.refresh(),
                retry: soa.retry(),
                expire: soa.expire(),
                minimum: soa.minimum(),
                serial: soa.serial()
            },
            trust_dns_types::RData::DNSSEC(data) => RData::DNSSEC(data),
            rdata => {
                let code = rdata.to_record_type().into();
                let mut data = Vec::new();
                let mut encoder = BinEncoder::new(&mut data);
                rdata.emit(&mut encoder).expect("could not encode data");

                RData::Unknown {
                    code,
                    data: base64::encode(data),
                }
            }
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
135
136
137
138
        }
    }
}

139
struct CAAValue<'a>(&'a trust_dns_types::caa::Value);
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
140

141
142
143
144
145
146
147
// trust_dns Display implementation panics if no parameters
// Implementation based on caa::emit_value
// Also the quotes are strips to render in JSON
impl<'a> fmt::Display for CAAValue<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        match self.0 {
            trust_dns_types::caa::Value::Issuer(name, parameters) => {
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
148

149
150
151
                if let Some(name) = name {
                    write!(f, "{}", name)?;
                }
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
152

153
154
155
                if name.is_none() && parameters.is_empty() {
                    write!(f, ";")?;
                }
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
156

157
158
159
160
161
162
                for value in parameters {
                    write!(f, "; {}", value)?;
                }
            }
            trust_dns_types::caa::Value::Url(url) => write!(f, "{}", url)?,
            trust_dns_types::caa::Value::Unknown(v) => write!(f, "{:?}", v)?,
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
163
        }
164
        Ok(())
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    }
}

#[derive(Deserialize, Serialize)]
pub enum DNSClass {
    IN,
    CH,
    HS,
    NONE,
    ANY,
    OPT(u16),
}

impl From<trust_dns_types::DNSClass> for DNSClass {
    fn from(dns_class: trust_dns_types::DNSClass) -> DNSClass {
        match dns_class {
            trust_dns_types::DNSClass::IN => DNSClass::IN,
            trust_dns_types::DNSClass::CH => DNSClass::CH,
            trust_dns_types::DNSClass::HS => DNSClass::HS,
            trust_dns_types::DNSClass::NONE => DNSClass::NONE,
            trust_dns_types::DNSClass::ANY => DNSClass::ANY,
            trust_dns_types::DNSClass::OPT(v) => DNSClass::OPT(v),
        }
    }
}


#[derive(Deserialize, Serialize)]
pub struct Record {
    #[serde(rename = "Name")]
195
    pub name: String,
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
196
    #[serde(rename = "Class")]
197
    pub dns_class: DNSClass,
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
198
    #[serde(rename = "TTL")]
199
    pub ttl: u32,
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
200
    #[serde(flatten)]
201
    pub rdata: RData,
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
202
203
204
205
206
}

impl From<trust_dns_types::Record> for Record {
    fn from(record: trust_dns_types::Record) -> Record {
        Record {
207
            name: record.name().to_utf8(),
Gaël Berthaud-Müller's avatar
Gaël Berthaud-Müller committed
208
209
210
211
212
213
214
            //rr_type: record.rr_type().into(),
            dns_class: record.dns_class().into(),
            ttl: record.ttl(),
            rdata: record.into_data().into(),
        }
    }
}