1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 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
use std::net::{Ipv4Addr, Ipv6Addr};

use byteorder::{BigEndian, ByteOrder};

use {Name, Type, Error, SoaRecord};
use std::str;

/// The enumeration that represents known types of DNS resource records data
#[derive(Debug)]
#[allow(missing_docs)] // resource records are pretty self-descriptive
pub enum RRData<'a> {
    CNAME(Name<'a>),
    NS(Name<'a>),
    A(Ipv4Addr),
    AAAA(Ipv6Addr),
    SRV { priority: u16, weight: u16, port: u16, target: Name<'a> },
    SOA(SoaRecord<'a>),
    PTR(Name<'a>),
    MX { preference: u16, exchange: Name<'a> },
    TXT(String),
    /// Anything that can't be parsed yet
    Unknown(&'a [u8]),
}

impl<'a> RRData<'a> {
    /// Parse an RR data and return RRData enumeration
    pub fn parse(typ: Type, rdata: &'a [u8], original: &'a [u8])
        -> Result<RRData<'a>, Error>
    {
        match typ {
            Type::A => {
                if rdata.len() != 4 {
                    return Err(Error::WrongRdataLength);
                }
                Ok(RRData::A(
                    Ipv4Addr::from(BigEndian::read_u32(rdata))))
            }
            Type::AAAA => {
                if rdata.len() != 16 {
                    return Err(Error::WrongRdataLength);
                }
                Ok(RRData::AAAA(Ipv6Addr::new(
                    BigEndian::read_u16(&rdata[0..2]),
                    BigEndian::read_u16(&rdata[2..4]),
                    BigEndian::read_u16(&rdata[4..6]),
                    BigEndian::read_u16(&rdata[6..8]),
                    BigEndian::read_u16(&rdata[8..10]),
                    BigEndian::read_u16(&rdata[10..12]),
                    BigEndian::read_u16(&rdata[12..14]),
                    BigEndian::read_u16(&rdata[14..16]),
                )))
            }
            Type::CNAME => {
                Ok(RRData::CNAME(try!(Name::scan(rdata, original))))
            }
            Type::NS => {
                Ok(RRData::NS(try!(Name::scan(rdata, original))))
            }
            Type::MX => {
                if rdata.len() < 3 {
                    return Err(Error::WrongRdataLength);
                }
                Ok(RRData::MX {
                    preference: BigEndian::read_u16(&rdata[..2]),
                    exchange: try!(Name::scan(&rdata[2..], original)),
                })
            }
            Type::PTR => {
                Ok(RRData::PTR(try!(Name::scan(rdata, original))))
            }
            Type::SOA => {
                let mut pos = 0;
                let primary_name_server = try!(Name::scan(rdata, original));
                pos += primary_name_server.byte_len();
                let mailbox = try!(Name::scan(&rdata[pos..], original));
                pos += mailbox.byte_len();
                if rdata[pos..].len() < 20 {
                    return Err(Error::WrongRdataLength);
                }
                Ok(RRData::SOA(SoaRecord {
                    primary_ns: primary_name_server,
                    mailbox: mailbox,
                    serial: BigEndian::read_u32(&rdata[pos..(pos+4)]),
                    refresh: BigEndian::read_u32(&rdata[(pos+4)..(pos+8)]),
                    retry: BigEndian::read_u32(&rdata[(pos+8)..(pos+12)]),
                    expire: BigEndian::read_u32(&rdata[(pos+12)..(pos+16)]),
                    minimum_ttl: BigEndian::read_u32(&rdata[(pos+16)..(pos+20)]),
                }))
            }
            Type::SRV => {
                if rdata.len() < 7 {
                    return Err(Error::WrongRdataLength);
                }
                Ok(RRData::SRV {
                    priority: BigEndian::read_u16(&rdata[..2]),
                    weight: BigEndian::read_u16(&rdata[2..4]),
                    port: BigEndian::read_u16(&rdata[4..6]),
                    target: try!(Name::scan(&rdata[6..], original)),
                })
            }
            Type::TXT => {
                let len = rdata.len();
                if len < 1 {
                    return Err(Error::WrongRdataLength);
                }
                let mut ret_string = String::new();
                let mut pos = 0;
                while pos < len {
                    let rdlen = rdata[pos] as usize;
                    pos += 1;
                    if len < rdlen + pos {
                        return Err(Error::WrongRdataLength);
                    }
                    match str::from_utf8(&rdata[pos..(pos+rdlen)]) {
                        Ok(val) => ret_string.push_str(val),
                        Err(e) => return Err(Error::TxtDataIsNotUTF8(e)),
                    }
                    pos += rdlen;
                }
                Ok(RRData::TXT(ret_string))
            }
            _ => {
                Ok(RRData::Unknown(rdata))
            }
        }
    }
}