1use super::SvcParams;
6use crate::base::cmp::CanonicalOrd;
7use crate::base::iana::Rtype;
8use crate::base::name::{FlattenInto, ParsedName, ToName};
9use crate::base::rdata::{
10 ComposeRecordData, LongRecordData, ParseRecordData, RecordData,
11};
12use crate::base::wire::{Compose, Composer, Parse, ParseError};
13use crate::base::zonefile_fmt::{self, Formatter, ZonefileFmt};
14use core::marker::PhantomData;
15use core::{cmp, fmt, hash};
16use octseq::octets::{Octets, OctetsFrom, OctetsInto};
17use octseq::parse::Parser;
18
19#[derive(Clone)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62#[cfg_attr(
63 feature = "serde",
64 serde(
65 bound(
66 serialize = "
67 Name: serde::Serialize,
68 Octs: octseq::serde::SerializeOctets
69 ",
70 deserialize = "
71 Name: serde::Deserialize<'de>,
72 Octs: octseq::serde::DeserializeOctets<'de>
73 ",
74 )
75 )
76)]
77pub struct SvcbRdata<Variant, Octs, Name> {
78 priority: u16,
80
81 target: Name,
83
84 params: SvcParams<Octs>,
86
87 marker: PhantomData<Variant>,
89}
90
91#[derive(Clone, Copy, Debug)]
96pub struct SvcbVariant;
97
98#[derive(Clone, Copy, Debug)]
103pub struct HttpsVariant;
104
105pub type Svcb<Octs, Name> = SvcbRdata<SvcbVariant, Octs, Name>;
112
113pub type Https<Octs, Name> = SvcbRdata<HttpsVariant, Octs, Name>;
119
120impl SvcbRdata<SvcbVariant, (), ()> {
121 pub(crate) const RTYPE: Rtype = Rtype::SVCB;
123}
124
125impl SvcbRdata<HttpsVariant, (), ()> {
126 pub(crate) const RTYPE: Rtype = Rtype::HTTPS;
128}
129
130impl<Variant, Octs, Name> SvcbRdata<Variant, Octs, Name> {
131 pub fn new(
136 priority: u16, target: Name, params: SvcParams<Octs>
137 ) -> Result<Self, LongRecordData>
138 where Octs: AsRef<[u8]>, Name: ToName {
139 LongRecordData::check_len(
140 usize::from(
141 u16::COMPOSE_LEN + target.compose_len()
142 ).checked_add(params.len()).expect("long params")
143 )?;
144 Ok( unsafe { Self::new_unchecked(priority, target, params) })
145 }
146
147 pub unsafe fn new_unchecked(
154 priority: u16, target: Name, params: SvcParams<Octs>
155 ) -> Self {
156 SvcbRdata { priority, target, params, marker: PhantomData }
157 }
158}
159
160impl<Variant, Octs: AsRef<[u8]>> SvcbRdata<Variant, Octs, ParsedName<Octs>> {
161 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
163 parser: &mut Parser<'a, Src>,
164 ) -> Result<Self, ParseError> {
165 let priority = u16::parse(parser)?;
166 let target = ParsedName::parse(parser)?;
167 let params = SvcParams::parse(parser)?;
168 Ok(unsafe { Self::new_unchecked(priority, target, params) })
169 }
170}
171
172impl<Variant, Octs, Name> SvcbRdata<Variant, Octs, Name> {
173 pub fn priority(&self) -> u16 {
175 self.priority
176 }
177
178 pub fn is_alias(&self) -> bool {
182 self.priority == 0
183 }
184
185 pub fn is_service(&self) -> bool {
189 self.priority != 0
190 }
191
192 pub fn target(&self) -> &Name {
197 &self.target
198 }
199
200 pub fn params(&self) -> &SvcParams<Octs> {
202 &self.params
203 }
204
205 pub(crate) fn convert_octets<TOcts, TName>(
207 self
208 ) -> Result<SvcbRdata<Variant, TOcts, TName>, TOcts::Error>
209 where
210 TOcts: OctetsFrom<Octs>,
211 TName: OctetsFrom<Name, Error = TOcts::Error>,
212 {
213 Ok(unsafe {
214 SvcbRdata::new_unchecked(
215 self.priority,
216 self.target.try_octets_into()?,
217 self.params.try_octets_into()?,
218 )
219 })
220 }
221
222 pub(crate) fn flatten<TOcts, TName>(
223 self,
224 ) -> Result<SvcbRdata<Variant, TOcts, TName>, TOcts::Error>
225 where
226 TOcts: OctetsFrom<Octs>,
227 Name: FlattenInto<TName, AppendError = TOcts::Error>,
228 {
229 Ok(unsafe {
230 SvcbRdata::new_unchecked(
231 self.priority,
232 self.target.try_flatten_into()?,
233 self.params.try_octets_into()?,
234 )
235 })
236 }
237}
238
239impl<Variant, Octs, SrcOctets, Name, SrcName>
242 OctetsFrom<SvcbRdata<Variant, SrcOctets, SrcName>>
243 for SvcbRdata<Variant, Octs, Name>
244where
245 Octs: OctetsFrom<SrcOctets>,
246 Name: OctetsFrom<SrcName, Error = Octs::Error>,
247{
248 type Error = Octs::Error;
249
250 fn try_octets_from(
251 source: SvcbRdata<Variant, SrcOctets, SrcName>,
252 ) -> Result<Self, Self::Error> {
253 source.convert_octets()
254 }
255}
256
257impl<Variant, Octs, TOcts, Name, TName>
258 FlattenInto<SvcbRdata<Variant, TOcts, TName>>
259 for SvcbRdata<Variant, Octs, Name>
260where
261 TOcts: OctetsFrom<Octs>,
262 Name: FlattenInto<TName, AppendError = TOcts::Error>,
263{
264 type AppendError = TOcts::Error;
265
266 fn try_flatten_into(
267 self,
268 ) -> Result<SvcbRdata<Variant, TOcts, TName>, TOcts::Error> {
269 self.flatten()
270 }
271}
272
273impl<Variant, OtherVariant, Octs, OtherOcts, Name, OtherName>
276 PartialEq<SvcbRdata<OtherVariant, OtherOcts, OtherName>>
277for SvcbRdata<Variant, Octs, Name>
278where
279 Octs: AsRef<[u8]>,
280 OtherOcts: AsRef<[u8]>,
281 Name: ToName,
282 OtherName: ToName,
283{
284 fn eq(
285 &self, other: &SvcbRdata<OtherVariant, OtherOcts, OtherName>
286 ) -> bool {
287 self.priority == other.priority
288 && self.target.name_eq(&other.target)
289 && self.params == other.params
290 }
291}
292
293impl<Variant, Octs: AsRef<[u8]>, Name: ToName> Eq
294for SvcbRdata<Variant, Octs, Name> { }
295
296impl<Variant, Octs: AsRef<[u8]>, Name: hash::Hash> hash::Hash
299for SvcbRdata<Variant, Octs, Name> {
300 fn hash<H: hash::Hasher>(&self, state: &mut H) {
301 self.priority.hash(state);
302 self.target.hash(state);
303 self.params.hash(state);
304 }
305}
306
307impl<Variant, OtherVariant, Octs, OtherOcts, Name, OtherName>
310 PartialOrd<SvcbRdata<OtherVariant, OtherOcts, OtherName>>
311for SvcbRdata<Variant, Octs, Name>
312where
313 Octs: AsRef<[u8]>,
314 OtherOcts: AsRef<[u8]>,
315 Name: ToName,
316 OtherName: ToName,
317{
318 fn partial_cmp(
319 &self, other: &SvcbRdata<OtherVariant, OtherOcts, OtherName>
320 ) -> Option<cmp::Ordering> {
321 match self.priority.partial_cmp(&other.priority) {
322 Some(cmp::Ordering::Equal) => { }
323 other => return other
324 }
325 match self.target.name_cmp(&other.target) {
326 cmp::Ordering::Equal => { }
327 other => return Some(other)
328 }
329 self.params.partial_cmp(&other.params)
330 }
331}
332
333impl<Variant, Octs: AsRef<[u8]>, Name: ToName> Ord
334for SvcbRdata<Variant, Octs, Name> {
335 fn cmp(&self, other: &Self) -> cmp::Ordering {
336 match self.priority.cmp(&other.priority) {
337 cmp::Ordering::Equal => { }
338 other => return other
339 }
340 match self.target.name_cmp(&other.target) {
341 cmp::Ordering::Equal => { }
342 other => return other
343 }
344 self.params.cmp(&other.params)
345 }
346}
347
348impl<Variant, OtherVariant, Octs, OtherOcts, Name, OtherName>
349 CanonicalOrd<SvcbRdata<OtherVariant, OtherOcts, OtherName>>
350for SvcbRdata<Variant, Octs, Name>
351where
352 Octs: AsRef<[u8]>,
353 OtherOcts: AsRef<[u8]>,
354 Name: ToName,
355 OtherName: ToName,
356{
357 fn canonical_cmp(
358 &self, other: &SvcbRdata<OtherVariant, OtherOcts, OtherName>
359 ) -> cmp::Ordering {
360 match self.priority.cmp(&other.priority) {
361 cmp::Ordering::Equal => { }
362 other => return other
363 }
364 match self.target.name_cmp(&other.target) {
365 cmp::Ordering::Equal => { }
366 other => return other
367 }
368 self.params.canonical_cmp(&other.params)
369 }
370}
371
372impl<Octs, Name> RecordData for SvcbRdata<SvcbVariant, Octs, Name> {
375 fn rtype(&self) -> Rtype {
376 Rtype::SVCB
377 }
378}
379
380impl<Octs, Name> RecordData for SvcbRdata<HttpsVariant, Octs, Name> {
381 fn rtype(&self) -> Rtype {
382 Rtype::HTTPS
383 }
384}
385
386impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
387for SvcbRdata<SvcbVariant, Octs::Range<'a>, ParsedName<Octs::Range<'a>>> {
388 fn parse_rdata(
389 rtype: Rtype, parser: &mut Parser<'a, Octs>
390 ) -> Result<Option<Self>, ParseError> {
391 if rtype == Rtype::SVCB {
392 Self::parse(parser).map(Some)
393 }
394 else {
395 Ok(None)
396 }
397 }
398}
399
400impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
401for SvcbRdata<HttpsVariant, Octs::Range<'a>, ParsedName<Octs::Range<'a>>> {
402 fn parse_rdata(
403 rtype: Rtype, parser: &mut Parser<'a, Octs>
404 ) -> Result<Option<Self>, ParseError> {
405 if rtype == Rtype::HTTPS {
406 Self::parse(parser).map(Some)
407 }
408 else {
409 Ok(None)
410 }
411 }
412}
413
414impl<Variant, Octs, Name> ComposeRecordData for SvcbRdata<Variant, Octs, Name>
415where Self: RecordData, Octs: AsRef<[u8]>, Name: ToName {
416 fn rdlen(&self, _compress: bool) -> Option<u16> {
417 Some(
418 u16::checked_add(
419 u16::COMPOSE_LEN + self.target.compose_len(),
420 self.params.len().try_into().expect("long params"),
421 ).expect("long record data")
422 )
423 }
424
425 fn compose_rdata<Target: Composer + ?Sized>(
426 &self, target: &mut Target
427 ) -> Result<(), Target::AppendError> {
428 self.priority.compose(target)?;
429 self.target.compose(target)?;
430 self.params.compose(target)
431 }
432
433 fn compose_canonical_rdata<Target: Composer + ?Sized>(
434 &self, target: &mut Target
435 ) -> Result<(), Target::AppendError> {
436 self.compose_rdata(target)
437 }
438}
439
440impl<Variant, Octs, Name> fmt::Display for SvcbRdata<Variant, Octs, Name>
443where
444 Octs: Octets,
445 Name: fmt::Display,
446{
447 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
448 write!(f, "{} {}. {}", self.priority, self.target, self.params)
449 }
450}
451
452impl<Variant, Octs, Name> fmt::Debug for SvcbRdata<Variant, Octs, Name>
453where
454 Octs: Octets,
455 Name: fmt::Debug,
456{
457 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
458 f.debug_struct("SvcbRdata")
459 .field("priority", &self.priority)
460 .field("target", &self.target)
461 .field("params", &self.params)
462 .finish()
463 }
464}
465
466impl<Variant, Octs, Name> ZonefileFmt for SvcbRdata<Variant, Octs, Name>
469where
470 Octs: Octets,
471 Name: ToName,
472{
473 fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
474 p.block(|p| {
475 p.write_token(self.priority)?;
476 p.write_comment("priority")?;
477 p.write_token(self.target.fmt_with_dot())?;
478 p.write_comment("target")?;
479 p.write_show(&self.params)
480 })
481 }
482}
483
484#[cfg(test)]
487mod test {
488 use super::*;
489 use super::super::UnknownSvcParam;
490 use super::super::value::AllValues;
491 use crate::base::Name;
492 use octseq::array::Array;
493 use core::str::FromStr;
494
495 type Octets512 = Array<512>;
496 type Dname512 = Name<Array<512>>;
497 type Params512 = SvcParams<Array<512>>;
498
499 #[test]
504 fn test_vectors_alias() {
505 let rdata =
506 b"\x00\x00\
507 \x03\x66\x6f\x6f\
508 \x07\x65\x78\x61\x6d\x70\x6c\x65\
509 \x03\x63\x6f\x6d\
510 \x00\
511 ";
512
513 let mut parser = Parser::from_ref(rdata.as_ref());
515 let svcb = Svcb::parse(&mut parser).unwrap();
516 assert_eq!(0, svcb.priority);
517 assert_eq!(
518 Dname512::from_str("foo.example.com").unwrap(),
519 svcb.target
520 );
521 assert_eq!(0, svcb.params.len());
522
523 let svcb_builder = Svcb::new(
525 svcb.priority, svcb.target, Params512::default()
526 ).unwrap();
527
528 let mut buf = Octets512::new();
529 svcb_builder.compose_rdata(&mut buf).unwrap();
530 assert_eq!(rdata.as_ref(), buf.as_ref());
531 }
532
533 #[test]
534 fn test_vectors_unknown_param() {
535 let rdata =
536 b"\x00\x01\
537 \x03\x66\x6f\x6f\
538 \x07\x65\x78\x61\x6d\x70\x6c\x65\
539 \x03\x63\x6f\x6d\
540 \x00\
541 \x02\x9b\
542 \x00\x05\
543 \x68\x65\x6c\x6c\x6f\
544 ";
545
546 let mut parser = Parser::from_ref(rdata.as_ref());
548 let svcb = Svcb::parse(&mut parser).unwrap();
549 assert_eq!(1, svcb.priority);
550 assert_eq!(
551 Dname512::from_str("foo.example.com").unwrap(),
552 svcb.target
553 );
554
555 let mut param_iter = svcb.params().iter();
556 match param_iter.next() {
557 Some(Ok(AllValues::Unknown(param))) => {
558 assert_eq!(0x029b, param.key().to_int());
559 assert_eq!(b"\x68\x65\x6c\x6c\x6f".as_ref(), *param.value(),);
560 }
561 r => panic!("{:?}", r),
562 }
563 assert_eq!(None, param_iter.next());
564
565 let svcb_builder = Svcb::new(
567 svcb.priority, svcb.target,
568 Params512::from_values(|builder| {
569 builder.push(
570 &UnknownSvcParam::new(0x029b.into(), b"hello").unwrap()
571 )
572 }).unwrap()
573 ).unwrap();
574 let mut buf = Octets512::new();
575 svcb_builder.compose_rdata(&mut buf).unwrap();
576 assert_eq!(rdata.as_ref(), buf.as_ref());
577 }
578}
579