From d89a2827e2fcbaffc7b7b3ecb17366c6b3819ec8 Mon Sep 17 00:00:00 2001 From: arya dradjica Date: Mon, 29 Jun 2026 15:13:03 +0200 Subject: [PATCH] [new/rdata] Add `Srv` --- src/new/rdata/mod.rs | 13 +++++ src/new/rdata/srv.rs | 112 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/new/rdata/srv.rs diff --git a/src/new/rdata/mod.rs b/src/new/rdata/mod.rs index 7666fd1b..18f22653 100644 --- a/src/new/rdata/mod.rs +++ b/src/new/rdata/mod.rs @@ -43,6 +43,7 @@ //! - [`Mx`] //! - [`Txt`] //! - [`Rp`] +//! - [`Srv`] //! //! Indirection types: //! - [`CName`] @@ -115,6 +116,9 @@ pub use edns::{EdnsOptionsIter, Opt}; mod rp; pub use rp::Rp; +mod srv; +pub use srv::Srv; + mod dnssec; pub use dnssec::{ DNSKey, DNSKeyFlags, DigestType, Ds, NSec, NSec3, NSec3Flags, @@ -280,6 +284,9 @@ define_record_data! { /// Identification of the person/party responsible for this domain. Rp(Rp) = RP, + /// The locations of services associated with this domain. + Srv(&'a Srv) = SRV, + /// The IPv6 address of a host responsible for this domain. Aaaa(Aaaa) = AAAA, @@ -331,6 +338,7 @@ impl<'a, N> RecordData<'a, N> { Self::Txt(r) => RecordData::Txt(r), Self::Rp(r) => RecordData::Rp(r.map_names(f)), Self::Aaaa(r) => RecordData::Aaaa(r), + Self::Srv(r) => RecordData::Srv(r), Self::DName(r) => RecordData::DName(r), Self::Opt(r) => RecordData::Opt(r), Self::Ds(r) => RecordData::Ds(r), @@ -360,6 +368,7 @@ impl<'a, N> RecordData<'a, N> { Self::Txt(r) => RecordData::Txt(r), Self::Rp(r) => RecordData::Rp(r.map_names_by_ref(f)), Self::Aaaa(r) => RecordData::Aaaa(*r), + Self::Srv(r) => RecordData::Srv(r), Self::DName(r) => RecordData::DName(r), Self::Opt(r) => RecordData::Opt(r), Self::Ds(r) => RecordData::Ds(r), @@ -395,6 +404,7 @@ impl<'a, N> RecordData<'a, N> { Self::Txt(r) => RecordData::Txt(copy_to_bump(*r, bump)), Self::Rp(r) => RecordData::Rp(r.clone()), Self::Aaaa(r) => RecordData::Aaaa(*r), + Self::Srv(r) => RecordData::Srv(copy_to_bump(*r, bump)), Self::DName(r) => RecordData::DName(copy_to_bump(*r, bump)), Self::Opt(r) => RecordData::Opt(copy_to_bump(*r, bump)), Self::Ds(r) => RecordData::Ds(copy_to_bump(*r, bump)), @@ -450,6 +460,9 @@ impl<'a, N: SplitMessageBytes<'a>> ParseRecordData<'a> for RecordData<'a, N> { RType::AAAA => { Aaaa::parse_bytes(&contents[start..]).map(Self::Aaaa) } + RType::SRV => { + <&Srv>::parse_bytes(&contents[start..]).map(Self::Srv) + } RType::DNAME => { <&DName>::parse_bytes(&contents[start..]).map(Self::DName) } diff --git a/src/new/rdata/srv.rs b/src/new/rdata/srv.rs new file mode 100644 index 00000000..ee8dc4d4 --- /dev/null +++ b/src/new/rdata/srv.rs @@ -0,0 +1,112 @@ +//! The SRV record data type. +//! +//! See [RFC 2782](https://datatracker.ietf.org/doc/html/rfc2782). + +use core::cmp::Ordering; + +use crate::new::base::build::{ + AsBytes, BuildBytes, BuildInMessage, NameCompressor, +}; +use crate::new::base::name::{CanonicalName, Name}; +use crate::new::base::wire::*; +use crate::new::base::{ + CanonicalRecordData, ParseRecordData, ParseRecordDataBytes, RType, +}; +use crate::utils::dst::UnsizedCopy; + +//----------- Srv ------------------------------------------------------------ + +/// The locations of services associated with this domain. +// +// TODO: Documentation. +#[derive( + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + AsBytes, + BuildBytes, + ParseBytesZC, + SplitBytesZC, + UnsizedCopy, +)] +#[repr(C)] +pub struct Srv { + /// The priority of this host. + pub priority: U16, + + /// The relative weight for selection of this host. + pub weight: U16, + + /// The port number on which the service is provided. + pub port: U16, + + /// The domain name of the target host. + pub name: Name, +} + +//--- Canonical operations + +impl CanonicalRecordData for Srv { + fn build_canonical_bytes<'b>( + &self, + bytes: &'b mut [u8], + ) -> Result<&'b mut [u8], TruncationError> { + let bytes = self.priority.build_bytes(bytes)?; + let bytes = self.weight.build_bytes(bytes)?; + let bytes = self.port.build_bytes(bytes)?; + let bytes = self.name.build_lowercased_bytes(bytes)?; + Ok(bytes) + } + + fn cmp_canonical(&self, other: &Self) -> Ordering { + // `Srv` uses canonical comparisons by default. + self.cmp(other) + } +} + +//--- Building into DNS messages + +impl BuildInMessage for Srv { + fn build_in_message( + &self, + contents: &mut [u8], + start: usize, + _compressor: &mut NameCompressor, + ) -> Result { + let bytes = self.as_bytes(); + let end = start + bytes.len(); + contents + .get_mut(start..end) + .ok_or(TruncationError)? + .copy_from_slice(bytes); + Ok(end) + } +} + +//--- Cloning + +#[cfg(feature = "alloc")] +impl Clone for alloc::boxed::Box { + fn clone(&self) -> Self { + (*self).unsized_copy_into() + } +} + +//--- Parsing record data + +impl<'a> ParseRecordData<'a> for &'a Srv {} + +impl<'a> ParseRecordDataBytes<'a> for &'a Srv { + fn parse_record_data_bytes( + bytes: &'a [u8], + rtype: RType, + ) -> Result { + match rtype { + RType::SRV => Self::parse_bytes(bytes), + _ => Err(ParseError), + } + } +}