
10 changed files with 435 additions and 20 deletions
@ -0,0 +1,285 @@
|
||||
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<T> = ::std::result::Result<T, OptError>; |
||||
|
||||
#[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<OptFlag> for OptFlags { |
||||
type Output = bool; |
||||
|
||||
fn bitor(self, rhs: OptFlag) -> Self::Output { |
||||
0 != self.0 & rhs.0 |
||||
} |
||||
} |
||||
|
||||
impl ::std::ops::BitOr<OptFlags> 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<Bytes>) -> Result<Self> { |
||||
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<Self> { |
||||
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<u8>) -> 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::<BigEndian>(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<Bytes>) -> Result<Self> { |
||||
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<u8>) -> 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::<BigEndian>(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<DnsOption>, |
||||
} |
||||
|
||||
impl Opt { |
||||
fn deserialize_options(ur: &UnknownRecord) -> Result<Vec<DnsOption>> { |
||||
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<OptResult<Self>> { |
||||
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::<UnknownRecord>() { |
||||
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<Resource> { |
||||
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(), |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue