2017-12-27 14:24:11 +00:00
|
|
|
use bytes::{Bytes, BufMut};
|
2017-12-21 12:32:14 +00:00
|
|
|
use data_encoding::{self, HEXLOWER_PERMISSIVE};
|
2019-07-01 15:43:34 +00:00
|
|
|
use crate::errors::*;
|
2017-12-21 12:32:14 +00:00
|
|
|
use failure::{Fail, ResultExt};
|
2019-07-01 15:43:34 +00:00
|
|
|
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes, short_blob, write_short_blob, get_blob};
|
|
|
|
use crate::ser::text::*;
|
2017-12-21 12:32:14 +00:00
|
|
|
use std::fmt;
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
static WHITESPACE: &str = "\t\n\x0c\r "; // \f == \x0c formfeed
|
|
|
|
|
2019-07-01 15:43:34 +00:00
|
|
|
lazy_static::lazy_static!{
|
2017-12-21 12:32:14 +00:00
|
|
|
pub(crate) static ref HEXLOWER_PERMISSIVE_ALLOW_WS: data_encoding::Encoding = {
|
|
|
|
let mut spec = data_encoding::Specification::new();
|
|
|
|
spec.symbols.push_str("0123456789abcdef");
|
|
|
|
spec.translate.from.push_str("ABCDEF");
|
|
|
|
spec.translate.to.push_str("abcdef");
|
|
|
|
spec.ignore.push_str(WHITESPACE);
|
|
|
|
spec.encoding().unwrap()
|
|
|
|
};
|
|
|
|
|
|
|
|
static ref BASE64_ALLOW_WS: data_encoding::Encoding = {
|
|
|
|
let mut spec = data_encoding::Specification::new();
|
|
|
|
spec.symbols.push_str("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
|
|
|
|
spec.padding = Some('=');
|
|
|
|
spec.ignore.push_str(WHITESPACE);
|
|
|
|
spec.encoding().unwrap()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Similar to `ShortText`, but uses (unquoted, no spaces allowed) hex
|
|
|
|
// for text representation; "-" when empty
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct HexShortBlob(Bytes);
|
|
|
|
|
2019-09-08 15:06:38 +00:00
|
|
|
impl HexShortBlob {
|
|
|
|
pub fn new(data: Vec<u8>) -> Result<Self> {
|
|
|
|
failure::ensure!(data.len() < 256, "short hex blob must be at most 255 bytes long");
|
|
|
|
Ok(Self(data.into()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-21 12:32:14 +00:00
|
|
|
impl DnsPacketData for HexShortBlob {
|
|
|
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
|
|
|
Ok(HexShortBlob(short_blob(data)?))
|
|
|
|
}
|
2017-12-27 14:24:11 +00:00
|
|
|
|
|
|
|
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
|
|
write_short_blob(&self.0, packet)
|
|
|
|
}
|
2017-12-21 12:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DnsTextData for HexShortBlob {
|
2019-07-01 15:43:34 +00:00
|
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
2017-12-21 12:32:14 +00:00
|
|
|
let s = next_field(data)?;
|
|
|
|
if s == "-" {
|
|
|
|
Ok(HexShortBlob(Bytes::new()))
|
|
|
|
} else {
|
|
|
|
let raw = HEXLOWER_PERMISSIVE.decode(s.as_bytes())
|
|
|
|
.with_context(|e| e.context(format!("invalid hex: {:?}", s)))?;
|
2019-07-01 15:43:34 +00:00
|
|
|
failure::ensure!(raw.len() < 256, "short hex field must be at most 255 bytes long");
|
2017-12-21 12:32:14 +00:00
|
|
|
Ok(HexShortBlob(raw.into()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
|
|
if self.0.is_empty() {
|
|
|
|
write!(f, "-")
|
|
|
|
} else {
|
|
|
|
write!(f, "{}", HEXLOWER_PERMISSIVE.encode(&self.0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-04 21:54:43 +00:00
|
|
|
impl std::ops::Deref for HexShortBlob {
|
|
|
|
type Target = [u8];
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-27 20:50:51 +00:00
|
|
|
// 16-bit length, uses decimal length + base64 encoding (if length > 0)
|
|
|
|
// for text representation.
|
|
|
|
//
|
|
|
|
// In base64 encoding no whitespace allowed and padding required.
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct Base64LongBlob(Bytes);
|
|
|
|
|
2019-09-08 15:06:38 +00:00
|
|
|
impl Base64LongBlob {
|
|
|
|
pub fn new(data: Vec<u8>) -> Result<Self> {
|
|
|
|
failure::ensure!(data.len() < 0x1_0000, "long base64 blob must be at most 65535 bytes long");
|
|
|
|
Ok(Self(data.into()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-27 20:50:51 +00:00
|
|
|
impl DnsPacketData for Base64LongBlob {
|
|
|
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
|
|
|
let len = u16::deserialize(data)? as usize;
|
|
|
|
check_enough_data!(data, len, "data for long blob");
|
|
|
|
|
|
|
|
Ok(Base64LongBlob(get_blob(data, len)?))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
|
|
let len = self.0.len();
|
2019-07-01 15:43:34 +00:00
|
|
|
failure::ensure!(len < 0x1_0000, "blob too long");
|
2017-12-27 20:50:51 +00:00
|
|
|
(len as u16).serialize(context, packet)?;
|
|
|
|
packet.reserve(len);
|
|
|
|
packet.put_slice(&self.0);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DnsTextData for Base64LongBlob {
|
2019-07-01 15:43:34 +00:00
|
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
2017-12-27 20:50:51 +00:00
|
|
|
let length_field = next_field(data)?;
|
|
|
|
let length = length_field.parse::<u16>()
|
|
|
|
.with_context(|_| format!("invalid length for blob: {:?}", length_field))?;
|
|
|
|
|
|
|
|
if length > 0 {
|
|
|
|
let blob_field = next_field(data)?;
|
|
|
|
let result = BASE64_ALLOW_WS.decode(blob_field.as_bytes())
|
|
|
|
.with_context(|e| e.context(format!("invalid base64: {:?}", blob_field)))?;
|
|
|
|
Ok(Base64LongBlob(result.into()))
|
|
|
|
} else {
|
|
|
|
Ok(Base64LongBlob(Bytes::new()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
|
|
if self.0.len() >= 0x1_0000 { return Err(fmt::Error); }
|
|
|
|
if self.0.is_empty() {
|
|
|
|
write!(f, "0")
|
|
|
|
} else {
|
|
|
|
write!(f, "{} {}", self.0.len(), BASE64_ALLOW_WS.encode(&self.0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-04 21:54:43 +00:00
|
|
|
impl std::ops::Deref for Base64LongBlob {
|
|
|
|
type Target = [u8];
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-29 21:25:43 +00:00
|
|
|
/// No length byte (or restriction), just all data to end of record. uses
|
|
|
|
/// base64 encoding for text representation, whitespace allowed, padding
|
|
|
|
/// required.
|
|
|
|
///
|
|
|
|
/// No following field allowed, i.e. last field in the record.
|
2017-12-21 12:32:14 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct Base64RemainingBlob(Bytes);
|
|
|
|
|
2019-09-08 15:06:38 +00:00
|
|
|
impl Base64RemainingBlob {
|
|
|
|
pub fn new(data: Vec<u8>) -> Self {
|
|
|
|
Self(data.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-21 12:32:14 +00:00
|
|
|
impl DnsPacketData for Base64RemainingBlob {
|
|
|
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
|
|
|
Ok(Base64RemainingBlob(remaining_bytes(data)))
|
|
|
|
}
|
2017-12-27 14:24:11 +00:00
|
|
|
|
|
|
|
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
|
|
packet.reserve(self.0.len());
|
|
|
|
packet.put_slice(&self.0);
|
|
|
|
Ok(())
|
|
|
|
}
|
2017-12-21 12:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DnsTextData for Base64RemainingBlob {
|
2019-07-01 15:43:34 +00:00
|
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
2017-12-21 12:32:14 +00:00
|
|
|
skip_whitespace(data);
|
|
|
|
let result = BASE64_ALLOW_WS.decode(data.as_bytes())
|
|
|
|
.with_context(|e| e.context(format!("invalid base64: {:?}", data)))?;
|
|
|
|
*data = "";
|
|
|
|
Ok(Base64RemainingBlob(result.into()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
2017-12-27 20:50:51 +00:00
|
|
|
if !self.0.is_empty() {
|
|
|
|
write!(f, "{}", BASE64_ALLOW_WS.encode(&self.0))
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
2017-12-21 12:32:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-04 21:54:43 +00:00
|
|
|
impl std::ops::Deref for Base64RemainingBlob {
|
|
|
|
type Target = [u8];
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-29 21:25:43 +00:00
|
|
|
/// No length byte (or restriction), just all data to end of record. uses
|
|
|
|
/// hex encoding for text representation, whitespace allowed.
|
|
|
|
///
|
|
|
|
/// No following field allowed, i.e. last field in the record.
|
2017-12-21 12:32:14 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct HexRemainingBlob(Bytes);
|
|
|
|
|
2019-09-08 15:06:38 +00:00
|
|
|
impl HexRemainingBlob {
|
|
|
|
pub fn new(data: Vec<u8>) -> Self {
|
|
|
|
Self(data.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-21 12:32:14 +00:00
|
|
|
impl DnsPacketData for HexRemainingBlob {
|
|
|
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
|
|
|
Ok(HexRemainingBlob(remaining_bytes(data)))
|
|
|
|
}
|
2017-12-27 14:24:11 +00:00
|
|
|
|
|
|
|
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
|
|
packet.reserve(self.0.len());
|
|
|
|
packet.put_slice(&self.0);
|
|
|
|
Ok(())
|
|
|
|
}
|
2017-12-21 12:32:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DnsTextData for HexRemainingBlob {
|
2019-07-01 15:43:34 +00:00
|
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
2017-12-21 12:32:14 +00:00
|
|
|
skip_whitespace(data);
|
|
|
|
let result = HEXLOWER_PERMISSIVE_ALLOW_WS.decode(data.as_bytes())
|
|
|
|
.with_context(|e| e.context(format!("invalid hex: {:?}", data)))?;
|
|
|
|
*data = "";
|
|
|
|
Ok(HexRemainingBlob(result.into()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
|
|
write!(f, "{}", HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.0))
|
|
|
|
}
|
|
|
|
}
|
2019-07-02 15:37:40 +00:00
|
|
|
|
2019-07-04 21:54:43 +00:00
|
|
|
impl std::ops::Deref for HexRemainingBlob {
|
|
|
|
type Target = [u8];
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-02 15:37:40 +00:00
|
|
|
/// No length byte (or restriction), just all data to end of record. uses
|
|
|
|
/// hex encoding for text representation, whitespace allowed.
|
|
|
|
///
|
|
|
|
/// No following field allowed, i.e. last field in the record.
|
|
|
|
///
|
|
|
|
/// Must contain at least one byte
|
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
|
|
pub struct HexRemainingBlobNotEmpty(Bytes);
|
|
|
|
|
2019-09-08 15:06:38 +00:00
|
|
|
impl HexRemainingBlobNotEmpty {
|
|
|
|
pub fn new(data: Vec<u8>) -> Result<Self> {
|
|
|
|
failure::ensure!(!data.is_empty(), "must not be empty");
|
|
|
|
Ok(Self(data.into()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-02 15:37:40 +00:00
|
|
|
impl DnsPacketData for HexRemainingBlobNotEmpty {
|
|
|
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
|
|
|
let data = remaining_bytes(data);
|
|
|
|
failure::ensure!(!data.is_empty(), "must not be empty");
|
|
|
|
Ok(HexRemainingBlobNotEmpty(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
|
|
failure::ensure!(!self.0.is_empty(), "must not be empty");
|
|
|
|
packet.reserve(self.0.len());
|
|
|
|
packet.put_slice(&self.0);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DnsTextData for HexRemainingBlobNotEmpty {
|
|
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
|
|
|
skip_whitespace(data);
|
|
|
|
let result = HEXLOWER_PERMISSIVE_ALLOW_WS.decode(data.as_bytes())
|
|
|
|
.with_context(|e| e.context(format!("invalid hex: {:?}", data)))?;
|
|
|
|
*data = "";
|
|
|
|
failure::ensure!(!result.is_empty(), "must not be empty");
|
|
|
|
Ok(HexRemainingBlobNotEmpty(result.into()))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
|
|
if self.0.is_empty() { return Err(fmt::Error); }
|
|
|
|
write!(f, "{}", HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.0))
|
|
|
|
}
|
|
|
|
}
|
2019-07-04 21:54:43 +00:00
|
|
|
|
|
|
|
impl std::ops::Deref for HexRemainingBlobNotEmpty {
|
|
|
|
type Target = [u8];
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|