1use super::SvcParams;
6use crate::base::iana::Rtype;
7use crate::base::name::{FlattenInto, ParsedDname, ToDname};
8use crate::base::rdata::{
9 ComposeRecordData, LongRecordData, ParseRecordData, RecordData,
10};
11use crate::base::wire::{Compose, Composer, Parse, ParseError};
12use octseq::octets::{Octets, OctetsFrom, OctetsInto};
13use octseq::parse::Parser;
14use core::{cmp, fmt, hash};
15use core::marker::PhantomData;
16
17#[derive(Clone)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60#[cfg_attr(
61 feature = "serde",
62 serde(
63 bound(
64 serialize = "
65 Name: serde::Serialize,
66 Octs: octseq::serde::SerializeOctets
67 ",
68 deserialize = "
69 Name: serde::Deserialize<'de>,
70 Octs: octseq::serde::DeserializeOctets<'de>
71 ",
72 )
73 )
74)]
75pub struct SvcbRdata<Variant, Octs, Name> {
76 priority: u16,
78
79 target: Name,
81
82 params: SvcParams<Octs>,
84
85 marker: PhantomData<Variant>,
87}
88
89#[derive(Clone, Copy, Debug)]
94pub struct SvcbVariant;
95
96#[derive(Clone, Copy, Debug)]
101pub struct HttpsVariant;
102
103pub type Svcb<Octs, Name> = SvcbRdata<SvcbVariant, Octs, Name>;
110
111pub type Https<Octs, Name> = SvcbRdata<HttpsVariant, Octs, Name>;
117
118impl<Variant, Octs, Name> SvcbRdata<Variant, Octs, Name> {
119 pub fn new(
124 priority: u16, target: Name, params: SvcParams<Octs>
125 ) -> Result<Self, LongRecordData>
126 where Octs: AsRef<[u8]>, Name: ToDname {
127 LongRecordData::check_len(
128 usize::from(
129 u16::COMPOSE_LEN + target.compose_len()
130 ).checked_add(params.len()).expect("long params")
131 )?;
132 Ok( unsafe { Self::new_unchecked(priority, target, params) })
133 }
134
135 pub unsafe fn new_unchecked(
142 priority: u16, target: Name, params: SvcParams<Octs>
143 ) -> Self {
144 SvcbRdata { priority, target, params, marker: PhantomData }
145 }
146}
147
148impl<Variant, Octs: AsRef<[u8]>> SvcbRdata<Variant, Octs, ParsedDname<Octs>> {
149 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
151 parser: &mut Parser<'a, Src>
152 ) -> Result<Self, ParseError> {
153 let priority = u16::parse(parser)?;
154 let target = ParsedDname::parse(parser)?;
155 let params = SvcParams::parse(parser)?;
156 Ok(unsafe {
157 Self::new_unchecked(priority, target, params)
158 })
159 }
160}
161
162impl<Variant, Octs, Name> SvcbRdata<Variant, Octs, Name> {
163 pub fn priority(&self) -> u16 {
165 self.priority
166 }
167
168 pub fn is_alias(&self) -> bool {
172 self.priority == 0
173 }
174
175 pub fn is_service(&self) -> bool {
179 self.priority != 0
180 }
181
182 pub fn target(&self) -> &Name {
187 &self.target
188 }
189
190 pub fn params(&self) -> &SvcParams<Octs> {
192 &self.params
193 }
194
195 pub(crate) fn convert_octets<TOcts, TName>(
197 self
198 ) -> Result<SvcbRdata<Variant, TOcts, TName>, TOcts::Error>
199 where
200 TOcts: OctetsFrom<Octs>,
201 TName: OctetsFrom<Name, Error = TOcts::Error>,
202 {
203 Ok(unsafe {
204 SvcbRdata::new_unchecked(
205 self.priority,
206 self.target.try_octets_into()?,
207 self.params.try_octets_into()?,
208 )
209 })
210 }
211
212 pub(crate) fn flatten<TOcts, TName>(
213 self,
214 ) -> Result<SvcbRdata<Variant, TOcts, TName>, TOcts::Error>
215 where
216 TOcts: OctetsFrom<Octs>,
217 Name: FlattenInto<TName, AppendError = TOcts::Error>,
218 {
219 Ok(unsafe {
220 SvcbRdata::new_unchecked(
221 self.priority,
222 self.target.try_flatten_into()?,
223 self.params.try_octets_into()?,
224 )
225 })
226 }
227}
228
229impl<Variant, Octs, SrcOctets, Name, SrcName>
232 OctetsFrom<SvcbRdata<Variant, SrcOctets, SrcName>>
233 for SvcbRdata<Variant, Octs, Name>
234where
235 Octs: OctetsFrom<SrcOctets>,
236 Name: OctetsFrom<SrcName, Error = Octs::Error>,
237{
238 type Error = Octs::Error;
239
240 fn try_octets_from(
241 source: SvcbRdata<Variant, SrcOctets, SrcName>,
242 ) -> Result<Self, Self::Error> {
243 source.convert_octets()
244 }
245}
246
247impl<Variant, Octs, TOcts, Name, TName>
248 FlattenInto<SvcbRdata<Variant, TOcts, TName>>
249 for SvcbRdata<Variant, Octs, Name>
250where
251 TOcts: OctetsFrom<Octs>,
252 Name: FlattenInto<TName, AppendError = TOcts::Error>
253{
254 type AppendError = TOcts::Error;
255
256 fn try_flatten_into(
257 self
258 ) -> Result<SvcbRdata<Variant, TOcts, TName>, TOcts::Error> {
259 self.flatten()
260 }
261}
262
263impl<Variant, OtherVariant, Octs, OtherOcts, Name, OtherName>
266 PartialEq<SvcbRdata<OtherVariant, OtherOcts, OtherName>>
267for SvcbRdata<Variant, Octs, Name>
268where
269 Octs: AsRef<[u8]>,
270 OtherOcts: AsRef<[u8]>,
271 Name: ToDname,
272 OtherName: ToDname,
273{
274 fn eq(
275 &self, other: &SvcbRdata<OtherVariant, OtherOcts, OtherName>
276 ) -> bool {
277 self.priority == other.priority
278 && self.target.name_eq(&other.target)
279 && self.params == other.params
280 }
281}
282
283impl<Variant, Octs: AsRef<[u8]>, Name: ToDname> Eq
284for SvcbRdata<Variant, Octs, Name> { }
285
286impl<Variant, Octs: AsRef<[u8]>, Name: hash::Hash> hash::Hash
289for SvcbRdata<Variant, Octs, Name> {
290 fn hash<H: hash::Hasher>(&self, state: &mut H) {
291 self.priority.hash(state);
292 self.target.hash(state);
293 self.params.hash(state);
294 }
295}
296
297impl<Variant, OtherVariant, Octs, OtherOcts, Name, OtherName>
300 PartialOrd<SvcbRdata<OtherVariant, OtherOcts, OtherName>>
301for SvcbRdata<Variant, Octs, Name>
302where
303 Octs: AsRef<[u8]>,
304 OtherOcts: AsRef<[u8]>,
305 Name: ToDname,
306 OtherName: ToDname,
307{
308 fn partial_cmp(
309 &self, other: &SvcbRdata<OtherVariant, OtherOcts, OtherName>
310 ) -> Option<cmp::Ordering> {
311 match self.priority.partial_cmp(&other.priority) {
312 Some(cmp::Ordering::Equal) => { }
313 other => return other
314 }
315 match self.target.name_cmp(&other.target) {
316 cmp::Ordering::Equal => { }
317 other => return Some(other)
318 }
319 self.params.partial_cmp(&other.params)
320 }
321}
322
323impl<Variant, Octs: AsRef<[u8]>, Name: ToDname> Ord
324for SvcbRdata<Variant, Octs, Name> {
325 fn cmp(&self, other: &Self) -> cmp::Ordering {
326 match self.priority.cmp(&other.priority) {
327 cmp::Ordering::Equal => { }
328 other => return other
329 }
330 match self.target.name_cmp(&other.target) {
331 cmp::Ordering::Equal => { }
332 other => return other
333 }
334 self.params.cmp(&other.params)
335 }
336}
337
338impl<Octs, Name> RecordData for SvcbRdata<SvcbVariant, Octs, Name> {
341 fn rtype(&self) -> Rtype {
342 Rtype::Svcb
343 }
344}
345
346impl<Octs, Name> RecordData for SvcbRdata<HttpsVariant, Octs, Name> {
347 fn rtype(&self) -> Rtype {
348 Rtype::Https
349 }
350}
351
352impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
353for SvcbRdata<SvcbVariant, Octs::Range<'a>, ParsedDname<Octs::Range<'a>>> {
354 fn parse_rdata(
355 rtype: Rtype, parser: &mut Parser<'a, Octs>
356 ) -> Result<Option<Self>, ParseError> {
357 if rtype == Rtype::Svcb {
358 Self::parse(parser).map(Some)
359 }
360 else {
361 Ok(None)
362 }
363 }
364}
365
366impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
367for SvcbRdata<HttpsVariant, Octs::Range<'a>, ParsedDname<Octs::Range<'a>>> {
368 fn parse_rdata(
369 rtype: Rtype, parser: &mut Parser<'a, Octs>
370 ) -> Result<Option<Self>, ParseError> {
371 if rtype == Rtype::Https{
372 Self::parse(parser).map(Some)
373 }
374 else {
375 Ok(None)
376 }
377 }
378}
379
380impl<Variant, Octs, Name> ComposeRecordData for SvcbRdata<Variant, Octs, Name>
381where Self: RecordData, Octs: AsRef<[u8]>, Name: ToDname {
382 fn rdlen(&self, _compress: bool) -> Option<u16> {
383 Some(
384 u16::checked_add(
385 u16::COMPOSE_LEN + self.target.compose_len(),
386 self.params.len().try_into().expect("long params"),
387 ).expect("long record data")
388 )
389 }
390
391 fn compose_rdata<Target: Composer + ?Sized>(
392 &self, target: &mut Target
393 ) -> Result<(), Target::AppendError> {
394 self.priority.compose(target)?;
395 self.target.compose(target)?;
396 self.params.compose(target)
397 }
398
399 fn compose_canonical_rdata<Target: Composer + ?Sized>(
400 &self, target: &mut Target
401 ) -> Result<(), Target::AppendError> {
402 self.compose_rdata(target)
403 }
404}
405
406impl<Variant, Octs, Name> fmt::Display for SvcbRdata<Variant, Octs, Name>
409where
410 Octs: Octets,
411 Name: fmt::Display,
412{
413 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
414 write!(f, "{} {} {}", self.priority, self.target, self.params)
415 }
416}
417
418impl<Variant, Octs, Name> fmt::Debug for SvcbRdata<Variant, Octs, Name>
419where
420 Octs: Octets,
421 Name: fmt::Debug,
422{
423 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424 f.debug_struct("SvcbRdata")
425 .field("priority", &self.priority)
426 .field("target", &self.target)
427 .field("params", &self.params)
428 .finish()
429 }
430}
431
432#[cfg(test)]
435mod test {
436 use super::*;
437 use super::super::UnknownSvcParam;
438 use super::super::value::AllValues;
439 use crate::base::Dname;
440 use octseq::array::Array;
441 use core::str::FromStr;
442
443 type Octets512 = Array<512>;
444 type Dname512 = Dname<Array<512>>;
445 type Params512 = SvcParams<Array<512>>;
446
447 #[test]
452 fn test_vectors_alias() {
453 let rdata =
454 b"\x00\x00\
455 \x03\x66\x6f\x6f\
456 \x07\x65\x78\x61\x6d\x70\x6c\x65\
457 \x03\x63\x6f\x6d\
458 \x00\
459 ";
460
461 let mut parser = Parser::from_ref(rdata.as_ref());
463 let svcb = Svcb::parse(&mut parser).unwrap();
464 assert_eq!(0, svcb.priority);
465 assert_eq!(
466 Dname512::from_str("foo.example.com").unwrap(),
467 svcb.target
468 );
469 assert_eq!(0, svcb.params.len());
470
471 let svcb_builder = Svcb::new(
473 svcb.priority, svcb.target, Params512::default()
474 ).unwrap();
475
476 let mut buf = Octets512::new();
477 svcb_builder.compose_rdata(&mut buf).unwrap();
478 assert_eq!(rdata.as_ref(), buf.as_ref());
479 }
480
481 #[test]
482 fn test_vectors_unknown_param() {
483 let rdata =
484 b"\x00\x01\
485 \x03\x66\x6f\x6f\
486 \x07\x65\x78\x61\x6d\x70\x6c\x65\
487 \x03\x63\x6f\x6d\
488 \x00\
489 \x02\x9b\
490 \x00\x05\
491 \x68\x65\x6c\x6c\x6f\
492 ";
493
494 let mut parser = Parser::from_ref(rdata.as_ref());
496 let svcb = Svcb::parse(&mut parser).unwrap();
497 assert_eq!(1, svcb.priority);
498 assert_eq!(
499 Dname512::from_str("foo.example.com").unwrap(),
500 svcb.target
501 );
502
503 let mut param_iter = svcb.params().iter();
504 match param_iter.next() {
505 Some(Ok(AllValues::Unknown(param))) => {
506 assert_eq!(0x029b, param.key());
507 assert_eq!(b"\x68\x65\x6c\x6c\x6f".as_ref(), *param.value(),);
508 }
509 r => panic!("{:?}", r),
510 }
511 assert_eq!(None, param_iter.next());
512
513 let svcb_builder = Svcb::new(
515 svcb.priority, svcb.target,
516 Params512::from_values(|builder| {
517 builder.push(
518 &UnknownSvcParam::new(0x029b.into(), b"hello").unwrap()
519 )
520 }).unwrap()
521 ).unwrap();
522 let mut buf = Octets512::new();
523 svcb_builder.compose_rdata(&mut buf).unwrap();
524 assert_eq!(rdata.as_ref(), buf.as_ref());
525 }
526}
527