use byteorder::ByteOrder; use bytes::{Bytes, Buf, BufMut, BigEndian}; use common_types::{DnsCompressedName, Class, types}; use errors::*; use packet::Resource; use records::UnknownRecord; use ser::packet::{DnsPacketData, DnsPacketWriteContext, get_blob, remaining_bytes}; use std::io::{Cursor, Read}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum OptError { UnknownVersion, InvalidData, } /// parsing OPT can fail without having to throw away the packet /// completely; just fail in some way /// /// TODO: define some useful error... pub type OptResult = ::std::result::Result; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct OptFlag(pub u16); #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct OptFlags(pub u16); impl ::std::ops::BitOr for OptFlags { type Output = bool; fn bitor(self, rhs: OptFlag) -> Self::Output { 0 != self.0 & rhs.0 } } impl ::std::ops::BitOr for OptFlag { type Output = bool; fn bitor(self, rhs: OptFlags) -> Self::Output { 0 != self.0 & rhs.0 } } #[derive(Clone, PartialEq, Eq, Debug)] pub enum DnsOption { /// DNS Name Server Identifier (0x0003) NSID(Bytes), // DAU (0x0005) // DHU (0x0006) // N3U (0x0007) // edns-client-subnet (0x0008) ClientSubnet { source_prefix_length: u8, scope_prefix_length: u8, addr: IpAddr, }, // EDNS EXPIRE (0x0009) // COOKIE (0x000a) // edns-tcp-keepalive (0x000b) // Padding (0x000c) // CHAIN (0x000d) // edns-key-tag (0x000e) // DeviceID (26946 = 0x6942) Unknown { code: u16, data: Bytes, } } impl DnsOption { fn parse_client_subnet(data: &mut Cursor) -> Result { let addr_family = u16::deserialize(data)?; let source_prefix_length = u8::deserialize(data)?; let scope_prefix_length = u8::deserialize(data)?; let addr_prefix_len = ((source_prefix_length + 7) / 8) as usize; ensure!(scope_prefix_length <= source_prefix_length, "scope prefix {} > source prefix {}", scope_prefix_length, source_prefix_length); let addr = match addr_family { 1 => { ensure!(source_prefix_length <= 32, "invalid prefix for IPv4"); let mut o = [0u8; 4]; data.read_exact(&mut o[0..addr_prefix_len])?; if 0 != source_prefix_length % 8 { let mask = 0xff >> (source_prefix_length % 8); ensure!(0 == o[addr_prefix_len - 1] & mask, "non-zero padding"); } IpAddr::V4(Ipv4Addr::from(o)) }, 2 => { ensure!(source_prefix_length <= 128, "invalid prefix for IPv6"); let mut o = [0u8; 16]; data.read_exact(&mut o[0..addr_prefix_len])?; if 0 != source_prefix_length % 8 { let mask = 0xff >> (source_prefix_length % 8); ensure!(0 == o[addr_prefix_len - 1] & mask, "non-zero padding"); } IpAddr::V6(Ipv6Addr::from(o)) }, _ => { bail!("unknown address family {}", addr_family); }, }; Ok(DnsOption::ClientSubnet{ source_prefix_length, scope_prefix_length, addr, }) } fn parse_opt(code: u16, opt_data: Bytes) -> Result { let mut data = Cursor::new(opt_data); let result = (|| Ok(match code { 0x0003 => DnsOption::NSID(remaining_bytes(&mut data)), 0x0008 => DnsOption::parse_client_subnet(&mut data)?, _ => bail!("unknown option {}", code), }))()?; ensure!(!data.has_remaining(), "option data remaining: {} bytes", data.remaining()); Ok(result) } fn write_opt_data(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { match *self { DnsOption::NSID(ref id) => { packet.reserve(id.len()); packet.put_slice(id); }, DnsOption::ClientSubnet{source_prefix_length, scope_prefix_length, ref addr} => { let addr_prefix_len = ((source_prefix_length + 7) / 8) as usize; packet.reserve(4 + addr_prefix_len); packet.put_u16::(match *addr { IpAddr::V4(_) => 1, IpAddr::V6(_) => 2, }); packet.put_u8(source_prefix_length); packet.put_u8(scope_prefix_length); match *addr { IpAddr::V4(ref addr) => { let o = addr.octets(); packet.put_slice(&o[..addr_prefix_len]); }, IpAddr::V6(ref addr) => { let o = addr.octets(); packet.put_slice(&o[..addr_prefix_len]); }, } }, DnsOption::Unknown{ref data, ..} => { packet.reserve(data.len()); packet.put_slice(data); }, } Ok(()) } } impl DnsPacketData for DnsOption { fn deserialize(data: &mut Cursor) -> Result { let code = u16::deserialize(data)?; let opt_len = u16::deserialize(data)? as usize; let opt_data = get_blob(data, opt_len)?; DnsOption::parse_opt(code, opt_data.clone()).or_else(|_| { Ok(DnsOption::Unknown{ code: code, data: opt_data, }) }) } fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { let code: u16 = match *self { DnsOption::NSID(_) => 0x0003, DnsOption::ClientSubnet{..} => 0x0008, DnsOption::Unknown{code, ..} => code, }; code.serialize(context, packet)?; let opt_len_pos = packet.len(); packet.reserve(2); packet.put_u16::(0); // stub let opt_start = packet.len(); self.write_opt_data(context, packet)?; let opt_end = packet.len(); let opt_len = opt_end - opt_start; ensure!(opt_len < 0x1_0000, "OPTION DATA too big"); // now patch length BigEndian::write_u16(&mut packet[opt_len_pos..][..2], opt_len as u16); Ok(()) } } pub const DNSSEC_OK: OptFlag = OptFlag(0x8000); #[derive(Clone, PartialEq, Eq, Debug)] pub struct Opt { // "CLASS" pub udp_payload_size: u16, // "TTL" pub extended_rcode_high: u8, pub version: u8, pub flags: OptFlags, // RDATA pub options: Vec, } impl Opt { fn deserialize_options(ur: &UnknownRecord) -> Result> { let mut data = Cursor::new(ur.raw().clone()); let mut options = Vec::new(); while data.has_remaining() { options.push(DnsOption::deserialize(&mut data)?); } Ok(options) } pub fn deserialize(r: &Resource) -> Result> { let udp_payload_size = r.class.0; let extended_rcode_high = (r.ttl >> 24) as u8; let version = (r.ttl >> 16) as u8; let flags = OptFlags(r.ttl as u16); if version > 0 { return Ok(Err(OptError::UnknownVersion)); } let ur = match r.data.as_any().downcast_ref::() { Some(ur) => ur, None => bail!("need to parse OPT from UnknownRecord"), }; let options = match Opt::deserialize_options(ur) { Ok(options) => options, Err(_) => return Ok(Err(OptError::InvalidData)), }; Ok(Ok(Opt { udp_payload_size, extended_rcode_high, version, flags, options, })) } pub fn serialize(&self) -> Result { let mut data = Vec::new(); let mut ctx = DnsPacketWriteContext::new(); for o in &self.options { o.serialize(&mut ctx, &mut data)?; } let ur = UnknownRecord::new(types::OPT, data.into()); let ttl = ((self.extended_rcode_high as u32) << 24) | ((self.version as u32) << 16) | self.flags.0 as u32; Ok(Resource{ name: DnsCompressedName::new_root(), class: Class(self.udp_payload_size), ttl: ttl, data: Box::new(ur) as _, }) } } impl Default for Opt { fn default() -> Self { Opt { udp_payload_size: 500, extended_rcode_high: 0, version: 0, flags: OptFlags(0), options: Vec::new(), } } }