mozim/dhcpv6/
option.rs

1// SPDX-License-Identifier: Apache-2.0
2
3use std::{cmp::Ordering, collections::HashMap, net::Ipv6Addr};
4
5use crate::{
6    Buffer, BufferMut, DhcpError, DhcpV6Duid, DhcpV6OptionIaAddr,
7    DhcpV6OptionIaNa, DhcpV6OptionIaPd, DhcpV6OptionIaPrefix, DhcpV6OptionIaTa,
8    DhcpV6OptionStatus, ErrorContext, ErrorKind,
9};
10
11#[derive(Debug, PartialEq, Eq, Clone, Default)]
12pub(crate) struct DhcpV6Options {
13    data: HashMap<DhcpV6OptionCode, Vec<DhcpV6Option>>,
14}
15
16impl DhcpV6Options {
17    pub(crate) fn new() -> Self {
18        Self::default()
19    }
20
21    pub(crate) fn get_data_raw(&self, code: u16) -> Option<Vec<Vec<u8>>> {
22        let mut ret: Vec<Vec<u8>> = Vec::new();
23        if let Some(opts) = self.data.get(&DhcpV6OptionCode::from(code)) {
24            for opt in opts {
25                let mut buf = BufferMut::new();
26                opt.emit(&mut buf);
27                ret.push(buf.data);
28            }
29            Some(ret)
30        } else {
31            None
32        }
33    }
34
35    pub(crate) fn get_first(
36        &self,
37        code: DhcpV6OptionCode,
38    ) -> Option<&DhcpV6Option> {
39        self.data
40            .get(&code)
41            .map(|opts| opts.as_slice())
42            .and_then(|opts| opts.first())
43    }
44
45    pub(crate) fn insert(&mut self, opt: DhcpV6Option) {
46        self.data.entry(opt.code()).or_default().push(opt);
47    }
48
49    pub(crate) fn remove(&mut self, code: DhcpV6OptionCode) {
50        self.data.remove(&code);
51    }
52
53    pub(crate) fn parse(buf: &mut Buffer) -> Result<Self, DhcpError> {
54        let mut ret = Self::new();
55        while !buf.is_empty() {
56            match DhcpV6Option::parse(buf) {
57                Ok(opt) => {
58                    ret.insert(opt);
59                }
60                Err(e) => {
61                    log::info!(
62                        "Ignore DHCPv6 option due to parsing error: {e}"
63                    );
64                    continue;
65                }
66            }
67        }
68        Ok(ret)
69    }
70
71    pub(crate) fn emit(&self, buf: &mut BufferMut) {
72        let mut all_opts: Vec<&DhcpV6Option> = Vec::new();
73
74        for opts in self.data.values() {
75            for opt in opts {
76                all_opts.push(opt);
77            }
78        }
79
80        all_opts.sort_unstable();
81
82        for opt in all_opts {
83            opt.emit(buf);
84        }
85    }
86}
87
88const OPTION_CLIENTID: u16 = 1;
89const OPTION_SERVERID: u16 = 2;
90const OPTION_IA_NA: u16 = 3;
91const OPTION_IA_TA: u16 = 4;
92const OPTION_IAADDR: u16 = 5;
93const OPTION_ORO: u16 = 6;
94const OPTION_PREFERENCE: u16 = 7;
95const OPTION_ELAPSED_TIME: u16 = 8;
96const OPTION_UNICAST: u16 = 12;
97const OPTION_STATUS_CODE: u16 = 13;
98const OPTION_RAPID_COMMIT: u16 = 14;
99const OPTION_DNS_SERVERS: u16 = 23;
100const OPTION_DOMAIN_LIST: u16 = 24;
101const OPTION_IA_PD: u16 = 25;
102const OPTION_IAPREFIX: u16 = 26;
103const OPTION_NTP_SERVER: u16 = 56;
104
105#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)]
106#[non_exhaustive]
107pub enum DhcpV6OptionCode {
108    #[default]
109    ClientId,
110    ServerId,
111    IANA,
112    IATA,
113    IAPD,
114    IAAddr,
115    IAPrefix,
116    OptionRequestOption,
117    Preference,
118    ElapsedTime,
119    ServerUnicast,
120    StatusCode,
121    RapidCommit,
122    DnsServers,
123    DomainList,
124    NtpServer,
125    Other(u16),
126}
127
128impl From<DhcpV6OptionCode> for u16 {
129    fn from(v: DhcpV6OptionCode) -> u16 {
130        match v {
131            DhcpV6OptionCode::ClientId => OPTION_CLIENTID,
132            DhcpV6OptionCode::ServerId => OPTION_SERVERID,
133            DhcpV6OptionCode::IANA => OPTION_IA_NA,
134            DhcpV6OptionCode::IATA => OPTION_IA_TA,
135            DhcpV6OptionCode::IAPD => OPTION_IA_PD,
136            DhcpV6OptionCode::IAAddr => OPTION_IAADDR,
137            DhcpV6OptionCode::IAPrefix => OPTION_IAPREFIX,
138            DhcpV6OptionCode::OptionRequestOption => OPTION_ORO,
139            DhcpV6OptionCode::Preference => OPTION_PREFERENCE,
140            DhcpV6OptionCode::ElapsedTime => OPTION_ELAPSED_TIME,
141            DhcpV6OptionCode::ServerUnicast => OPTION_UNICAST,
142            DhcpV6OptionCode::StatusCode => OPTION_STATUS_CODE,
143            DhcpV6OptionCode::RapidCommit => OPTION_RAPID_COMMIT,
144            DhcpV6OptionCode::DnsServers => OPTION_DNS_SERVERS,
145            DhcpV6OptionCode::DomainList => OPTION_DOMAIN_LIST,
146            DhcpV6OptionCode::NtpServer => OPTION_NTP_SERVER,
147            DhcpV6OptionCode::Other(d) => d,
148        }
149    }
150}
151
152impl From<u16> for DhcpV6OptionCode {
153    fn from(d: u16) -> Self {
154        match d {
155            OPTION_CLIENTID => Self::ClientId,
156            OPTION_SERVERID => Self::ServerId,
157            OPTION_IA_NA => Self::IANA,
158            OPTION_IA_TA => Self::IATA,
159            OPTION_IA_PD => Self::IAPD,
160            OPTION_IAADDR => Self::IAAddr,
161            OPTION_IAPREFIX => Self::IAPrefix,
162            OPTION_ORO => Self::OptionRequestOption,
163            OPTION_PREFERENCE => Self::Preference,
164            OPTION_ELAPSED_TIME => Self::ElapsedTime,
165            OPTION_UNICAST => Self::ServerUnicast,
166            OPTION_STATUS_CODE => Self::StatusCode,
167            OPTION_RAPID_COMMIT => Self::RapidCommit,
168            OPTION_DNS_SERVERS => Self::DnsServers,
169            OPTION_DOMAIN_LIST => Self::DomainList,
170            OPTION_NTP_SERVER => Self::NtpServer,
171            _ => Self::Other(d),
172        }
173    }
174}
175
176impl Ord for DhcpV6OptionCode {
177    fn cmp(&self, other: &Self) -> Ordering {
178        u16::from(*self).cmp(&u16::from(*other))
179    }
180}
181
182impl PartialOrd for DhcpV6OptionCode {
183    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
184        Some(self.cmp(other))
185    }
186}
187
188impl std::fmt::Display for DhcpV6OptionCode {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        match self {
191            Self::ClientId => write!(f, "OPTION_CLIENTID"),
192            Self::ServerId => write!(f, "OPTION_SERVERID"),
193            Self::IANA => write!(f, "OPTION_IA_NA"),
194            Self::IATA => write!(f, "OPTION_IA_TA"),
195            Self::IAPD => write!(f, "OPTION_IA_PD"),
196            Self::IAAddr => write!(f, "OPTION_IAADDR"),
197            Self::IAPrefix => write!(f, "OPTION_IAPREFIX"),
198            Self::OptionRequestOption => write!(f, "OPTION_ORO"),
199            Self::Preference => write!(f, "OPTION_PREFERENCE"),
200            Self::ElapsedTime => write!(f, "OPTION_ELAPSED_TIME"),
201            Self::ServerUnicast => write!(f, "OPTION_UNICAST"),
202            Self::StatusCode => write!(f, "OPTION_STATUS_CODE"),
203            Self::RapidCommit => write!(f, "OPTION_RAPID_COMMIT"),
204            Self::DnsServers => write!(f, "OPTION_DNS_SERVERS"),
205            Self::DomainList => write!(f, "OPTION_DOMAIN_LIST"),
206            Self::NtpServer => write!(f, "OPTION_NTP_SERVER"),
207            Self::Other(d) => write!(f, "Unknown({d})"),
208        }
209    }
210}
211
212#[derive(Debug, PartialEq, Eq, Clone)]
213#[non_exhaustive]
214pub enum DhcpV6Option {
215    ClientId(DhcpV6Duid),
216    ServerId(DhcpV6Duid),
217    IANA(DhcpV6OptionIaNa),
218    IATA(DhcpV6OptionIaTa),
219    IAPD(DhcpV6OptionIaPd),
220    OptionRequestOption(Vec<DhcpV6OptionCode>),
221    IAAddr(DhcpV6OptionIaAddr),
222    IAPrefix(DhcpV6OptionIaPrefix),
223    Preference(u8),
224    ElapsedTime(u16),
225    ServerUnicast(Ipv6Addr),
226    StatusCode(DhcpV6OptionStatus),
227    RapidCommit,
228    /// RFC 3646
229    DnsServers(Vec<Ipv6Addr>),
230    /// RFC 3646
231    DomainList(Vec<String>),
232    /// RFC 5908
233    NtpServer(Vec<DhcpV6OptionNtpServer>),
234    Unknown(DhcpV6OptionUnknown),
235}
236
237impl Default for DhcpV6Option {
238    fn default() -> Self {
239        Self::Unknown(DhcpV6OptionUnknown::default())
240    }
241}
242
243impl Ord for DhcpV6Option {
244    fn cmp(&self, other: &Self) -> Ordering {
245        self.code().cmp(&other.code())
246    }
247}
248
249impl PartialOrd for DhcpV6Option {
250    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
251        Some(self.cmp(other))
252    }
253}
254
255impl DhcpV6Option {
256    pub fn code(&self) -> DhcpV6OptionCode {
257        match self {
258            DhcpV6Option::ClientId(_) => DhcpV6OptionCode::ClientId,
259            DhcpV6Option::ServerId(_) => DhcpV6OptionCode::ServerId,
260            DhcpV6Option::IANA(_) => DhcpV6OptionCode::IANA,
261            DhcpV6Option::IATA(_) => DhcpV6OptionCode::IATA,
262            DhcpV6Option::OptionRequestOption(_) => {
263                DhcpV6OptionCode::OptionRequestOption
264            }
265            DhcpV6Option::Preference(_) => DhcpV6OptionCode::Preference,
266            DhcpV6Option::ElapsedTime(_) => DhcpV6OptionCode::ElapsedTime,
267            DhcpV6Option::ServerUnicast(_) => DhcpV6OptionCode::ServerUnicast,
268            DhcpV6Option::StatusCode(_) => DhcpV6OptionCode::StatusCode,
269            DhcpV6Option::RapidCommit => DhcpV6OptionCode::RapidCommit,
270            DhcpV6Option::DnsServers(_) => DhcpV6OptionCode::DnsServers,
271            DhcpV6Option::DomainList(_) => DhcpV6OptionCode::DomainList,
272            DhcpV6Option::IAPD(_) => DhcpV6OptionCode::IAPD,
273            DhcpV6Option::NtpServer(_) => DhcpV6OptionCode::NtpServer,
274            DhcpV6Option::IAAddr(_) => DhcpV6OptionCode::IAAddr,
275            DhcpV6Option::IAPrefix(_) => DhcpV6OptionCode::IAPrefix,
276            DhcpV6Option::Unknown(u) => u.code(),
277        }
278    }
279
280    pub(crate) fn parse(buf: &mut Buffer) -> Result<DhcpV6Option, DhcpError> {
281        let code: DhcpV6OptionCode = buf
282            .peek_u16_be()
283            .context("Invalid DHCPv6 option code")?
284            .into();
285        let len: usize = buf
286            .peek_u16_be_offset(2)
287            .context("Invalid DHCPv6 option length")?
288            .into();
289        let opt_raw = buf.get_bytes(len + 4).context(format!(
290            "Invalid DHCPv6 option {code} with length {len}"
291        ))?;
292        let mut opt_buf = Buffer::new(opt_raw);
293
294        Ok(match code {
295            DhcpV6OptionCode::IAAddr => {
296                Self::IAAddr(DhcpV6OptionIaAddr::parse(&mut opt_buf)?)
297            }
298            DhcpV6OptionCode::IAPrefix => {
299                Self::IAPrefix(DhcpV6OptionIaPrefix::parse(&mut opt_buf)?)
300            }
301            DhcpV6OptionCode::ClientId => {
302                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
303                opt_buf
304                    .get_u16_be()
305                    .context("Invalid DHCPv6 option length")?;
306                Self::ClientId(DhcpV6Duid::parse(&mut opt_buf, len)?)
307            }
308            DhcpV6OptionCode::ServerId => {
309                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
310                opt_buf
311                    .get_u16_be()
312                    .context("Invalid DHCPv6 option length")?;
313                Self::ServerId(DhcpV6Duid::parse(&mut opt_buf, len)?)
314            }
315            DhcpV6OptionCode::IANA => {
316                Self::IANA(DhcpV6OptionIaNa::parse(&mut opt_buf)?)
317            }
318            DhcpV6OptionCode::IATA => {
319                Self::IATA(DhcpV6OptionIaTa::parse(&mut opt_buf)?)
320            }
321            DhcpV6OptionCode::IAPD => {
322                Self::IAPD(DhcpV6OptionIaPd::parse(&mut opt_buf)?)
323            }
324            DhcpV6OptionCode::OptionRequestOption => {
325                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
326                opt_buf
327                    .get_u16_be()
328                    .context("Invalid DHCPv6 option length")?;
329                let mut opts: Vec<DhcpV6OptionCode> = Vec::new();
330                for _ in 0..len / 2 {
331                    opts.push(
332                        opt_buf
333                            .get_u16_be()
334                            .context("Invalid DHCPv6 option OPTION_ORO")?
335                            .into(),
336                    );
337                }
338                Self::OptionRequestOption(opts)
339            }
340            DhcpV6OptionCode::Preference => Self::Preference({
341                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
342                opt_buf
343                    .get_u16_be()
344                    .context("Invalid DHCPv6 option length")?;
345                opt_buf
346                    .get_u8()
347                    .context("Invalid DHCPv6 option OPTION_PREFERENCE")?
348            }),
349            DhcpV6OptionCode::ElapsedTime => Self::ElapsedTime({
350                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
351                opt_buf
352                    .get_u16_be()
353                    .context("Invalid DHCPv6 option length")?;
354                opt_buf
355                    .get_u16_be()
356                    .context("Invalid DHCPv6 option OPTION_ELAPSED_TIME")?
357            }),
358            DhcpV6OptionCode::ServerUnicast => Self::ServerUnicast({
359                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
360                opt_buf
361                    .get_u16_be()
362                    .context("Invalid DHCPv6 option length")?;
363                opt_buf
364                    .get_ipv6()
365                    .context("Invalid DHCPv6 option OPTION_UNICAST")?
366            }),
367            DhcpV6OptionCode::StatusCode => {
368                Self::StatusCode(DhcpV6OptionStatus::parse(&mut opt_buf)?)
369            }
370            DhcpV6OptionCode::RapidCommit => {
371                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
372                opt_buf
373                    .get_u16_be()
374                    .context("Invalid DHCPv6 option length")?;
375                Self::RapidCommit
376            }
377            DhcpV6OptionCode::DnsServers => {
378                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
379                opt_buf
380                    .get_u16_be()
381                    .context("Invalid DHCPv6 option length")?;
382                let mut addrs = Vec::new();
383                for _ in 0..len / 16 {
384                    addrs.push(
385                        opt_buf.get_ipv6().context(
386                            "Invalid DHCPv6 option OPTION_DNS_SERVERS",
387                        )?,
388                    );
389                }
390                Self::DnsServers(addrs)
391            }
392            DhcpV6OptionCode::DomainList => {
393                // RFC 1035: 3.1. Name space definitions
394                //      Domain names in messages are expressed in terms of a
395                //      sequence of labels.  Each label is represented as a one
396                //      octet length field followed by that number of octets.
397                //      Since every domain name ends with the null label of the
398                //      root, a domain name is terminated by a length byte of
399                //      zero.  The high order two bits of every length octet
400                //      must be zero, and the remaining six bits of the length
401                //      field limit the label to 63 octets or less.
402                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
403                opt_buf
404                    .get_u16_be()
405                    .context("Invalid DHCPv6 option length")?;
406                let raw = opt_buf
407                    .get_bytes(len)
408                    .context("Invalid DHCPv6 option OPTION_DOMAIN_LIST")?;
409                let mut tmp_opt_buf = Buffer::new(raw);
410                let mut domains = Vec::new();
411                while !tmp_opt_buf.is_empty() {
412                    let str_len = tmp_opt_buf.get_u8().context(
413                        "Invalid DHCPv6 option OPTION_DOMAIN_LIST length",
414                    )?;
415                    domains.push(
416                        tmp_opt_buf
417                            .get_string_with_null(str_len.into())
418                            .context(
419                                "Invalid DHCPv6 option OPTION_DOMAIN_LIST \
420                                 domain",
421                            )?,
422                    );
423                }
424                Self::DomainList(domains)
425            }
426            DhcpV6OptionCode::NtpServer => {
427                // RFC 5908
428                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
429                opt_buf
430                    .get_u16_be()
431                    .context("Invalid DHCPv6 option length")?;
432                let raw = opt_buf
433                    .get_bytes(len)
434                    .context("Invalid DHCPv6 option OPTION_NTP_SERVER")?;
435                let mut tmp_opt_buf = Buffer::new(raw);
436                let mut srvs: Vec<DhcpV6OptionNtpServer> = Vec::new();
437                while !tmp_opt_buf.is_empty() {
438                    srvs.push(DhcpV6OptionNtpServer::parse(&mut tmp_opt_buf)?);
439                }
440                Self::NtpServer(srvs)
441            }
442            DhcpV6OptionCode::Other(d) => Self::Unknown({
443                opt_buf.get_u16_be().context("Invalid DHCPv6 option code")?;
444                opt_buf
445                    .get_u16_be()
446                    .context("Invalid DHCPv6 option length")?;
447                DhcpV6OptionUnknown {
448                    code: d,
449                    raw: opt_buf
450                        .get_bytes(len)
451                        .context(format!("Invalid DHCPv6 option {d}"))?
452                        .to_vec(),
453                }
454            }),
455        })
456    }
457
458    pub(crate) fn emit(&self, buf: &mut BufferMut) {
459        match self {
460            Self::ClientId(id) | Self::ServerId(id) => {
461                let mut value_buf = BufferMut::new();
462                id.emit(&mut value_buf);
463                buf.write_u16_be(self.code().into());
464                buf.write_u16_be(value_buf.len() as u16);
465                buf.write_bytes(value_buf.data.as_slice());
466            }
467            Self::IAAddr(v) => v.emit(buf),
468            Self::IAPrefix(v) => v.emit(buf),
469            Self::IANA(v) => v.emit(buf),
470            Self::IATA(v) => v.emit(buf),
471            Self::IAPD(v) => v.emit(buf),
472            Self::OptionRequestOption(opts) => {
473                buf.write_u16_be(self.code().into());
474                buf.write_u16_be((opts.len() * 2) as u16);
475                for opt in opts {
476                    buf.write_u16_be((*opt).into());
477                }
478            }
479            Self::Preference(d) => {
480                buf.write_u16_be(self.code().into());
481                buf.write_u16_be(1);
482                buf.write_u8(*d);
483            }
484            Self::ElapsedTime(d) => {
485                buf.write_u16_be(self.code().into());
486                buf.write_u16_be(2);
487                buf.write_u16_be(*d);
488            }
489            Self::ServerUnicast(i) => {
490                buf.write_u16_be(self.code().into());
491                buf.write_u16_be(16);
492                buf.write_ipv6(*i);
493            }
494            Self::StatusCode(v) => {
495                v.emit(buf);
496            }
497            Self::RapidCommit => {
498                buf.write_u16_be(self.code().into());
499                buf.write_u16_be(0);
500            }
501            Self::DnsServers(addrs) => {
502                buf.write_u16_be(self.code().into());
503                buf.write_u16_be((addrs.len() * 16) as u16);
504                for addr in addrs {
505                    buf.write_ipv6(*addr);
506                }
507            }
508            Self::DomainList(domains) => {
509                let mut value_buf = BufferMut::new();
510                for domain in domains {
511                    value_buf.write_u8((domain.len() + 1) as u8);
512                    value_buf.write_string_with_null(domain, domain.len() + 1);
513                }
514                buf.write_u16_be(self.code().into());
515                buf.write_u16_be(value_buf.len() as u16);
516                buf.write_bytes(value_buf.data.as_slice());
517            }
518            Self::NtpServer(srvs) => {
519                let mut value_buf = BufferMut::new();
520                for srv in srvs {
521                    srv.emit(&mut value_buf);
522                }
523                buf.write_u16_be(self.code().into());
524                buf.write_u16_be(value_buf.len() as u16);
525                buf.write_bytes(value_buf.data.as_slice());
526            }
527            Self::Unknown(v) => {
528                buf.write_u16_be(self.code().into());
529                buf.write_u16_be(v.raw.len() as u16);
530                buf.write_bytes(v.raw.as_slice());
531            }
532        }
533    }
534}
535
536#[derive(Debug, PartialEq, Eq, Clone, Default)]
537pub struct DhcpV6OptionUnknown {
538    pub code: u16,
539    pub raw: Vec<u8>,
540}
541
542impl DhcpV6OptionUnknown {
543    pub fn code(&self) -> DhcpV6OptionCode {
544        self.code.into()
545    }
546}
547
548/// DHCPv6 Option for NTP Server
549///
550/// Defined by RFC 5908
551#[derive(Debug, PartialEq, Eq, Clone)]
552#[non_exhaustive]
553pub enum DhcpV6OptionNtpServer {
554    ServerAddr(Ipv6Addr),
555    MulticastAddr(Ipv6Addr),
556    ServerFqdn(String),
557    Other((u16, Vec<u8>)),
558}
559
560const NTP_SUBOPTION_SRV_ADDR: u16 = 1;
561const NTP_SUBOPTION_MC_ADDR: u16 = 2;
562const NTP_SUBOPTION_SRV_FQDN: u16 = 3;
563
564impl DhcpV6OptionNtpServer {
565    pub(crate) fn parse(buf: &mut Buffer) -> Result<Self, DhcpError> {
566        let subopt_type = buf
567            .get_u16_be()
568            .context("Invalid OPTION_NTP_SERVER suboption")?;
569        let subopt_len = buf
570            .get_u16_be()
571            .context("Invalid OPTION_NTP_SERVER suboption-len")?;
572        Ok(match subopt_type {
573            NTP_SUBOPTION_SRV_ADDR => {
574                Self::ServerAddr(buf.get_ipv6().context(
575                    "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_SRV_ADDR",
576                )?)
577            }
578            NTP_SUBOPTION_MC_ADDR => {
579                Self::MulticastAddr(buf.get_ipv6().context(
580                    "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_MC_ADDR",
581                )?)
582            }
583            NTP_SUBOPTION_SRV_FQDN => Self::ServerFqdn({
584                let mut lables = Vec::new();
585                let raw = buf.get_bytes(subopt_len.into()).context(
586                    "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_SRV_FQDN",
587                )?;
588                let mut fqdn_buf = Buffer::new(raw);
589                while !fqdn_buf.is_empty() {
590                    let lable_len = fqdn_buf.get_u8().context(
591                        "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_SRV_FQDN",
592                    )?;
593                    if lable_len == 0 {
594                        break;
595                    }
596                    let lable_raw =
597                        fqdn_buf.get_bytes(lable_len as usize).context(
598                            "Invalid OPTION_NTP_SERVER NTP_SUBOPTION_SRV_FQDN",
599                        )?;
600                    match std::str::from_utf8(lable_raw) {
601                        Ok(l) => lables.push(l.to_string()),
602                        Err(e) => {
603                            return Err(DhcpError::new(
604                                ErrorKind::InvalidDhcpMessage,
605                                format!(
606                                    "Invalid OPTION_NTP_SERVER \
607                                     NTP_SUBOPTION_SRV_FQDN: {e}"
608                                ),
609                            ));
610                        }
611                    }
612                }
613                lables.join(".").to_string()
614            }),
615            _ => Self::Other((
616                subopt_type,
617                buf.get_bytes(subopt_len.into())
618                    .context(format!(
619                        "Invalid OPTION_NTP_SERVER {}",
620                        subopt_type
621                    ))?
622                    .to_vec(),
623            )),
624        })
625    }
626
627    pub(crate) fn emit(&self, buf: &mut BufferMut) {
628        match self {
629            Self::ServerAddr(ip) => {
630                buf.write_u16_be(NTP_SUBOPTION_SRV_ADDR);
631                buf.write_u16_be(16);
632                buf.write_ipv6(*ip);
633            }
634            Self::MulticastAddr(ip) => {
635                buf.write_u16_be(NTP_SUBOPTION_MC_ADDR);
636                buf.write_u16_be(16);
637                buf.write_ipv6(*ip);
638            }
639            Self::ServerFqdn(name) => {
640                buf.write_u16_be(NTP_SUBOPTION_SRV_FQDN);
641                buf.write_u16_be((name.len() + 1) as u16);
642                buf.write_string_with_null(name, name.len() + 1);
643            }
644            Self::Other((subopt_type, v)) => {
645                buf.write_u16_be(*subopt_type);
646                buf.write_u16_be(v.len() as u16);
647                buf.write_bytes(v.as_slice());
648            }
649        }
650    }
651}