rust-dnsbox/lib/dnsbox-base/src/packet/opt.rs

286 lines
7.2 KiB
Rust

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(),
}
}
}