#![allow(clippy::new_without_default)]
use crate::parameters::DnsRecordType;
use serde::Serialize;
use std::net::{Ipv4Addr, Ipv6Addr};
use validator::{Validate, ValidationError};
fn is_ipv4(content: &str) -> Result<(), ValidationError> {
let result = content.parse::<Ipv4Addr>();
match result {
Ok(_v) => Ok(()),
Err(_e) => Err(ValidationError::new("IPv4")),
}
}
fn is_ipv6(content: &str) -> Result<(), ValidationError> {
let result = content.parse::<Ipv6Addr>();
match result {
Ok(_v) => Ok(()),
Err(_e) => Err(ValidationError::new("IPv6")),
}
}
fn is_valid_spf(content: &str) -> Result<(), ValidationError> {
if content.starts_with("v=spf1") {
return Ok(());
}
Err(ValidationError::new("SPF"))
}
#[cfg(test)]
mod tests {
use crate::dns::*;
use crate::parameters::DnsRecordType;
#[test]
fn should_have_a_type() {
let ipv4_record = ARecord::new();
assert_eq!(ipv4_record.record_type, DnsRecordType::A)
}
#[test]
fn should_have_aaaa_type() {
let ipv6_record = AAAARecord::new();
assert_eq!(ipv6_record.record_type, DnsRecordType::AAAA)
}
#[test]
#[should_panic]
fn should_panic_for_bad_spf_content() {
let mut spf = SPFRecord::new();
spf.content = "start wrong".into();
spf.validate().unwrap();
}
}
#[derive(Debug, Validate, Serialize)]
pub struct ARecord {
#[serde(rename(serialize = "type"))]
record_type: DnsRecordType,
#[validate(length(min = 1, max = 255))]
pub name: String,
#[validate(custom(
function = "is_ipv4",
message = "The provided content is not a valid IPv4 address"
))]
pub content: String,
pub ttl: u32,
}
impl ARecord {
pub fn new() -> ARecord {
ARecord {
record_type: DnsRecordType::A,
name: "".into(),
content: "".into(),
ttl: 1,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct AAAARecord {
#[serde(rename(serialize = "type"))]
record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
#[validate(custom(
function = "is_ipv6",
message = "The provided content is not a valid IPv6 address"
))]
pub content: String,
pub ttl: u32,
}
impl AAAARecord {
pub fn new() -> AAAARecord {
AAAARecord {
record_type: DnsRecordType::AAAA,
name: "".into(),
content: "".into(),
ttl: 1,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct CNAMERecord {
#[serde(rename(serialize = "type"))]
pub record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
#[validate(length(max = 255))]
pub content: String,
pub ttl: u32,
}
impl CNAMERecord {
pub fn new() -> CNAMERecord {
CNAMERecord {
record_type: DnsRecordType::CNAME,
name: "".into(),
content: "".into(),
ttl: 1,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct TXTRecord {
#[serde(rename(serialize = "type"))]
record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
#[validate(length(max = 255))]
pub content: String,
pub ttl: u32,
}
impl TXTRecord {
pub fn new() -> TXTRecord {
TXTRecord {
record_type: DnsRecordType::TXT,
name: "".into(),
content: "".into(),
ttl: 1,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct MXRecord {
#[serde(rename(serialize = "type"))]
pub record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
pub content: String,
pub ttl: u32,
#[validate(range(min = 0, max = 65535))]
pub priority: u32,
}
impl MXRecord {
pub fn new() -> MXRecord {
MXRecord {
record_type: DnsRecordType::MX,
name: "".into(),
content: "".into(),
ttl: 1,
priority: 0,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct NSRecord {
#[serde(rename(serialize = "type"))]
pub record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
pub content: String,
pub ttl: u32,
}
impl NSRecord {
pub fn new() -> NSRecord {
NSRecord {
record_type: DnsRecordType::NS,
name: "".into(),
content: "".into(),
ttl: 1,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct SRVRecord {
#[serde(rename(serialize = "type"))]
pub record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
pub content: String,
pub ttl: u32,
}
impl SRVRecord {
pub fn new() -> SRVRecord {
SRVRecord {
record_type: DnsRecordType::SRV,
name: "".into(),
content: "".into(),
ttl: 1,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct CAARecord {
#[serde(rename(serialize = "type"))]
pub record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
pub ttl: u32,
data: CAAData,
}
#[derive(Debug, Validate, Serialize)]
pub(crate) struct CAAData {
flags: u8,
tag: String,
value: String,
}
impl CAARecord {
pub fn new(ca_authority: String) -> CAARecord {
let data = CAAData {
flags: 0,
tag: "issue".into(),
value: ca_authority,
};
CAARecord {
record_type: DnsRecordType::CAA,
name: "".into(),
ttl: 1,
data,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct PTRRecord {
#[serde(rename(serialize = "type"))]
pub record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
pub content: String,
pub ttl: u32,
}
impl PTRRecord {
pub fn new() -> PTRRecord {
PTRRecord {
record_type: DnsRecordType::PTR,
name: "".into(),
content: "".into(),
ttl: 1,
}
}
}
#[derive(Debug, Validate, Serialize)]
pub struct SPFRecord {
#[serde(rename(serialize = "type"))]
pub record_type: DnsRecordType,
#[validate(length(max = 255))]
pub name: String,
#[validate(custom(
function = "is_valid_spf",
message = "The provided content should start with v=spf1"
))]
pub content: String,
pub ttl: u32,
}
impl SPFRecord {
pub fn new() -> SPFRecord {
SPFRecord {
record_type: DnsRecordType::SPF,
name: "".into(),
content: "".into(),
ttl: 1,
}
}
}