mirror of
https://github.com/NLnetLabs/domain.git
synced 2026-09-27 12:14:55 +02:00
Check for record data length upon creation. (#169)
This commit changes the new function of the following record types to check whether the wire format representation of the record data is too long and thus returns a result: Tsig<_, _>, Dnskey<_>, Rrsig<_, _>, Ds<_>, Cdnskey<_>, Cds<_>. In addition, it changes the Null::new to Null::from_octets.
This commit is contained in:
+8
-1
@@ -114,6 +114,9 @@ impl Opt<[u8]> {
|
||||
|
||||
/// Checks that the slice contains acceptable OPT record data.
|
||||
fn check_slice(slice: &[u8]) -> Result<(), ParseError> {
|
||||
if slice.len() > usize::from(u16::MAX) {
|
||||
return Err(FormError::new("long record data").into());
|
||||
}
|
||||
let mut parser = Parser::from_ref(slice);
|
||||
while parser.remaining() > 0 {
|
||||
parser.advance(2)?;
|
||||
@@ -126,11 +129,15 @@ impl Opt<[u8]> {
|
||||
|
||||
impl<Octs: AsRef<[u8]> + ?Sized> Opt<Octs> {
|
||||
/// Returns the length of the OPT record data.
|
||||
#[allow(clippy::len_without_is_empty)] // never empty.
|
||||
pub fn len(&self) -> usize {
|
||||
self.octets.as_ref().len()
|
||||
}
|
||||
|
||||
/// Returns whether the OPT record data is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.octets.as_ref().is_empty()
|
||||
}
|
||||
|
||||
/// Returns an iterator over options of a given type.
|
||||
///
|
||||
/// The returned iterator will return only options represented by type
|
||||
|
||||
+17
-6
@@ -243,11 +243,8 @@ impl<Octs> UnknownRecordData<Octs> {
|
||||
where
|
||||
Octs: AsRef<[u8]>,
|
||||
{
|
||||
if data.as_ref().len() > 0xFFFF {
|
||||
Err(LongRecordData())
|
||||
} else {
|
||||
Ok(UnknownRecordData { rtype, data })
|
||||
}
|
||||
LongRecordData::check_len(data.as_ref().len())?;
|
||||
Ok(UnknownRecordData { rtype, data })
|
||||
}
|
||||
|
||||
/// Returns the record type this data is for.
|
||||
@@ -450,9 +447,23 @@ impl<Octs: AsRef<[u8]>> fmt::Debug for UnknownRecordData<Octs> {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct LongRecordData();
|
||||
|
||||
impl LongRecordData {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
"record data too long"
|
||||
}
|
||||
|
||||
pub fn check_len(len: usize) -> Result<(), Self> {
|
||||
if len > usize::from(u16::MAX) {
|
||||
Err(LongRecordData())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LongRecordData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("record data too long")
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ macro_rules! rdata_types {
|
||||
Unknown($crate::base::rdata::UnknownRecordData<O>),
|
||||
}
|
||||
|
||||
impl<Octets: AsRef<[u8]>, Name> ZoneRecordData<Octets, Name> {
|
||||
impl<Octets: AsRef<[u8]>, Name: ToDname> ZoneRecordData<Octets, Name> {
|
||||
/// Scans a value of the given rtype.
|
||||
///
|
||||
/// If the record data is given via the notation for unknown
|
||||
|
||||
+157
-78
@@ -9,14 +9,16 @@ use crate::base::cmp::CanonicalOrd;
|
||||
use crate::base::iana::Rtype;
|
||||
use crate::base::name::{Dname, ParsedDname, PushError, ToDname};
|
||||
use crate::base::net::Ipv4Addr;
|
||||
use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData};
|
||||
use crate::base::rdata::{
|
||||
ComposeRecordData, LongRecordData, ParseRecordData, RecordData
|
||||
};
|
||||
use crate::base::scan::{Scan, Scanner, ScannerError, Symbol};
|
||||
use crate::base::serial::Serial;
|
||||
use crate::base::wire::{Compose, Composer, Parse, ParseError};
|
||||
use crate::base::wire::{Compose, Composer, FormError, Parse, ParseError};
|
||||
#[cfg(feature = "bytes")]
|
||||
use bytes::BytesMut;
|
||||
use core::cmp::Ordering;
|
||||
use core::convert::Infallible;
|
||||
use core::convert::{Infallible, TryFrom};
|
||||
use core::str::FromStr;
|
||||
use core::{fmt, hash, ops, str};
|
||||
use octseq::builder::{
|
||||
@@ -937,7 +939,7 @@ dname_type_well_known! {
|
||||
/// The Null record type is defined in RFC 1035, section 3.3.10.
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Null<Octs> {
|
||||
pub struct Null<Octs: ?Sized> {
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(
|
||||
@@ -953,21 +955,54 @@ pub struct Null<Octs> {
|
||||
}
|
||||
|
||||
impl<Octs> Null<Octs> {
|
||||
/// Creates new, empty owned Null record data.
|
||||
pub fn new(data: Octs) -> Self {
|
||||
Null { data }
|
||||
/// Creates new NULL record data from the given octets.
|
||||
///
|
||||
/// The function will fail if `data` is longer than 65,535 octets.
|
||||
pub fn from_octets(data: Octs) -> Result<Self, LongRecordData>
|
||||
where Octs: AsRef<[u8]> {
|
||||
Null::check_slice(data.as_ref())?;
|
||||
Ok(unsafe { Self::from_octets_unchecked(data) })
|
||||
}
|
||||
|
||||
/// Creates new NULL record data without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller has to ensure that `data` is at most 65,535 octets long.
|
||||
pub unsafe fn from_octets_unchecked(data: Octs) -> Self {
|
||||
Null { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl Null<[u8]> {
|
||||
/// Creates new NULL record data from an octets slice.
|
||||
///
|
||||
/// The function will fail if `data` is longer than 65,535 octets.
|
||||
pub fn from_slice(data: &[u8]) -> Result<&Self, LongRecordData> {
|
||||
Self::check_slice(data)?;
|
||||
Ok(unsafe { Self::from_slice_unchecked(data) })
|
||||
}
|
||||
|
||||
/// Creates new NULL record from an octets slice data without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller has to ensure that `data` is at most 65,535 octets long.
|
||||
pub unsafe fn from_slice_unchecked(data: &[u8]) -> &Self {
|
||||
&*(data as *const [u8] as *const Self)
|
||||
}
|
||||
|
||||
/// Checks that a slice can be used for NULL record data.
|
||||
fn check_slice(slice: &[u8]) -> Result<(), LongRecordData> {
|
||||
LongRecordData::check_len(slice.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs: ?Sized> Null<Octs> {
|
||||
/// The raw content of the record.
|
||||
pub fn data(&self) -> &Octs {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
|
||||
self,
|
||||
) -> Result<Null<Target>, Target::Error> {
|
||||
Ok(Null::new(self.data.try_octets_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs: AsRef<[u8]>> Null<Octs> {
|
||||
@@ -980,12 +1015,24 @@ impl<Octs: AsRef<[u8]>> Null<Octs> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs> Null<Octs> {
|
||||
pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
|
||||
self,
|
||||
) -> Result<Null<Target>, Target::Error> {
|
||||
Ok(unsafe {
|
||||
Null::from_octets_unchecked(self.data.try_octets_into()?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs> Null<Octs> {
|
||||
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
|
||||
parser: &mut Parser<'a, Src>,
|
||||
) -> Result<Self, ParseError> {
|
||||
let len = parser.remaining();
|
||||
parser.parse_octets(len).map(Self::new).map_err(Into::into)
|
||||
parser.parse_octets(len).map(|res| {
|
||||
unsafe { Self::from_octets_unchecked(res) }
|
||||
}).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,15 +1041,11 @@ impl<SrcOcts> Null<SrcOcts> {
|
||||
where
|
||||
Octs: OctetsFrom<SrcOcts>,
|
||||
{
|
||||
Ok(Null::new(self.data.try_octets_into().map_err(Into::into)?))
|
||||
}
|
||||
}
|
||||
|
||||
//--- From
|
||||
|
||||
impl<Octs> From<Octs> for Null<Octs> {
|
||||
fn from(data: Octs) -> Self {
|
||||
Self::new(data)
|
||||
Ok(unsafe {
|
||||
Null::from_octets_unchecked(
|
||||
self.data.try_octets_into().map_err(Into::into)?
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1015,7 +1058,9 @@ where
|
||||
type Error = Octs::Error;
|
||||
|
||||
fn try_octets_from(source: Null<SrcOcts>) -> Result<Self, Self::Error> {
|
||||
Octs::try_octets_from(source.data).map(Self::new)
|
||||
Octs::try_octets_from(source.data).map(|res| {
|
||||
unsafe { Self::from_octets_unchecked(res) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1023,22 +1068,22 @@ where
|
||||
|
||||
impl<Octs, Other> PartialEq<Null<Other>> for Null<Octs>
|
||||
where
|
||||
Octs: AsRef<[u8]>,
|
||||
Other: AsRef<[u8]>,
|
||||
Octs: AsRef<[u8]> + ?Sized,
|
||||
Other: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn eq(&self, other: &Null<Other>) -> bool {
|
||||
self.data.as_ref().eq(other.data.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs: AsRef<[u8]>> Eq for Null<Octs> {}
|
||||
impl<Octs: AsRef<[u8]> + ?Sized> Eq for Null<Octs> {}
|
||||
|
||||
//--- PartialOrd, CanonicalOrd, and Ord
|
||||
|
||||
impl<Octs, Other> PartialOrd<Null<Other>> for Null<Octs>
|
||||
where
|
||||
Octs: AsRef<[u8]>,
|
||||
Other: AsRef<[u8]>,
|
||||
Octs: AsRef<[u8]> + ?Sized,
|
||||
Other: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Null<Other>) -> Option<Ordering> {
|
||||
self.data.as_ref().partial_cmp(other.data.as_ref())
|
||||
@@ -1047,15 +1092,15 @@ where
|
||||
|
||||
impl<Octs, Other> CanonicalOrd<Null<Other>> for Null<Octs>
|
||||
where
|
||||
Octs: AsRef<[u8]>,
|
||||
Other: AsRef<[u8]>,
|
||||
Octs: AsRef<[u8]> + ?Sized,
|
||||
Other: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn canonical_cmp(&self, other: &Null<Other>) -> Ordering {
|
||||
self.data.as_ref().cmp(other.data.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs: AsRef<[u8]>> Ord for Null<Octs> {
|
||||
impl<Octs: AsRef<[u8]> + ?Sized> Ord for Null<Octs> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.data.as_ref().cmp(other.data.as_ref())
|
||||
}
|
||||
@@ -1063,7 +1108,7 @@ impl<Octs: AsRef<[u8]>> Ord for Null<Octs> {
|
||||
|
||||
//--- Hash
|
||||
|
||||
impl<Octs: AsRef<[u8]>> hash::Hash for Null<Octs> {
|
||||
impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for Null<Octs> {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
self.data.as_ref().hash(state)
|
||||
}
|
||||
@@ -1071,7 +1116,7 @@ impl<Octs: AsRef<[u8]>> hash::Hash for Null<Octs> {
|
||||
|
||||
//--- RecordData, ParseRecordData, ComposeRecordData
|
||||
|
||||
impl<Octs> RecordData for Null<Octs> {
|
||||
impl<Octs: ?Sized> RecordData for Null<Octs> {
|
||||
fn rtype(&self) -> Rtype {
|
||||
Rtype::Null
|
||||
}
|
||||
@@ -1093,7 +1138,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs: AsRef<[u8]>> ComposeRecordData for Null<Octs> {
|
||||
impl<Octs: AsRef<[u8]> + ?Sized> ComposeRecordData for Null<Octs> {
|
||||
fn rdlen(&self, _compress: bool) -> Option<u16> {
|
||||
Some(
|
||||
u16::try_from(self.data.as_ref().len()).expect("long NULL rdata"),
|
||||
@@ -1115,16 +1160,6 @@ impl<Octs: AsRef<[u8]>> ComposeRecordData for Null<Octs> {
|
||||
}
|
||||
}
|
||||
|
||||
//--- Deref
|
||||
|
||||
impl<Octs> ops::Deref for Null<Octs> {
|
||||
type Target = Octs;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
//--- AsRef
|
||||
|
||||
impl<Octs: AsRef<Other>, Other> AsRef<Other> for Null<Octs> {
|
||||
@@ -1570,7 +1605,7 @@ impl<Octs: FromBuilder> Txt<Octs> {
|
||||
|
||||
impl<Octs> Txt<Octs> {
|
||||
/// Creates new TXT record data from its encoded content.
|
||||
pub fn from_octets(octets: Octs) -> Result<Self, CharStrError>
|
||||
pub fn from_octets(octets: Octs) -> Result<Self, TxtError>
|
||||
where
|
||||
Octs: AsRef<[u8]>
|
||||
{
|
||||
@@ -1587,7 +1622,40 @@ impl<Octs> Txt<Octs> {
|
||||
unsafe fn from_octets_unchecked(octets: Octs) -> Self {
|
||||
Txt(octets)
|
||||
}
|
||||
}
|
||||
|
||||
impl Txt<[u8]> {
|
||||
/// Creates new TXT record data on an octets slice.
|
||||
pub fn from_slice(slice: &[u8]) -> Result<&Self, TxtError> {
|
||||
Txt::check_slice(slice)?;
|
||||
Ok(unsafe { Txt::from_slice_unchecked(slice) })
|
||||
}
|
||||
|
||||
/// Creates new TXT record data on an octets slice without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The passed octets must contain correctly encoded TXT record data,
|
||||
/// that is a sequence of encoded character strings.
|
||||
unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
|
||||
unsafe { &*(slice as *const [u8] as *const Self) }
|
||||
}
|
||||
|
||||
/// Checks that a slice contains correctly encoded TXT data.
|
||||
fn check_slice(mut slice: &[u8]) -> Result<(), TxtError> {
|
||||
LongRecordData::check_len(slice.len())?;
|
||||
while let Some(&len) = slice.first() {
|
||||
let len = usize::from(len);
|
||||
if slice.len() <= len {
|
||||
return Err(TxtError(TxtErrorInner::ShortInput));
|
||||
}
|
||||
slice = &slice[len + 1..];
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs> Txt<Octs> {
|
||||
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
|
||||
parser: &mut Parser<'a, Src>,
|
||||
) -> Result<Self, ParseError>
|
||||
@@ -1608,36 +1676,6 @@ impl<Octs> Txt<Octs> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Txt<[u8]> {
|
||||
/// Creates new TXT record data on an octets slice.
|
||||
pub fn from_slice(slice: &[u8]) -> Result<&Self, CharStrError> {
|
||||
Txt::check_slice(slice)?;
|
||||
Ok(unsafe { Txt::from_slice_unchecked(slice) })
|
||||
}
|
||||
|
||||
/// Creates new TXT record data on an octets slice without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The passed octets must contain correctly encoded TXT record data,
|
||||
/// that is a sequence of encoded character strings.
|
||||
unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
|
||||
unsafe { &*(slice as *const [u8] as *const Self) }
|
||||
}
|
||||
|
||||
/// Checks that a slice contains correctly encoded TXT data.
|
||||
fn check_slice(mut slice: &[u8]) -> Result<(), CharStrError> {
|
||||
while let Some(&len) = slice.first() {
|
||||
let len = usize::from(len);
|
||||
if slice.len() <= len {
|
||||
return Err(CharStrError);
|
||||
}
|
||||
slice = &slice[len + 1..];
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Octs: AsRef<[u8]> + ?Sized> Txt<Octs> {
|
||||
/// Returns an iterator over the text items.
|
||||
///
|
||||
@@ -2176,7 +2214,48 @@ impl<Builder: OctetsBuilder + EmptyBuilder> Default for TxtBuilder<Builder> {
|
||||
}
|
||||
}
|
||||
|
||||
//============ Testing ======================================================
|
||||
//============ Error Types ===================================================
|
||||
|
||||
//------------ TxtError ------------------------------------------------------
|
||||
|
||||
/// An octets sequence does not form valid TXT record data.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TxtError(TxtErrorInner);
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum TxtErrorInner {
|
||||
Long(LongRecordData),
|
||||
ShortInput,
|
||||
}
|
||||
|
||||
impl TxtError {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self.0 {
|
||||
TxtErrorInner::Long(err) => err.as_str(),
|
||||
TxtErrorInner::ShortInput => "short input",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LongRecordData> for TxtError {
|
||||
fn from(err: LongRecordData) -> TxtError {
|
||||
TxtError(TxtErrorInner::Long(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TxtError> for FormError {
|
||||
fn from(err: TxtError) -> FormError {
|
||||
FormError::new(err.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TxtError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
//============ Testing =======================================================
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(feature = "std", feature = "bytes"))]
|
||||
@@ -2276,7 +2355,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn null_compose_parse_scan() {
|
||||
let rdata = Null::new("foo");
|
||||
let rdata = Null::from_octets("foo").unwrap();
|
||||
test_rdlen(&rdata);
|
||||
test_compose_parse(&rdata, |parser| Null::parse(parser));
|
||||
}
|
||||
|
||||
+82
-45
@@ -7,7 +7,9 @@
|
||||
use crate::base::cmp::CanonicalOrd;
|
||||
use crate::base::iana::{Rtype, TsigRcode};
|
||||
use crate::base::name::{Dname, ParsedDname, PushError, ToDname};
|
||||
use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData};
|
||||
use crate::base::rdata::{
|
||||
ComposeRecordData, LongRecordData, ParseRecordData, RecordData
|
||||
};
|
||||
use crate::base::wire::{Compose, Composer, Parse, ParseError};
|
||||
use crate::utils::base64;
|
||||
use core::cmp::Ordering;
|
||||
@@ -77,14 +79,11 @@ pub struct Tsig<Octs, Name> {
|
||||
}
|
||||
|
||||
impl<O, N> Tsig<O, N> {
|
||||
/// Creates a new TSIG record from its components.
|
||||
/// Creates new TSIG record data from its components.
|
||||
///
|
||||
/// See the access methods for an explanation of these components.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Since `time_signed` is actually a 48 bit integer, the function will
|
||||
/// panic of the upper 16 bits are not all 0.
|
||||
/// See the access methods for an explanation of these components. The
|
||||
/// function will return an error if the wire format length of the record
|
||||
/// would exceed 65,535 octets.
|
||||
pub fn new(
|
||||
algorithm: N,
|
||||
time_signed: Time48,
|
||||
@@ -93,6 +92,42 @@ impl<O, N> Tsig<O, N> {
|
||||
original_id: u16,
|
||||
error: TsigRcode,
|
||||
other: O,
|
||||
) -> Result<Self, LongRecordData>
|
||||
where O: AsRef<[u8]>, N: ToDname {
|
||||
LongRecordData::check_len(
|
||||
6 // time_signed
|
||||
+ 2 // fudge
|
||||
+ 2 // MAC length
|
||||
+ 2 // original ID
|
||||
+ 2 // error
|
||||
+ 2 // other length
|
||||
+ usize::from(algorithm.compose_len()).checked_add(
|
||||
mac.as_ref().len()
|
||||
).expect("long MAC").checked_add(
|
||||
other.as_ref().len()
|
||||
).expect("long TSIG")
|
||||
)?;
|
||||
Ok(unsafe {
|
||||
Tsig::new_unchecked(
|
||||
algorithm, time_signed, fudge, mac, original_id, error, other,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates new TSIG record data without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller needs to ensure that the wire format length of the
|
||||
/// created record will not exceed 65,535 octets.
|
||||
pub unsafe fn new_unchecked(
|
||||
algorithm: N,
|
||||
time_signed: Time48,
|
||||
fudge: u16,
|
||||
mac: O,
|
||||
original_id: u16,
|
||||
error: TsigRcode,
|
||||
other: O,
|
||||
) -> Self {
|
||||
Tsig {
|
||||
algorithm,
|
||||
@@ -214,15 +249,17 @@ impl<O, N> Tsig<O, N> {
|
||||
TOcts: OctetsFrom<O>,
|
||||
TName: OctetsFrom<N, Error = TOcts::Error>,
|
||||
{
|
||||
Ok(Tsig::new(
|
||||
self.algorithm.try_octets_into()?,
|
||||
self.time_signed,
|
||||
self.fudge,
|
||||
self.mac.try_octets_into()?,
|
||||
self.original_id,
|
||||
self.error,
|
||||
self.other.try_octets_into()?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Tsig::new_unchecked(
|
||||
self.algorithm.try_octets_into()?,
|
||||
self.time_signed,
|
||||
self.fudge,
|
||||
self.mac.try_octets_into()?,
|
||||
self.original_id,
|
||||
self.error,
|
||||
self.other.try_octets_into()?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,15 +283,17 @@ impl<Octs, NOcts> Tsig<Octs, ParsedDname<NOcts>> {
|
||||
other,
|
||||
} = self;
|
||||
|
||||
Ok(Tsig::new(
|
||||
algorithm.flatten_into()?,
|
||||
time_signed,
|
||||
fudge,
|
||||
mac.try_octets_into().map_err(Into::into)?,
|
||||
original_id,
|
||||
error,
|
||||
other.try_octets_into().map_err(Into::into)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Tsig::new_unchecked(
|
||||
algorithm.flatten_into()?,
|
||||
time_signed,
|
||||
fudge,
|
||||
mac.try_octets_into().map_err(Into::into)?,
|
||||
original_id,
|
||||
error,
|
||||
other.try_octets_into().map_err(Into::into)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,14 +310,10 @@ impl<Octs> Tsig<Octs, ParsedDname<Octs>> {
|
||||
let error = TsigRcode::parse(parser)?;
|
||||
let other_len = u16::parse(parser)?;
|
||||
let other = parser.parse_octets(other_len as usize)?;
|
||||
Ok(Tsig {
|
||||
algorithm,
|
||||
time_signed,
|
||||
fudge,
|
||||
mac,
|
||||
original_id,
|
||||
error,
|
||||
other,
|
||||
Ok(unsafe {
|
||||
Tsig::new_unchecked(
|
||||
algorithm, time_signed, fudge, mac, original_id, error, other,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -297,15 +332,17 @@ where
|
||||
fn try_octets_from(
|
||||
source: Tsig<SrcOctets, SrcName>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Tsig::new(
|
||||
Name::try_octets_from(source.algorithm)?,
|
||||
source.time_signed,
|
||||
source.fudge,
|
||||
Octs::try_octets_from(source.mac)?,
|
||||
source.original_id,
|
||||
source.error,
|
||||
Octs::try_octets_from(source.other)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Tsig::new_unchecked(
|
||||
Name::try_octets_from(source.algorithm)?,
|
||||
source.time_signed,
|
||||
source.fudge,
|
||||
Octs::try_octets_from(source.mac)?,
|
||||
source.original_id,
|
||||
source.error,
|
||||
Octs::try_octets_from(source.other)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,7 +703,7 @@ impl fmt::Display for Time48 {
|
||||
}
|
||||
}
|
||||
|
||||
//============ Testing ======================================================
|
||||
//============ Testing =======================================================
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(feature = "std", feature = "bytes"))]
|
||||
@@ -687,7 +724,7 @@ mod test {
|
||||
13,
|
||||
TsigRcode::BadCookie,
|
||||
"",
|
||||
);
|
||||
).unwrap();
|
||||
test_rdlen(&rdata);
|
||||
test_compose_parse(&rdata, |parser| Tsig::parse(parser));
|
||||
}
|
||||
|
||||
+252
-111
@@ -7,7 +7,9 @@
|
||||
use crate::base::cmp::CanonicalOrd;
|
||||
use crate::base::iana::{DigestAlg, Rtype, SecAlg};
|
||||
use crate::base::name::{Dname, ParsedDname, PushError, ToDname};
|
||||
use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData};
|
||||
use crate::base::rdata::{
|
||||
ComposeRecordData, LongRecordData, ParseRecordData, RecordData
|
||||
};
|
||||
use crate::base::scan::{Scan, Scanner, ScannerError};
|
||||
use crate::base::serial::Serial;
|
||||
use crate::base::wire::{Compose, Composer, FormError, Parse, ParseError};
|
||||
@@ -59,6 +61,36 @@ impl<Octs> Dnskey<Octs> {
|
||||
protocol: u8,
|
||||
algorithm: SecAlg,
|
||||
public_key: Octs,
|
||||
) -> Result<Self, LongRecordData>
|
||||
where
|
||||
Octs: AsRef<[u8]>,
|
||||
{
|
||||
LongRecordData::check_len(
|
||||
usize::from(
|
||||
u16::COMPOSE_LEN + u8::COMPOSE_LEN + SecAlg::COMPOSE_LEN
|
||||
).checked_add(
|
||||
public_key.as_ref().len()
|
||||
).expect("long key")
|
||||
)?;
|
||||
Ok(Dnskey {
|
||||
flags,
|
||||
protocol,
|
||||
algorithm,
|
||||
public_key,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates new DNSKEY record data without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller needs to ensure that wire format representation of the
|
||||
/// record data is at most 65,535 octets long.
|
||||
pub unsafe fn new_unchecked(
|
||||
flags: u16,
|
||||
protocol: u8,
|
||||
algorithm: SecAlg,
|
||||
public_key: Octs,
|
||||
) -> Self {
|
||||
Dnskey {
|
||||
flags,
|
||||
@@ -176,12 +208,14 @@ impl<Octs> Dnskey<Octs> {
|
||||
pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
|
||||
self,
|
||||
) -> Result<Dnskey<Target>, Target::Error> {
|
||||
Ok(Dnskey::new(
|
||||
self.flags,
|
||||
self.protocol,
|
||||
self.algorithm,
|
||||
self.public_key.try_octets_into()?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Dnskey::new_unchecked(
|
||||
self.flags,
|
||||
self.protocol,
|
||||
self.algorithm,
|
||||
self.public_key.try_octets_into()?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
|
||||
@@ -191,23 +225,26 @@ impl<Octs> Dnskey<Octs> {
|
||||
Some(len) => len,
|
||||
None => return Err(ParseError::ShortInput),
|
||||
};
|
||||
Ok(Self::new(
|
||||
u16::parse(parser)?,
|
||||
u8::parse(parser)?,
|
||||
SecAlg::parse(parser)?,
|
||||
parser.parse_octets(len)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Self::new_unchecked(
|
||||
u16::parse(parser)?,
|
||||
u8::parse(parser)?,
|
||||
SecAlg::parse(parser)?,
|
||||
parser.parse_octets(len)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn scan<S: Scanner<Octets = Octs>>(
|
||||
scanner: &mut S,
|
||||
) -> Result<Self, S::Error> {
|
||||
Ok(Self::new(
|
||||
) -> Result<Self, S::Error>
|
||||
where Octs: AsRef<[u8]> {
|
||||
Self::new(
|
||||
u16::scan(scanner)?,
|
||||
u8::scan(scanner)?,
|
||||
SecAlg::scan(scanner)?,
|
||||
scanner.convert_entry(base64::SymbolConverter::new())?,
|
||||
))
|
||||
).map_err(|err| S::Error::custom(err.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,12 +260,14 @@ impl<SrcOcts> Dnskey<SrcOcts> {
|
||||
public_key,
|
||||
} = self;
|
||||
|
||||
Ok(Dnskey::new(
|
||||
flags,
|
||||
protocol,
|
||||
algorithm,
|
||||
public_key.try_octets_into().map_err(Into::into)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Dnskey::new_unchecked(
|
||||
flags,
|
||||
protocol,
|
||||
algorithm,
|
||||
public_key.try_octets_into().map_err(Into::into)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,12 +280,14 @@ where
|
||||
type Error = Octs::Error;
|
||||
|
||||
fn try_octets_from(source: Dnskey<SrcOcts>) -> Result<Self, Self::Error> {
|
||||
Ok(Dnskey::new(
|
||||
source.flags,
|
||||
source.protocol,
|
||||
source.algorithm,
|
||||
Octs::try_octets_from(source.public_key)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Dnskey::new_unchecked(
|
||||
source.flags,
|
||||
source.protocol,
|
||||
source.algorithm,
|
||||
Octs::try_octets_from(source.public_key)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +474,10 @@ impl<Name> ProtoRrsig<Name> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_rrsig<Octs>(self, signature: Octs) -> Rrsig<Octs, Name> {
|
||||
pub fn into_rrsig<Octs: AsRef<[u8]>>(
|
||||
self, signature: Octs
|
||||
) -> Result<Rrsig<Octs, Name>, LongRecordData>
|
||||
where Name: ToDname {
|
||||
Rrsig::new(
|
||||
self.type_covered,
|
||||
self.algorithm,
|
||||
@@ -595,6 +639,55 @@ impl<Octs, Name> Rrsig<Octs, Name> {
|
||||
key_tag: u16,
|
||||
signer_name: Name,
|
||||
signature: Octs,
|
||||
) -> Result<Self, LongRecordData>
|
||||
where Octs: AsRef<[u8]>, Name: ToDname {
|
||||
LongRecordData::check_len(
|
||||
usize::from(
|
||||
Rtype::COMPOSE_LEN
|
||||
+ SecAlg::COMPOSE_LEN
|
||||
+ u8::COMPOSE_LEN
|
||||
+ u32::COMPOSE_LEN
|
||||
+ Serial::COMPOSE_LEN
|
||||
+ Serial::COMPOSE_LEN
|
||||
+ u16::COMPOSE_LEN
|
||||
+ signer_name.compose_len()
|
||||
).checked_add(
|
||||
signature.as_ref().len()
|
||||
)
|
||||
.expect("long signature")
|
||||
)?;
|
||||
Ok(unsafe {
|
||||
Rrsig::new_unchecked(
|
||||
type_covered,
|
||||
algorithm,
|
||||
labels,
|
||||
original_ttl,
|
||||
expiration,
|
||||
inception,
|
||||
key_tag,
|
||||
signer_name,
|
||||
signature,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates new RRSIG record data without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller needs to ensure that wire format representation of the
|
||||
/// record data is at most 65,535 octets long.
|
||||
#[allow(clippy::too_many_arguments)] // XXX Consider changing.
|
||||
pub unsafe fn new_unchecked(
|
||||
type_covered: Rtype,
|
||||
algorithm: SecAlg,
|
||||
labels: u8,
|
||||
original_ttl: u32,
|
||||
expiration: Serial,
|
||||
inception: Serial,
|
||||
key_tag: u16,
|
||||
signer_name: Name,
|
||||
signature: Octs,
|
||||
) -> Self {
|
||||
Rrsig {
|
||||
type_covered,
|
||||
@@ -656,23 +749,29 @@ impl<Octs, Name> Rrsig<Octs, Name> {
|
||||
TOcts: OctetsFrom<Octs>,
|
||||
TName: OctetsFrom<Name, Error = TOcts::Error>,
|
||||
{
|
||||
Ok(Rrsig::new(
|
||||
self.type_covered,
|
||||
self.algorithm,
|
||||
self.labels,
|
||||
self.original_ttl,
|
||||
self.expiration,
|
||||
self.inception,
|
||||
self.key_tag,
|
||||
TName::try_octets_from(self.signer_name)?,
|
||||
TOcts::try_octets_from(self.signature)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Rrsig::new_unchecked(
|
||||
self.type_covered,
|
||||
self.algorithm,
|
||||
self.labels,
|
||||
self.original_ttl,
|
||||
self.expiration,
|
||||
self.inception,
|
||||
self.key_tag,
|
||||
TName::try_octets_from(self.signer_name)?,
|
||||
TOcts::try_octets_from(self.signature)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn scan<S: Scanner<Octets = Octs, Dname = Name>>(
|
||||
scanner: &mut S,
|
||||
) -> Result<Self, S::Error> {
|
||||
Ok(Self::new(
|
||||
) -> Result<Self, S::Error>
|
||||
where
|
||||
Octs: AsRef<[u8]>,
|
||||
Name: ToDname,
|
||||
{
|
||||
Self::new(
|
||||
Rtype::scan(scanner)?,
|
||||
SecAlg::scan(scanner)?,
|
||||
u8::scan(scanner)?,
|
||||
@@ -682,7 +781,7 @@ impl<Octs, Name> Rrsig<Octs, Name> {
|
||||
u16::scan(scanner)?,
|
||||
scanner.scan_dname()?,
|
||||
scanner.convert_entry(base64::SymbolConverter::new())?,
|
||||
))
|
||||
).map_err(|err| S::Error::custom(err.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,17 +808,19 @@ impl<Octs, NOcts> Rrsig<Octs, ParsedDname<NOcts>> {
|
||||
signature,
|
||||
} = self;
|
||||
|
||||
Ok(Rrsig::new(
|
||||
type_covered,
|
||||
algorithm,
|
||||
labels,
|
||||
original_ttl,
|
||||
expiration,
|
||||
inception,
|
||||
key_tag,
|
||||
signer_name.flatten_into()?,
|
||||
Target::try_octets_from(signature).map_err(Into::into)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Rrsig::new_unchecked(
|
||||
type_covered,
|
||||
algorithm,
|
||||
labels,
|
||||
original_ttl,
|
||||
expiration,
|
||||
inception,
|
||||
key_tag,
|
||||
signer_name.flatten_into()?,
|
||||
Target::try_octets_from(signature).map_err(Into::into)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,17 +838,19 @@ impl<Octs> Rrsig<Octs, ParsedDname<Octs>> {
|
||||
let signer_name = ParsedDname::parse(parser)?;
|
||||
let len = parser.remaining();
|
||||
let signature = parser.parse_octets(len)?;
|
||||
Ok(Self::new(
|
||||
type_covered,
|
||||
algorithm,
|
||||
labels,
|
||||
original_ttl,
|
||||
expiration,
|
||||
inception,
|
||||
key_tag,
|
||||
signer_name,
|
||||
signature,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Self::new_unchecked(
|
||||
type_covered,
|
||||
algorithm,
|
||||
labels,
|
||||
original_ttl,
|
||||
expiration,
|
||||
inception,
|
||||
key_tag,
|
||||
signer_name,
|
||||
signature,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,17 +868,19 @@ where
|
||||
fn try_octets_from(
|
||||
source: Rrsig<SrcOcts, SrcName>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Rrsig::new(
|
||||
source.type_covered,
|
||||
source.algorithm,
|
||||
source.labels,
|
||||
source.original_ttl,
|
||||
source.expiration,
|
||||
source.inception,
|
||||
source.key_tag,
|
||||
Name::try_octets_from(source.signer_name)?,
|
||||
Octs::try_octets_from(source.signature)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Rrsig::new_unchecked(
|
||||
source.type_covered,
|
||||
source.algorithm,
|
||||
source.labels,
|
||||
source.original_ttl,
|
||||
source.expiration,
|
||||
source.inception,
|
||||
source.key_tag,
|
||||
Name::try_octets_from(source.signer_name)?,
|
||||
Octs::try_octets_from(source.signature)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1353,6 +1458,30 @@ impl<Octs> Ds<Octs> {
|
||||
algorithm: SecAlg,
|
||||
digest_type: DigestAlg,
|
||||
digest: Octs,
|
||||
) -> Result<Self, LongRecordData>
|
||||
where Octs: AsRef<[u8]> {
|
||||
LongRecordData::check_len(
|
||||
usize::from(
|
||||
u16::COMPOSE_LEN + SecAlg::COMPOSE_LEN + DigestAlg::COMPOSE_LEN
|
||||
).checked_add(digest.as_ref().len()).expect("long digest")
|
||||
)?;
|
||||
Ok(unsafe {
|
||||
Ds::new_unchecked(key_tag, algorithm, digest_type, digest)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/// Creates new DS record data without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller needs to ensure that wire format representation of the
|
||||
/// record data is at most 65,535 octets long.
|
||||
pub unsafe fn new_unchecked(
|
||||
key_tag: u16,
|
||||
algorithm: SecAlg,
|
||||
digest_type: DigestAlg,
|
||||
digest: Octs,
|
||||
) -> Self {
|
||||
Ds {
|
||||
key_tag,
|
||||
@@ -1385,12 +1514,14 @@ impl<Octs> Ds<Octs> {
|
||||
pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
|
||||
self,
|
||||
) -> Result<Ds<Target>, Target::Error> {
|
||||
Ok(Ds::new(
|
||||
self.key_tag,
|
||||
self.algorithm,
|
||||
self.digest_type,
|
||||
self.digest.try_octets_into()?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Ds::new_unchecked(
|
||||
self.key_tag,
|
||||
self.algorithm,
|
||||
self.digest_type,
|
||||
self.digest.try_octets_into()?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
|
||||
@@ -1400,23 +1531,26 @@ impl<Octs> Ds<Octs> {
|
||||
Some(len) => len,
|
||||
None => return Err(ParseError::ShortInput),
|
||||
};
|
||||
Ok(Self::new(
|
||||
u16::parse(parser)?,
|
||||
SecAlg::parse(parser)?,
|
||||
DigestAlg::parse(parser)?,
|
||||
parser.parse_octets(len)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Self::new_unchecked(
|
||||
u16::parse(parser)?,
|
||||
SecAlg::parse(parser)?,
|
||||
DigestAlg::parse(parser)?,
|
||||
parser.parse_octets(len)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn scan<S: Scanner<Octets = Octs>>(
|
||||
scanner: &mut S,
|
||||
) -> Result<Self, S::Error> {
|
||||
Ok(Self::new(
|
||||
) -> Result<Self, S::Error>
|
||||
where Octs: AsRef<[u8]> {
|
||||
Self::new(
|
||||
u16::scan(scanner)?,
|
||||
SecAlg::scan(scanner)?,
|
||||
DigestAlg::scan(scanner)?,
|
||||
scanner.convert_entry(base16::SymbolConverter::new())?,
|
||||
))
|
||||
).map_err(|err| S::Error::custom(err.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1431,12 +1565,14 @@ impl<SrcOcts> Ds<SrcOcts> {
|
||||
digest_type,
|
||||
digest,
|
||||
} = self;
|
||||
Ok(Ds::new(
|
||||
key_tag,
|
||||
algorithm,
|
||||
digest_type,
|
||||
digest.try_octets_into().map_err(Into::into)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Ds::new_unchecked(
|
||||
key_tag,
|
||||
algorithm,
|
||||
digest_type,
|
||||
digest.try_octets_into().map_err(Into::into)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1449,12 +1585,14 @@ where
|
||||
type Error = Octs::Error;
|
||||
|
||||
fn try_octets_from(source: Ds<SrcOcts>) -> Result<Self, Self::Error> {
|
||||
Ok(Ds::new(
|
||||
source.key_tag,
|
||||
source.algorithm,
|
||||
source.digest_type,
|
||||
Octs::try_octets_from(source.digest)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Ds::new_unchecked(
|
||||
source.key_tag,
|
||||
source.algorithm,
|
||||
source.digest_type,
|
||||
Octs::try_octets_from(source.digest)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2281,7 +2419,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn dnskey_compose_parse_scan() {
|
||||
let rdata = Dnskey::new(10, 11, SecAlg::RsaSha1, b"key");
|
||||
let rdata = Dnskey::new(10, 11, SecAlg::RsaSha1, b"key").unwrap();
|
||||
test_rdlen(&rdata);
|
||||
test_compose_parse(&rdata, |parser| Dnskey::parse(parser));
|
||||
test_scan(&["10", "11", "RSASHA1", "a2V5"], Dnskey::scan, &rdata);
|
||||
@@ -2301,7 +2439,7 @@ mod test {
|
||||
15,
|
||||
Dname::<Vec<u8>>::from_str("example.com.").unwrap(),
|
||||
b"key",
|
||||
);
|
||||
).unwrap();
|
||||
test_rdlen(&rdata);
|
||||
test_compose_parse(&rdata, |parser| Rrsig::parse(parser));
|
||||
test_scan(
|
||||
@@ -2341,7 +2479,9 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn ds_compose_parse_scan() {
|
||||
let rdata = Ds::new(10, SecAlg::RsaSha1, DigestAlg::Sha256, b"key");
|
||||
let rdata = Ds::new(
|
||||
10, SecAlg::RsaSha1, DigestAlg::Sha256, b"key"
|
||||
).unwrap();
|
||||
test_rdlen(&rdata);
|
||||
test_compose_parse(&rdata, |parser| Ds::parse(parser));
|
||||
test_scan(&["10", "RSASHA1", "2", "6b6579"], Ds::scan, &rdata);
|
||||
@@ -2440,7 +2580,7 @@ mod test {
|
||||
KLZ02cRWXqM="
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
).unwrap()
|
||||
.key_tag(),
|
||||
59944
|
||||
);
|
||||
@@ -2460,7 +2600,7 @@ mod test {
|
||||
9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU="
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
).unwrap()
|
||||
.key_tag(),
|
||||
20326
|
||||
);
|
||||
@@ -2476,7 +2616,7 @@ mod test {
|
||||
C+7Eoi12SqybMTicD3Ezwa9XbG1iPjmjhbMrLh7MSQpX"
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
).unwrap()
|
||||
.key_tag(),
|
||||
18698
|
||||
);
|
||||
@@ -2484,8 +2624,9 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn dnskey_flags() {
|
||||
let dnskey =
|
||||
Dnskey::new(257, 3, SecAlg::RsaSha256, bytes::Bytes::new());
|
||||
let dnskey = Dnskey::new(
|
||||
257, 3, SecAlg::RsaSha256, bytes::Bytes::new()
|
||||
).unwrap();
|
||||
assert!(dnskey.is_zsk());
|
||||
assert!(dnskey.is_secure_entry_point());
|
||||
assert!(!dnskey.is_revoked());
|
||||
|
||||
+126
-64
@@ -4,8 +4,10 @@
|
||||
use crate::base::cmp::CanonicalOrd;
|
||||
use crate::base::iana::{DigestAlg, Rtype, SecAlg};
|
||||
use crate::base::name::PushError;
|
||||
use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData};
|
||||
use crate::base::scan::{Scan, Scanner};
|
||||
use crate::base::rdata::{
|
||||
ComposeRecordData, LongRecordData, ParseRecordData, RecordData
|
||||
};
|
||||
use crate::base::scan::{Scan, Scanner, ScannerError};
|
||||
use crate::base::wire::{Compose, Composer, Parse, ParseError};
|
||||
use crate::utils::{base16, base64};
|
||||
use core::cmp::Ordering;
|
||||
@@ -50,6 +52,29 @@ impl<Octs> Cdnskey<Octs> {
|
||||
protocol: u8,
|
||||
algorithm: SecAlg,
|
||||
public_key: Octs,
|
||||
) -> Result<Self, LongRecordData>
|
||||
where Octs: AsRef<[u8]> {
|
||||
LongRecordData::check_len(
|
||||
usize::from(
|
||||
u16::COMPOSE_LEN + u8::COMPOSE_LEN + SecAlg::COMPOSE_LEN
|
||||
).checked_add(public_key.as_ref().len()).expect("long key")
|
||||
)?;
|
||||
Ok(unsafe {
|
||||
Cdnskey::new_unchecked(flags, protocol, algorithm, public_key)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates new CDNSKEY record data without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller needs to ensure that wire format representation of the
|
||||
/// record data is at most 65,535 octets long.
|
||||
pub unsafe fn new_unchecked(
|
||||
flags: u16,
|
||||
protocol: u8,
|
||||
algorithm: SecAlg,
|
||||
public_key: Octs,
|
||||
) -> Self {
|
||||
Cdnskey {
|
||||
flags,
|
||||
@@ -78,12 +103,14 @@ impl<Octs> Cdnskey<Octs> {
|
||||
pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
|
||||
self,
|
||||
) -> Result<Cdnskey<Target>, Target::Error> {
|
||||
Ok(Cdnskey::new(
|
||||
self.flags,
|
||||
self.protocol,
|
||||
self.algorithm,
|
||||
self.public_key.try_octets_into()?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Cdnskey::new_unchecked(
|
||||
self.flags,
|
||||
self.protocol,
|
||||
self.algorithm,
|
||||
self.public_key.try_octets_into()?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
|
||||
@@ -93,23 +120,26 @@ impl<Octs> Cdnskey<Octs> {
|
||||
Some(len) => len,
|
||||
None => return Err(ParseError::ShortInput),
|
||||
};
|
||||
Ok(Self::new(
|
||||
u16::parse(parser)?,
|
||||
u8::parse(parser)?,
|
||||
SecAlg::parse(parser)?,
|
||||
parser.parse_octets(len)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Self::new_unchecked(
|
||||
u16::parse(parser)?,
|
||||
u8::parse(parser)?,
|
||||
SecAlg::parse(parser)?,
|
||||
parser.parse_octets(len)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn scan<S: Scanner<Octets = Octs>>(
|
||||
scanner: &mut S,
|
||||
) -> Result<Self, S::Error> {
|
||||
Ok(Self::new(
|
||||
) -> Result<Self, S::Error>
|
||||
where Octs: AsRef<[u8]> {
|
||||
Self::new(
|
||||
u16::scan(scanner)?,
|
||||
u8::scan(scanner)?,
|
||||
SecAlg::scan(scanner)?,
|
||||
scanner.convert_entry(base64::SymbolConverter::new())?,
|
||||
))
|
||||
).map_err(|err| S::Error::custom(err.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,12 +154,14 @@ impl<SrcOcts> Cdnskey<SrcOcts> {
|
||||
algorithm,
|
||||
public_key,
|
||||
} = self;
|
||||
Ok(Cdnskey::new(
|
||||
flags,
|
||||
protocol,
|
||||
algorithm,
|
||||
public_key.try_octets_into().map_err(Into::into)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Cdnskey::new_unchecked(
|
||||
flags,
|
||||
protocol,
|
||||
algorithm,
|
||||
public_key.try_octets_into().map_err(Into::into)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,12 +176,14 @@ where
|
||||
fn try_octets_from(
|
||||
source: Cdnskey<SrcOcts>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
Ok(Cdnskey::new(
|
||||
source.flags,
|
||||
source.protocol,
|
||||
source.algorithm,
|
||||
Octs::try_octets_from(source.public_key)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Cdnskey::new_unchecked(
|
||||
source.flags,
|
||||
source.protocol,
|
||||
source.algorithm,
|
||||
Octs::try_octets_from(source.public_key)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,6 +368,29 @@ impl<Octs> Cds<Octs> {
|
||||
algorithm: SecAlg,
|
||||
digest_type: DigestAlg,
|
||||
digest: Octs,
|
||||
) -> Result<Self, LongRecordData>
|
||||
where Octs: AsRef<[u8]> {
|
||||
LongRecordData::check_len(
|
||||
usize::from(
|
||||
u16::COMPOSE_LEN + SecAlg::COMPOSE_LEN + DigestAlg::COMPOSE_LEN
|
||||
).checked_add(digest.as_ref().len()).expect("long digest")
|
||||
)?;
|
||||
Ok(unsafe {
|
||||
Cds::new_unchecked(key_tag, algorithm, digest_type, digest)
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates new CDS record data without checking.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller needs to ensure that wire format representation of the
|
||||
/// record data is at most 65,535 octets long.
|
||||
pub unsafe fn new_unchecked(
|
||||
key_tag: u16,
|
||||
algorithm: SecAlg,
|
||||
digest_type: DigestAlg,
|
||||
digest: Octs,
|
||||
) -> Self {
|
||||
Cds {
|
||||
key_tag,
|
||||
@@ -366,12 +423,14 @@ impl<Octs> Cds<Octs> {
|
||||
pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
|
||||
self,
|
||||
) -> Result<Cds<Target>, Target::Error> {
|
||||
Ok(Cds::new(
|
||||
self.key_tag,
|
||||
self.algorithm,
|
||||
self.digest_type,
|
||||
self.digest.try_octets_into()?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Cds::new_unchecked(
|
||||
self.key_tag,
|
||||
self.algorithm,
|
||||
self.digest_type,
|
||||
self.digest.try_octets_into()?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
|
||||
@@ -381,23 +440,26 @@ impl<Octs> Cds<Octs> {
|
||||
Some(len) => len,
|
||||
None => return Err(ParseError::ShortInput),
|
||||
};
|
||||
Ok(Self::new(
|
||||
u16::parse(parser)?,
|
||||
SecAlg::parse(parser)?,
|
||||
DigestAlg::parse(parser)?,
|
||||
parser.parse_octets(len)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Self::new_unchecked(
|
||||
u16::parse(parser)?,
|
||||
SecAlg::parse(parser)?,
|
||||
DigestAlg::parse(parser)?,
|
||||
parser.parse_octets(len)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn scan<S: Scanner<Octets = Octs>>(
|
||||
scanner: &mut S,
|
||||
) -> Result<Self, S::Error> {
|
||||
Ok(Self::new(
|
||||
) -> Result<Self, S::Error>
|
||||
where Octs: AsRef<[u8]> {
|
||||
Self::new(
|
||||
u16::scan(scanner)?,
|
||||
SecAlg::scan(scanner)?,
|
||||
DigestAlg::scan(scanner)?,
|
||||
scanner.convert_entry(base16::SymbolConverter::new())?,
|
||||
))
|
||||
).map_err(|err| S::Error::custom(err.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,18 +468,14 @@ impl<SrcOcts> Cds<SrcOcts> {
|
||||
where
|
||||
Octs: OctetsFrom<SrcOcts>,
|
||||
{
|
||||
let Self {
|
||||
key_tag,
|
||||
algorithm,
|
||||
digest_type,
|
||||
digest,
|
||||
} = self;
|
||||
Ok(Cds::new(
|
||||
key_tag,
|
||||
algorithm,
|
||||
digest_type,
|
||||
digest.try_octets_into().map_err(Into::into)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Cds::new_unchecked(
|
||||
self.key_tag,
|
||||
self.algorithm,
|
||||
self.digest_type,
|
||||
self.digest.try_octets_into().map_err(Into::into)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,12 +488,14 @@ where
|
||||
type Error = Octs::Error;
|
||||
|
||||
fn try_octets_from(source: Cds<SrcOcts>) -> Result<Self, Self::Error> {
|
||||
Ok(Cds::new(
|
||||
source.key_tag,
|
||||
source.algorithm,
|
||||
source.digest_type,
|
||||
Octs::try_octets_from(source.digest)?,
|
||||
))
|
||||
Ok(unsafe {
|
||||
Cds::new_unchecked(
|
||||
source.key_tag,
|
||||
source.algorithm,
|
||||
source.digest_type,
|
||||
Octs::try_octets_from(source.digest)?,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,7 +683,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn cdnskey_compose_parse_scan() {
|
||||
let rdata = Cdnskey::new(10, 11, SecAlg::RsaSha1, b"key");
|
||||
let rdata = Cdnskey::new(10, 11, SecAlg::RsaSha1, b"key").unwrap();
|
||||
test_rdlen(&rdata);
|
||||
test_compose_parse(&rdata, |parser| Cdnskey::parse(parser));
|
||||
test_scan(&["10", "11", "RSASHA1", "a2V5"], Cdnskey::scan, &rdata);
|
||||
@@ -633,7 +693,9 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn cds_compose_parse_scan() {
|
||||
let rdata = Cds::new(10, SecAlg::RsaSha1, DigestAlg::Sha256, b"key");
|
||||
let rdata = Cds::new(
|
||||
10, SecAlg::RsaSha1, DigestAlg::Sha256, b"key"
|
||||
).unwrap();
|
||||
test_rdlen(&rdata);
|
||||
test_compose_parse(&rdata, |parser| Cds::parse(parser));
|
||||
test_scan(&["10", "RSASHA1", "2", "6b6579"], Cds::scan, &rdata);
|
||||
|
||||
+4
-2
@@ -74,7 +74,7 @@ impl<N, D> SortedRecords<N, D> {
|
||||
N: ToDname + Clone,
|
||||
D: RecordData + ComposeRecordData,
|
||||
Key: SigningKey,
|
||||
Octets: From<Key::Signature>,
|
||||
Octets: From<Key::Signature> + AsRef<[u8]>,
|
||||
ApexName: ToDname + Clone,
|
||||
{
|
||||
let mut res = Vec::new();
|
||||
@@ -154,7 +154,9 @@ impl<N, D> SortedRecords<N, D> {
|
||||
name.owner().clone(),
|
||||
name.class(),
|
||||
rrset.ttl(),
|
||||
rrsig.into_rrsig(key.sign(&buf)?.into()),
|
||||
rrsig
|
||||
.into_rrsig(key.sign(&buf)?.into())
|
||||
.expect("long signature"),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -52,7 +52,8 @@ impl<'a> Key<'a> {
|
||||
3,
|
||||
SecAlg::EcdsaP256Sha256,
|
||||
public_key,
|
||||
),
|
||||
)
|
||||
.expect("long key"),
|
||||
key: RingKey::Ecdsa(keypair),
|
||||
rng,
|
||||
})
|
||||
@@ -82,7 +83,8 @@ impl<'a> SigningKey for Key<'a> {
|
||||
self.dnskey.algorithm(),
|
||||
DigestAlg::Sha256,
|
||||
digest,
|
||||
))
|
||||
)
|
||||
.expect("long digest"))
|
||||
}
|
||||
|
||||
fn sign(&self, msg: &[u8]) -> Result<Self::Signature, Self::Error> {
|
||||
|
||||
+8
-2
@@ -1406,6 +1406,9 @@ impl Variables {
|
||||
key.name.clone(),
|
||||
Class::Any,
|
||||
0,
|
||||
// The only reason creating TSIG record data can fail here is
|
||||
// that the hmac is unreasonable large. Since we control its
|
||||
// creation, panicing in this case is fine.
|
||||
Tsig::new(
|
||||
key.algorithm().to_dname(),
|
||||
self.time_signed,
|
||||
@@ -1414,7 +1417,8 @@ impl Variables {
|
||||
original_id,
|
||||
self.error,
|
||||
other,
|
||||
),
|
||||
)
|
||||
.expect("long MAC"),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -1656,6 +1660,7 @@ impl<K: AsRef<Key>> ServerError<K> {
|
||||
tsig.owner(),
|
||||
tsig.class(),
|
||||
tsig.ttl(),
|
||||
// The TSIG record data can never ever be to long.
|
||||
Tsig::new(
|
||||
tsig.data().algorithm(),
|
||||
tsig.data().time_signed(),
|
||||
@@ -1664,7 +1669,8 @@ impl<K: AsRef<Key>> ServerError<K> {
|
||||
msg.header().id(),
|
||||
error,
|
||||
b"",
|
||||
),
|
||||
)
|
||||
.expect("long record data"),
|
||||
))?;
|
||||
}
|
||||
ServerErrorInner::Signed { context, variables } => {
|
||||
|
||||
+27
-17
@@ -382,8 +382,8 @@ mod test {
|
||||
)
|
||||
.unwrap();
|
||||
(
|
||||
Dnskey::new(257, 3, SecAlg::RsaSha256, ksk),
|
||||
Dnskey::new(256, 3, SecAlg::RsaSha256, zsk),
|
||||
Dnskey::new(257, 3, SecAlg::RsaSha256, ksk).unwrap(),
|
||||
Dnskey::new(256, 3, SecAlg::RsaSha256, zsk).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -398,8 +398,8 @@ mod test {
|
||||
)
|
||||
.unwrap();
|
||||
(
|
||||
Dnskey::new(257, 3, SecAlg::RsaSha256, ksk),
|
||||
Dnskey::new(256, 3, SecAlg::RsaSha256, zsk),
|
||||
Dnskey::new(257, 3, SecAlg::RsaSha256, ksk).unwrap(),
|
||||
Dnskey::new(256, 3, SecAlg::RsaSha256, zsk).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -415,7 +415,8 @@ mod test {
|
||||
"4G1EuAuPHTmpXAsNfGXQhFjogECbvGg0VxBCN8f47I0=",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
dnskey.digest(&owner, DigestAlg::Sha256).unwrap().as_ref(),
|
||||
expected.digest()
|
||||
@@ -474,7 +475,7 @@ mod test {
|
||||
"otBkINZAQu7AvPKjr/xWIEE7+SoZtKgF8bzVynX6bfJMJuPay8jPvNmwXkZOdSoYlvFp0bk9JWJKCh8y5uoNfMFkN6OSrDkr3t0E+c8c0Mnmwkk5CETH3Gqxthi0yyRX5T4VlHU06/Ks4zI+XAgl3FBpOc554ivdzez8YCjAIGx7XgzzooEb7heMSlLc7S7/HNjw51TPRs4RxrAVcezieKCzPPpeWBhjE6R3oiSwrl0SBD4/yplrDlr7UHs/Atcm3MSgemdyr2sOoOUkVQCVpcj3SQQezoD2tCM7861CXEQdg5fjeHDtz285xHt5HJpA5cOcctRo4ihybfow/+V7AQ==",
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
).unwrap();
|
||||
rrsig_verify_dnskey(ksk, zsk, rrsig);
|
||||
|
||||
// Test 1024b long key
|
||||
@@ -492,7 +493,7 @@ mod test {
|
||||
"j1s1IPMoZd0mbmelNVvcbYNe2tFCdLsLpNCnQ8xW6d91ujwPZ2yDlc3lU3hb+Jq3sPoj+5lVgB7fZzXQUQTPFWLF7zvW49da8pWuqzxFtg6EjXRBIWH5rpEhOcr+y3QolJcPOTx+/utCqt2tBKUUy3LfM6WgvopdSGaryWdwFJPW7qKHjyyLYxIGx5AEuLfzsA5XZf8CmpUheSRH99GRZoIB+sQzHuelWGMQ5A42DPvOVZFmTpIwiT2QaIpid4nJ7jNfahfwFrCoS+hvqjK9vktc5/6E/Mt7DwCQDaPt5cqDfYltUitQy+YA5YP5sOhINChYadZe+2N80OA+RKz0mA==",
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
).unwrap();
|
||||
rrsig_verify_dnskey(ksk, zsk, rrsig.clone());
|
||||
|
||||
// Test that 512b short RSA DNSKEY is not supported (too short)
|
||||
@@ -501,7 +502,7 @@ mod test {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let short_key = Dnskey::new(256, 3, SecAlg::RsaSha256, data);
|
||||
let short_key = Dnskey::new(256, 3, SecAlg::RsaSha256, data).unwrap();
|
||||
let err = rrsig
|
||||
.verify_signed_data(&short_key, &vec![0; 100])
|
||||
.unwrap_err();
|
||||
@@ -520,7 +521,8 @@ mod test {
|
||||
F+KkxLbxILfDLUT0rAK9iUzy1L53eKGQ==",
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
Dnskey::new(
|
||||
256,
|
||||
3,
|
||||
@@ -530,7 +532,8 @@ mod test {
|
||||
d8KqXXFJkqmVfRvMGPmM1x8fGAa2XhSA==",
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let owner = Dname::from_str("cloudflare.com.").unwrap();
|
||||
@@ -548,7 +551,8 @@ mod test {
|
||||
zcJBLvRmofYFDAhju21p1uTfLaYHrg==",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
rrsig_verify_dnskey(ksk, zsk, rrsig);
|
||||
}
|
||||
|
||||
@@ -563,7 +567,8 @@ mod test {
|
||||
"m1NELLVVQKl4fHVn/KKdeNO0PrYKGT3IGbYseT8XcKo=",
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
Dnskey::new(
|
||||
256,
|
||||
3,
|
||||
@@ -572,7 +577,8 @@ mod test {
|
||||
"2tstZAjgmlDTePn0NVXrAHBJmg84LoaFVxzLl1anjGI=",
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let owner =
|
||||
@@ -592,7 +598,8 @@ mod test {
|
||||
QdtgPXja7YkTaqzrYUbYk01J8ICsAA==",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
rrsig_verify_dnskey(ksk, zsk, rrsig);
|
||||
}
|
||||
|
||||
@@ -617,7 +624,8 @@ mod test {
|
||||
dg5fjeHDtz285xHt5HJpA5cOcctRo4ihybfow/+V7AQ==",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut records: Vec<Record<Dname, ZoneRecordData<Vec<u8>, Dname>>> =
|
||||
[&ksk, &zsk]
|
||||
@@ -656,7 +664,8 @@ mod test {
|
||||
vh0z2542lzMKR4Dh8uZffQ==",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let rrsig = Rrsig::new(
|
||||
Rtype::Mx,
|
||||
SecAlg::RsaSha1,
|
||||
@@ -673,7 +682,8 @@ mod test {
|
||||
36SR5xBni8vHI=",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
let record = Record::new(
|
||||
Dname::from_str("a.z.w.example.").unwrap(),
|
||||
Class::In,
|
||||
|
||||
Reference in New Issue
Block a user