migamake_api_cloudflare/
dns.rs

1//! Defines various type of DNS record structs to be used with
2//! [Create DNS record](crate::Cloudflare::create_dns_record)
3
4#![allow(clippy::new_without_default)]
5
6use crate::parameters::DnsRecordType;
7use serde::Serialize;
8use std::net::{Ipv4Addr, Ipv6Addr};
9use validator::{Validate, ValidationError};
10
11fn is_ipv4(content: &str) -> Result<(), ValidationError> {
12    let result = content.parse::<Ipv4Addr>();
13
14    match result {
15        Ok(_v) => Ok(()),
16        Err(_e) => Err(ValidationError::new("IPv4")),
17    }
18}
19
20fn is_ipv6(content: &str) -> Result<(), ValidationError> {
21    let result = content.parse::<Ipv6Addr>();
22
23    match result {
24        Ok(_v) => Ok(()),
25        Err(_e) => Err(ValidationError::new("IPv6")),
26    }
27}
28
29fn is_valid_spf(content: &str) -> Result<(), ValidationError> {
30    if content.starts_with("v=spf1") {
31        return Ok(());
32    }
33    Err(ValidationError::new("SPF"))
34}
35
36#[cfg(test)]
37mod tests {
38
39    use crate::dns::*;
40    use crate::parameters::DnsRecordType;
41
42    #[test]
43    fn should_have_a_type() {
44        let ipv4_record = ARecord::new();
45        assert_eq!(ipv4_record.record_type, DnsRecordType::A)
46    }
47    #[test]
48    fn should_have_aaaa_type() {
49        let ipv6_record = AAAARecord::new();
50        assert_eq!(ipv6_record.record_type, DnsRecordType::AAAA)
51    }
52
53    #[test]
54    #[should_panic]
55    fn should_panic_for_bad_spf_content() {
56        let mut spf = SPFRecord::new();
57        spf.content = "start wrong".into();
58        spf.validate().unwrap();
59    }
60}
61/// To create A record
62///
63/// The record that holds the IP address of a domain
64#[derive(Debug, Validate, Serialize)]
65pub struct ARecord {
66    #[serde(rename(serialize = "type"))]
67    record_type: DnsRecordType,
68    #[validate(length(min = 1, max = 255))]
69    pub name: String,
70    #[validate(custom(
71        function = "is_ipv4",
72        message = "The provided content is not a valid IPv4 address"
73    ))]
74    pub content: String,
75    pub ttl: u32,
76}
77
78impl ARecord {
79    pub fn new() -> ARecord {
80        ARecord {
81            record_type: DnsRecordType::A,
82            name: "".into(),
83            content: "".into(),
84            ttl: 1,
85        }
86    }
87}
88/// To create a AAAA Record
89///
90/// The record that holds the IP address of a domain in IPv6 format
91#[derive(Debug, Validate, Serialize)]
92pub struct AAAARecord {
93    #[serde(rename(serialize = "type"))]
94    record_type: DnsRecordType,
95    #[validate(length(max = 255))]
96    pub name: String,
97    #[validate(custom(
98        function = "is_ipv6",
99        message = "The provided content is not a valid IPv6 address"
100    ))]
101    pub content: String,
102    pub ttl: u32,
103}
104
105impl AAAARecord {
106    pub fn new() -> AAAARecord {
107        AAAARecord {
108            record_type: DnsRecordType::AAAA,
109            name: "".into(),
110            content: "".into(),
111            ttl: 1,
112        }
113    }
114}
115
116/// To create a CNAME Record
117///
118/// Forwards one domain or subdomain to another domain, does NOT provide an IP address
119#[derive(Debug, Validate, Serialize)]
120pub struct CNAMERecord {
121    #[serde(rename(serialize = "type"))]
122    pub record_type: DnsRecordType,
123    #[validate(length(max = 255))]
124    pub name: String,
125    #[validate(length(max = 255))]
126    pub content: String,
127    pub ttl: u32,
128}
129
130impl CNAMERecord {
131    pub fn new() -> CNAMERecord {
132        CNAMERecord {
133            record_type: DnsRecordType::CNAME,
134            name: "".into(),
135            content: "".into(),
136            ttl: 1,
137        }
138    }
139}
140/// To create a TXT Record
141///
142/// Lets an admin store text notes in the record.
143#[derive(Debug, Validate, Serialize)]
144pub struct TXTRecord {
145    #[serde(rename(serialize = "type"))]
146    record_type: DnsRecordType,
147    #[validate(length(max = 255))]
148    pub name: String,
149    #[validate(length(max = 255))]
150    pub content: String,
151    pub ttl: u32,
152}
153
154impl TXTRecord {
155    pub fn new() -> TXTRecord {
156        TXTRecord {
157            record_type: DnsRecordType::TXT,
158            name: "".into(),
159            content: "".into(),
160            ttl: 1,
161        }
162    }
163}
164/// To create a MX Record
165///
166/// Directs mail to an email server.
167#[derive(Debug, Validate, Serialize)]
168pub struct MXRecord {
169    #[serde(rename(serialize = "type"))]
170    pub record_type: DnsRecordType,
171    #[validate(length(max = 255))]
172    pub name: String,
173    pub content: String,
174    pub ttl: u32,
175    #[validate(range(min = 0, max = 65535))]
176    pub priority: u32,
177}
178
179impl MXRecord {
180    pub fn new() -> MXRecord {
181        MXRecord {
182            record_type: DnsRecordType::MX,
183            name: "".into(),
184            content: "".into(),
185            ttl: 1,
186            priority: 0,
187        }
188    }
189}
190
191/// To create a NS Record
192///
193/// Stores the name server for a DNS entry.
194#[derive(Debug, Validate, Serialize)]
195pub struct NSRecord {
196    #[serde(rename(serialize = "type"))]
197    pub record_type: DnsRecordType,
198    #[validate(length(max = 255))]
199    pub name: String,
200    pub content: String,
201    pub ttl: u32,
202}
203
204impl NSRecord {
205    pub fn new() -> NSRecord {
206        NSRecord {
207            record_type: DnsRecordType::NS,
208            name: "".into(),
209            content: "".into(),
210            ttl: 1,
211        }
212    }
213}
214/// To create a SRV Record
215///
216/// Specifies a port for specific services.
217#[derive(Debug, Validate, Serialize)]
218pub struct SRVRecord {
219    #[serde(rename(serialize = "type"))]
220    pub record_type: DnsRecordType,
221    #[validate(length(max = 255))]
222    pub name: String,
223    pub content: String,
224    pub ttl: u32,
225}
226
227impl SRVRecord {
228    pub fn new() -> SRVRecord {
229        SRVRecord {
230            record_type: DnsRecordType::SRV,
231            name: "".into(),
232            content: "".into(),
233            ttl: 1,
234        }
235    }
236}
237/// To create a CAA Record
238///
239/// This is the ‘certification authority authorization’ record
240#[derive(Debug, Validate, Serialize)]
241pub struct CAARecord {
242    #[serde(rename(serialize = "type"))]
243    pub record_type: DnsRecordType,
244    #[validate(length(max = 255))]
245    pub name: String,
246    pub ttl: u32,
247    data: CAAData,
248}
249/// Fields required for [CAARecord struct](CAARecord)
250#[derive(Debug, Validate, Serialize)]
251pub(crate) struct CAAData {
252    flags: u8,
253    tag: String,
254    value: String,
255}
256
257impl CAARecord {
258    pub fn new(ca_authority: String) -> CAARecord {
259        let data = CAAData {
260            flags: 0,
261            tag: "issue".into(),
262            value: ca_authority,
263        };
264        CAARecord {
265            record_type: DnsRecordType::CAA,
266            name: "".into(),
267            ttl: 1,
268            data,
269        }
270    }
271}
272/// To create a PTR Record
273///
274/// Provides a domain name in reverse-lookups.
275#[derive(Debug, Validate, Serialize)]
276pub struct PTRRecord {
277    #[serde(rename(serialize = "type"))]
278    pub record_type: DnsRecordType,
279    #[validate(length(max = 255))]
280    pub name: String,
281    pub content: String,
282    pub ttl: u32,
283}
284
285impl PTRRecord {
286    pub fn new() -> PTRRecord {
287        PTRRecord {
288            record_type: DnsRecordType::PTR,
289            name: "".into(),
290            content: "".into(),
291            ttl: 1,
292        }
293    }
294}
295/// To create a SPF Record
296///
297/// used to indicate to mail exchanges which hosts are authorized to send mail for a domain
298#[derive(Debug, Validate, Serialize)]
299pub struct SPFRecord {
300    #[serde(rename(serialize = "type"))]
301    pub record_type: DnsRecordType,
302    #[validate(length(max = 255))]
303    pub name: String,
304    #[validate(custom(
305        function = "is_valid_spf",
306        message = "The provided content should start with v=spf1"
307    ))]
308    pub content: String,
309    pub ttl: u32,
310}
311
312impl SPFRecord {
313    pub fn new() -> SPFRecord {
314        SPFRecord {
315            record_type: DnsRecordType::SPF,
316            name: "".into(),
317            content: "".into(),
318            ttl: 1,
319        }
320    }
321}