coap_lite/
packet.rs

1use alloc::{
2    collections::{BTreeMap, LinkedList},
3    vec::Vec,
4};
5use core::convert::TryFrom;
6
7use crate::{
8    error::{
9        IncompatibleOptionValueFormat, InvalidContentFormat, InvalidObserve,
10        MessageError,
11    },
12    header::{Header, HeaderRaw, MessageClass},
13    option_value::{OptionValueType, OptionValueU16, OptionValueU32},
14};
15
16macro_rules! u8_to_unsigned_be {
17    ($src:ident, $start:expr, $end:expr, $t:ty) => ({
18        (0..=$end - $start).rev().fold(
19            0, |acc, i| acc | $src[$start+i] as $t << i * 8
20        )
21    })
22}
23
24/// The CoAP options.
25#[derive(Debug, Clone, Copy, PartialEq)]
26pub enum CoapOption {
27    IfMatch,
28    UriHost,
29    ETag,
30    IfNoneMatch,
31    Observe,
32    UriPort,
33    LocationPath,
34    Oscore,
35    UriPath,
36    ContentFormat,
37    MaxAge,
38    UriQuery,
39    Accept,
40    LocationQuery,
41    Block2,
42    Block1,
43    ProxyUri,
44    ProxyScheme,
45    Size1,
46    Size2,
47    NoResponse,
48    Unknown(u16),
49}
50
51impl From<u16> for CoapOption {
52    fn from(number: u16) -> CoapOption {
53        match number {
54            1 => CoapOption::IfMatch,
55            3 => CoapOption::UriHost,
56            4 => CoapOption::ETag,
57            5 => CoapOption::IfNoneMatch,
58            6 => CoapOption::Observe,
59            7 => CoapOption::UriPort,
60            8 => CoapOption::LocationPath,
61            9 => CoapOption::Oscore,
62            11 => CoapOption::UriPath,
63            12 => CoapOption::ContentFormat,
64            14 => CoapOption::MaxAge,
65            15 => CoapOption::UriQuery,
66            17 => CoapOption::Accept,
67            20 => CoapOption::LocationQuery,
68            23 => CoapOption::Block2,
69            27 => CoapOption::Block1,
70            35 => CoapOption::ProxyUri,
71            39 => CoapOption::ProxyScheme,
72            60 => CoapOption::Size1,
73            28 => CoapOption::Size2,
74            258 => CoapOption::NoResponse,
75            _ => CoapOption::Unknown(number),
76        }
77    }
78}
79
80impl From<CoapOption> for u16 {
81    fn from(option: CoapOption) -> u16 {
82        match option {
83            CoapOption::IfMatch => 1,
84            CoapOption::UriHost => 3,
85            CoapOption::ETag => 4,
86            CoapOption::IfNoneMatch => 5,
87            CoapOption::Observe => 6,
88            CoapOption::UriPort => 7,
89            CoapOption::LocationPath => 8,
90            CoapOption::Oscore => 9,
91            CoapOption::UriPath => 11,
92            CoapOption::ContentFormat => 12,
93            CoapOption::MaxAge => 14,
94            CoapOption::UriQuery => 15,
95            CoapOption::Accept => 17,
96            CoapOption::LocationQuery => 20,
97            CoapOption::Block2 => 23,
98            CoapOption::Block1 => 27,
99            CoapOption::ProxyUri => 35,
100            CoapOption::ProxyScheme => 39,
101            CoapOption::Size1 => 60,
102            CoapOption::Size2 => 28,
103            CoapOption::NoResponse => 258,
104            CoapOption::Unknown(number) => number,
105        }
106    }
107}
108
109/// The content formats.
110#[derive(Debug, Clone, Copy, PartialEq)]
111#[non_exhaustive]
112pub enum ContentFormat {
113    TextPlain,
114    /// Media-Type: `application/cose; cose-type="cose-encrypt0"`, ID: 16
115    ApplicationCoseEncrypt0,
116    /// Media-Type: `application/cose; cose-type="cose-mac0"`, ID: 17
117    ApplicationCoseMac0,
118    /// Media-Type: `application/cose; cose-type="cose-sign1"`, ID: 18
119    ApplicationCoseSign1,
120    ApplicationAceCbor,
121    ImageGif,
122    ImageJpeg,
123    ImagePng,
124    ApplicationLinkFormat,
125    ApplicationXML,
126    ApplicationOctetStream,
127    ApplicationEXI,
128    ApplicationJSON,
129    ApplicationJsonPatchJson,
130    ApplicationMergePatchJson,
131    ApplicationCBOR,
132    ApplicationCWt,
133    ApplicationMultipartCore,
134    ApplicationCborSeq,
135    /// Media-Type: `application/cose; cose-type="cose-encrypt"`, ID: 96
136    ApplicationCoseEncrypt,
137    /// Media-Type: `application/cose; cose-type="cose-mac"`, ID: 97
138    ApplicationCoseMac,
139    /// Media-Type: `application/cose; cose-type="cose-sign"`, ID: 98
140    ApplicationCoseSign,
141    ApplicationCoseKey,
142    ApplicationCoseKeySet,
143    ApplicationSenmlJSON,
144    ApplicationSensmlJSON,
145    ApplicationSenmlCBOR,
146    ApplicationSensmlCBOR,
147    ApplicationSenmlExi,
148    ApplicationSensmlExi,
149    /// Media-Type: `application/yang-data+cbor; id=sid`, ID: 140
150    ApplicationYangDataCborSid,
151    ApplicationCoapGroupJson,
152    ApplicationDotsCbor,
153    ApplicationMissingBlocksCborSeq,
154    /// Media-Type: `application/pkcs7-mime; smime-type=server-generated-key`, ID: 280
155    ApplicationPkcs7MimeServerGeneratedKey,
156    /// Media-Type: `application/pkcs7-mime; smime-type=certs-only`, ID: 281
157    ApplicationPkcs7MimeCertsOnly,
158    ApplicationPkcs8,
159    ApplicationCsrattrs,
160    ApplicationPkcs10,
161    ApplicationPkixCert,
162    ApplicationAifCbor,
163    ApplicationAifJson,
164    ApplicationSenmlXML,
165    ApplicationSensmlXML,
166    ApplicationSenmlEtchJson,
167    ApplicationSenmlEtchCbor,
168    ApplicationYangDataCbor,
169    /// Media-Type: `application/yang-data+cbor; id=name`, ID: 341
170    ApplicationYangDataCborName,
171    ApplicationTdJson,
172    ApplicationVoucherCoseCbor,
173    ApplicationVndOcfCbor,
174    ApplicationOscore,
175    ApplicationJavascript,
176    ApplicationJsonDeflate,
177    ApplicationCborDeflate,
178    ApplicationVndOmaLwm2mTlv,
179    ApplicationVndOmaLwm2mJson,
180    ApplicationVndOmaLwm2mCbor,
181    TextCss,
182    ImageSvgXml,
183}
184
185impl TryFrom<usize> for ContentFormat {
186    type Error = InvalidContentFormat;
187
188    fn try_from(number: usize) -> Result<ContentFormat, InvalidContentFormat> {
189        match number {
190            0 => Ok(ContentFormat::TextPlain),
191            16 => Ok(ContentFormat::ApplicationCoseEncrypt0),
192            17 => Ok(ContentFormat::ApplicationCoseMac0),
193            18 => Ok(ContentFormat::ApplicationCoseSign1),
194            19 => Ok(ContentFormat::ApplicationAceCbor),
195            21 => Ok(ContentFormat::ImageGif),
196            22 => Ok(ContentFormat::ImageJpeg),
197            23 => Ok(ContentFormat::ImagePng),
198            40 => Ok(ContentFormat::ApplicationLinkFormat),
199            41 => Ok(ContentFormat::ApplicationXML),
200            42 => Ok(ContentFormat::ApplicationOctetStream),
201            47 => Ok(ContentFormat::ApplicationEXI),
202            50 => Ok(ContentFormat::ApplicationJSON),
203            51 => Ok(ContentFormat::ApplicationJsonPatchJson),
204            52 => Ok(ContentFormat::ApplicationMergePatchJson),
205            60 => Ok(ContentFormat::ApplicationCBOR),
206            61 => Ok(ContentFormat::ApplicationCWt),
207            62 => Ok(ContentFormat::ApplicationMultipartCore),
208            63 => Ok(ContentFormat::ApplicationCborSeq),
209            96 => Ok(ContentFormat::ApplicationCoseEncrypt),
210            97 => Ok(ContentFormat::ApplicationCoseMac),
211            98 => Ok(ContentFormat::ApplicationCoseSign),
212            101 => Ok(ContentFormat::ApplicationCoseKey),
213            102 => Ok(ContentFormat::ApplicationCoseKeySet),
214            110 => Ok(ContentFormat::ApplicationSenmlJSON),
215            111 => Ok(ContentFormat::ApplicationSensmlJSON),
216            112 => Ok(ContentFormat::ApplicationSenmlCBOR),
217            113 => Ok(ContentFormat::ApplicationSensmlCBOR),
218            114 => Ok(ContentFormat::ApplicationSenmlExi),
219            115 => Ok(ContentFormat::ApplicationSensmlExi),
220            140 => Ok(ContentFormat::ApplicationYangDataCborSid),
221            256 => Ok(ContentFormat::ApplicationCoapGroupJson),
222            271 => Ok(ContentFormat::ApplicationDotsCbor),
223            272 => Ok(ContentFormat::ApplicationMissingBlocksCborSeq),
224            280 => Ok(ContentFormat::ApplicationPkcs7MimeServerGeneratedKey),
225            281 => Ok(ContentFormat::ApplicationPkcs7MimeCertsOnly),
226            284 => Ok(ContentFormat::ApplicationPkcs8),
227            285 => Ok(ContentFormat::ApplicationCsrattrs),
228            286 => Ok(ContentFormat::ApplicationPkcs10),
229            287 => Ok(ContentFormat::ApplicationPkixCert),
230            290 => Ok(ContentFormat::ApplicationAifCbor),
231            291 => Ok(ContentFormat::ApplicationAifJson),
232            310 => Ok(ContentFormat::ApplicationSenmlXML),
233            311 => Ok(ContentFormat::ApplicationSensmlXML),
234            320 => Ok(ContentFormat::ApplicationSenmlEtchJson),
235            322 => Ok(ContentFormat::ApplicationSenmlEtchCbor),
236            340 => Ok(ContentFormat::ApplicationYangDataCbor),
237            341 => Ok(ContentFormat::ApplicationYangDataCborName),
238            432 => Ok(ContentFormat::ApplicationTdJson),
239            836 => Ok(ContentFormat::ApplicationVoucherCoseCbor),
240            10000 => Ok(ContentFormat::ApplicationVndOcfCbor),
241            10001 => Ok(ContentFormat::ApplicationOscore),
242            10002 => Ok(ContentFormat::ApplicationJavascript),
243            11050 => Ok(ContentFormat::ApplicationJsonDeflate),
244            11060 => Ok(ContentFormat::ApplicationCborDeflate),
245            11542 => Ok(ContentFormat::ApplicationVndOmaLwm2mTlv),
246            11543 => Ok(ContentFormat::ApplicationVndOmaLwm2mJson),
247            11544 => Ok(ContentFormat::ApplicationVndOmaLwm2mCbor),
248            20000 => Ok(ContentFormat::TextCss),
249            30000 => Ok(ContentFormat::ImageSvgXml),
250            _ => Err(InvalidContentFormat),
251        }
252    }
253}
254
255impl From<ContentFormat> for usize {
256    fn from(format: ContentFormat) -> usize {
257        match format {
258            ContentFormat::TextPlain => 0,
259            ContentFormat::ApplicationCoseEncrypt0 => 16,
260            ContentFormat::ApplicationCoseMac0 => 17,
261            ContentFormat::ApplicationCoseSign1 => 18,
262            ContentFormat::ApplicationAceCbor => 19,
263            ContentFormat::ImageGif => 21,
264            ContentFormat::ImageJpeg => 22,
265            ContentFormat::ImagePng => 23,
266            ContentFormat::ApplicationLinkFormat => 40,
267            ContentFormat::ApplicationXML => 41,
268            ContentFormat::ApplicationOctetStream => 42,
269            ContentFormat::ApplicationEXI => 47,
270            ContentFormat::ApplicationJSON => 50,
271            ContentFormat::ApplicationJsonPatchJson => 51,
272            ContentFormat::ApplicationMergePatchJson => 52,
273            ContentFormat::ApplicationCBOR => 60,
274            ContentFormat::ApplicationCWt => 61,
275            ContentFormat::ApplicationMultipartCore => 62,
276            ContentFormat::ApplicationCborSeq => 63,
277            ContentFormat::ApplicationCoseEncrypt => 96,
278            ContentFormat::ApplicationCoseMac => 97,
279            ContentFormat::ApplicationCoseSign => 98,
280            ContentFormat::ApplicationCoseKey => 101,
281            ContentFormat::ApplicationCoseKeySet => 102,
282            ContentFormat::ApplicationSenmlJSON => 110,
283            ContentFormat::ApplicationSensmlJSON => 111,
284            ContentFormat::ApplicationSenmlCBOR => 112,
285            ContentFormat::ApplicationSensmlCBOR => 113,
286            ContentFormat::ApplicationSenmlExi => 114,
287            ContentFormat::ApplicationSensmlExi => 115,
288            ContentFormat::ApplicationYangDataCborSid => 140,
289            ContentFormat::ApplicationCoapGroupJson => 256,
290            ContentFormat::ApplicationDotsCbor => 271,
291            ContentFormat::ApplicationMissingBlocksCborSeq => 272,
292            ContentFormat::ApplicationPkcs7MimeServerGeneratedKey => 280,
293            ContentFormat::ApplicationPkcs7MimeCertsOnly => 281,
294            ContentFormat::ApplicationPkcs8 => 284,
295            ContentFormat::ApplicationCsrattrs => 285,
296            ContentFormat::ApplicationPkcs10 => 286,
297            ContentFormat::ApplicationPkixCert => 287,
298            ContentFormat::ApplicationAifCbor => 290,
299            ContentFormat::ApplicationAifJson => 291,
300            ContentFormat::ApplicationSenmlXML => 310,
301            ContentFormat::ApplicationSensmlXML => 311,
302            ContentFormat::ApplicationSenmlEtchJson => 320,
303            ContentFormat::ApplicationSenmlEtchCbor => 322,
304            ContentFormat::ApplicationYangDataCbor => 340,
305            ContentFormat::ApplicationYangDataCborName => 341,
306            ContentFormat::ApplicationTdJson => 432,
307            ContentFormat::ApplicationVoucherCoseCbor => 836,
308            ContentFormat::ApplicationVndOcfCbor => 10000,
309            ContentFormat::ApplicationOscore => 10001,
310            ContentFormat::ApplicationJavascript => 10002,
311            ContentFormat::ApplicationJsonDeflate => 11050,
312            ContentFormat::ApplicationCborDeflate => 11060,
313            ContentFormat::ApplicationVndOmaLwm2mTlv => 11542,
314            ContentFormat::ApplicationVndOmaLwm2mJson => 11543,
315            ContentFormat::ApplicationVndOmaLwm2mCbor => 11544,
316            ContentFormat::TextCss => 20000,
317            ContentFormat::ImageSvgXml => 30000,
318        }
319    }
320}
321
322/// The values of the observe option.
323#[derive(Debug, Clone, Copy, PartialEq)]
324pub enum ObserveOption {
325    Register,
326    Deregister,
327}
328
329impl TryFrom<usize> for ObserveOption {
330    type Error = InvalidObserve;
331
332    fn try_from(number: usize) -> Result<ObserveOption, InvalidObserve> {
333        match number {
334            0 => Ok(ObserveOption::Register),
335            1 => Ok(ObserveOption::Deregister),
336            _ => Err(InvalidObserve),
337        }
338    }
339}
340
341impl From<ObserveOption> for usize {
342    fn from(observe: ObserveOption) -> usize {
343        match observe {
344            ObserveOption::Register => 0,
345            ObserveOption::Deregister => 1,
346        }
347    }
348}
349
350/// The CoAP packet.
351#[derive(Debug, Clone, Default, PartialEq)]
352pub struct Packet {
353    pub header: Header,
354    token: Vec<u8>,
355    pub(crate) options: BTreeMap<u16, LinkedList<Vec<u8>>>,
356    pub payload: Vec<u8>,
357}
358
359/// An iterator over the options of a packet.
360pub type Options<'a> =
361    alloc::collections::btree_map::Iter<'a, u16, LinkedList<Vec<u8>>>;
362
363impl Packet {
364    /// Maximum allowed packet size. By default limited to 1280 so that CoAP
365    /// packets can be sent over TCP or UDP.
366    #[cfg(not(feature = "udp"))]
367    pub const MAX_SIZE: usize = 1280;
368
369    /// Maximum allowed packet size.
370    #[cfg(feature = "udp")]
371    pub const MAX_SIZE: usize = 64_000;
372
373    /// Creates a new packet.
374    pub fn new() -> Packet {
375        Default::default()
376    }
377
378    /// Returns an iterator over the options of the packet.
379    pub fn options(&self) -> Options {
380        self.options.iter()
381    }
382
383    /// Sets the token.
384    pub fn set_token(&mut self, token: Vec<u8>) {
385        self.header.set_token_length(token.len() as u8);
386        self.token = token;
387    }
388
389    /// Returns the token.
390    pub fn get_token(&self) -> &[u8] {
391        &self.token
392    }
393
394    /// Sets an option's values.
395    pub fn set_option(&mut self, tp: CoapOption, value: LinkedList<Vec<u8>>) {
396        self.options.insert(tp.into(), value);
397    }
398
399    /// Sets an option's values using a structured option value format.
400    pub fn set_options_as<T: OptionValueType>(
401        &mut self,
402        tp: CoapOption,
403        value: LinkedList<T>,
404    ) {
405        let raw_value = value.into_iter().map(|x| x.into()).collect();
406        self.set_option(tp, raw_value);
407    }
408
409    /// Returns an option's values.
410    pub fn get_option(&self, tp: CoapOption) -> Option<&LinkedList<Vec<u8>>> {
411        self.options.get(&tp.into())
412    }
413
414    /// Returns an option's values all decoded using the specified structured
415    /// option value format.
416    pub fn get_options_as<T: OptionValueType>(
417        &self,
418        tp: CoapOption,
419    ) -> Option<LinkedList<Result<T, IncompatibleOptionValueFormat>>> {
420        self.get_option(tp).map(|options| {
421            options
422                .iter()
423                .map(|raw_value| T::try_from(raw_value.clone()))
424                .collect()
425        })
426    }
427
428    /// Returns an option's first value as a convenience when only one is
429    /// expected.
430    pub fn get_first_option(&self, tp: CoapOption) -> Option<&Vec<u8>> {
431        self.options
432            .get(&tp.into())
433            .and_then(|options| options.front())
434    }
435
436    /// Returns an option's first value as a convenience when only one is
437    /// expected.
438    pub fn get_first_option_as<T: OptionValueType>(
439        &self,
440        tp: CoapOption,
441    ) -> Option<Result<T, IncompatibleOptionValueFormat>> {
442        self.get_first_option(tp)
443            .map(|value| T::try_from(value.clone()))
444    }
445
446    /// Adds an option value.
447    pub fn add_option(&mut self, tp: CoapOption, value: Vec<u8>) {
448        let num = tp.into();
449        if let Some(list) = self.options.get_mut(&num) {
450            list.push_back(value);
451            return;
452        }
453
454        let mut list = LinkedList::new();
455        list.push_back(value);
456        self.options.insert(num, list);
457    }
458
459    /// Adds an option value using a structured option value format.
460    pub fn add_option_as<T: OptionValueType>(
461        &mut self,
462        tp: CoapOption,
463        value: T,
464    ) {
465        self.add_option(tp, value.into());
466    }
467
468    /// Removes an option.
469    pub fn clear_option(&mut self, tp: CoapOption) {
470        if let Some(list) = self.options.get_mut(&tp.into()) {
471            list.clear()
472        }
473    }
474
475    /// Removes all options.
476    pub fn clear_all_options(&mut self) {
477        self.options.clear()
478    }
479
480    /// Sets the content-format.
481    pub fn set_content_format(&mut self, cf: ContentFormat) {
482        let content_format: u16 = u16::try_from(usize::from(cf)).unwrap();
483        self.add_option_as(
484            CoapOption::ContentFormat,
485            OptionValueU16(content_format),
486        );
487    }
488
489    /// Returns the content-format.
490    pub fn get_content_format(&self) -> Option<ContentFormat> {
491        self.get_first_option_as::<OptionValueU16>(CoapOption::ContentFormat)
492            .and_then(|option| option.ok())
493            .map(|value| usize::from(value.0))
494            .and_then(|value| ContentFormat::try_from(value).ok())
495    }
496
497    /// Sets the value of the observe option.
498    pub fn set_observe_value(&mut self, value: u32) {
499        self.clear_option(CoapOption::Observe);
500        self.add_option_as(CoapOption::Observe, OptionValueU32(value));
501    }
502
503    /// Returns the value of the observe option.
504    pub fn get_observe_value(
505        &self,
506    ) -> Option<Result<u32, IncompatibleOptionValueFormat>> {
507        self.get_first_option_as::<OptionValueU32>(CoapOption::Observe)
508            .map(|option| option.map(|value| value.0))
509    }
510
511    /// Decodes a byte slice and constructs the equivalent packet.
512    pub fn from_bytes(buf: &[u8]) -> Result<Packet, MessageError> {
513        let header_result = HeaderRaw::try_from(buf);
514        match header_result {
515            Ok(raw_header) => {
516                let header = Header::from_raw(&raw_header);
517                let token_length = header.get_token_length();
518                let options_start: usize = 4 + token_length as usize;
519
520                if token_length > 8 {
521                    return Err(MessageError::InvalidTokenLength);
522                }
523
524                if options_start > buf.len() {
525                    return Err(MessageError::InvalidTokenLength);
526                }
527
528                let token = buf[4..options_start].to_vec();
529
530                let mut idx = options_start;
531                let mut options_number = 0u16;
532                let mut options: BTreeMap<u16, LinkedList<Vec<u8>>> =
533                    BTreeMap::new();
534                while idx < buf.len() {
535                    let byte = buf[idx];
536
537                    if byte == 255 || idx > buf.len() {
538                        break;
539                    }
540
541                    let mut delta = (byte >> 4) as u16;
542                    let mut length = (byte & 0xF) as usize;
543
544                    idx += 1;
545
546                    // Check for special delta characters
547                    match delta {
548                        13 => {
549                            if idx >= buf.len() {
550                                return Err(MessageError::InvalidOptionLength);
551                            }
552                            delta = buf[idx] as u16 + 13;
553                            idx += 1;
554                        }
555                        14 => {
556                            if idx + 1 >= buf.len() {
557                                return Err(MessageError::InvalidOptionLength);
558                            }
559
560                            delta = u16::from_be(u8_to_unsigned_be!(
561                                buf,
562                                idx,
563                                idx + 1,
564                                u16
565                            ))
566                            .checked_add(269)
567                            .ok_or(MessageError::InvalidOptionDelta)?;
568                            idx += 2;
569                        }
570                        15 => {
571                            return Err(MessageError::InvalidOptionDelta);
572                        }
573                        _ => {}
574                    };
575
576                    // Check for special length characters
577                    match length {
578                        13 => {
579                            if idx >= buf.len() {
580                                return Err(MessageError::InvalidOptionLength);
581                            }
582
583                            length = buf[idx] as usize + 13;
584                            idx += 1;
585                        }
586                        14 => {
587                            if idx + 1 >= buf.len() {
588                                return Err(MessageError::InvalidOptionLength);
589                            }
590
591                            length = (u16::from_be(u8_to_unsigned_be!(
592                                buf,
593                                idx,
594                                idx + 1,
595                                u16
596                            ))
597                            .checked_add(269)
598                            .ok_or(MessageError::InvalidOptionLength)?)
599                            .into();
600                            idx += 2;
601                        }
602                        15 => {
603                            return Err(MessageError::InvalidOptionLength);
604                        }
605                        _ => {}
606                    };
607
608                    options_number = options_number
609                        .checked_add(delta)
610                        .ok_or(MessageError::InvalidOptionDelta)?;
611
612                    let end = idx + length;
613                    if end > buf.len() {
614                        return Err(MessageError::InvalidOptionLength);
615                    }
616                    let options_value = buf[idx..end].to_vec();
617
618                    options
619                        .entry(options_number)
620                        .or_default()
621                        .push_back(options_value);
622
623                    idx += length;
624                }
625
626                let payload = if idx < buf.len() {
627                    buf[(idx + 1)..buf.len()].to_vec()
628                } else {
629                    Vec::new()
630                };
631
632                Ok(Packet {
633                    header,
634                    token,
635                    options,
636                    payload,
637                })
638            }
639            Err(_) => Err(MessageError::InvalidHeader),
640        }
641    }
642
643    /// Returns a vector of bytes representing the Packet.
644    pub fn to_bytes(&self) -> Result<Vec<u8>, MessageError> {
645        self.to_bytes_internal(Some(Self::MAX_SIZE))
646    }
647
648    /// Returns a vector of bytes representing the Packet, using a custom
649    /// `limit` instead of [`Packet::MAX_SIZE`] for the message size check.
650    pub fn to_bytes_with_limit(
651        &self,
652        limit: usize,
653    ) -> Result<Vec<u8>, MessageError> {
654        self.to_bytes_internal(Some(limit))
655    }
656
657    /// Returns a vector of bytes representing the Packet, skipping the message
658    /// size check against [`Packet::MAX_SIZE`].
659    pub fn to_bytes_unlimited(&self) -> Result<Vec<u8>, MessageError> {
660        self.to_bytes_internal(None)
661    }
662
663    fn to_bytes_internal(
664        &self,
665        limit: Option<usize>,
666    ) -> Result<Vec<u8>, MessageError> {
667        let mut options_delta_length = 0;
668        let mut options_bytes: Vec<u8> = Vec::new();
669        for (number, value_list) in self.options.iter() {
670            for value in value_list.iter() {
671                let mut header: Vec<u8> = Vec::with_capacity(1 + 2 + 2);
672                let delta = number - options_delta_length;
673
674                let mut byte: u8 = 0;
675                if delta <= 12 {
676                    byte |= (delta << 4) as u8;
677                } else if delta < 269 {
678                    byte |= 13 << 4;
679                } else {
680                    byte |= 14 << 4;
681                }
682                if value.len() <= 12 {
683                    byte |= value.len() as u8;
684                } else if value.len() < 269 {
685                    byte |= 13;
686                } else {
687                    byte |= 14;
688                }
689                header.push(byte);
690
691                if delta > 12 && delta < 269 {
692                    header.push((delta - 13) as u8);
693                } else if delta >= 269 {
694                    let fix = delta - 269;
695                    header.push((fix >> 8) as u8);
696                    header.push((fix & 0xFF) as u8);
697                }
698
699                if value.len() > 12 && value.len() < 269 {
700                    header.push((value.len() - 13) as u8);
701                } else if value.len() >= 269 {
702                    let fix = (value.len() - 269) as u16;
703                    header.push((fix >> 8) as u8);
704                    header.push((fix & 0xFF) as u8);
705                }
706
707                options_delta_length += delta;
708
709                options_bytes.reserve(header.len() + value.len());
710                unsafe {
711                    use core::ptr;
712                    let buf_len = options_bytes.len();
713                    ptr::copy(
714                        header.as_ptr(),
715                        options_bytes.as_mut_ptr().add(buf_len),
716                        header.len(),
717                    );
718                    ptr::copy(
719                        value.as_ptr(),
720                        options_bytes.as_mut_ptr().add(buf_len + header.len()),
721                        value.len(),
722                    );
723                    options_bytes
724                        .set_len(buf_len + header.len() + value.len());
725                }
726            }
727        }
728
729        let mut buf_length = 4 + self.payload.len() + self.token.len();
730        if self.header.code != MessageClass::Empty && !self.payload.is_empty()
731        {
732            buf_length += 1;
733        }
734        buf_length += options_bytes.len();
735
736        if limit.is_some() && buf_length > limit.unwrap() {
737            return Err(MessageError::InvalidPacketLength);
738        }
739
740        let mut buf: Vec<u8> = Vec::with_capacity(buf_length);
741        let header_result = self.header.to_raw().serialize_into(&mut buf);
742
743        match header_result {
744            Ok(_) => {
745                buf.reserve(self.token.len() + options_bytes.len());
746                unsafe {
747                    use core::ptr;
748                    let buf_len = buf.len();
749                    ptr::copy(
750                        self.token.as_ptr(),
751                        buf.as_mut_ptr().add(buf_len),
752                        self.token.len(),
753                    );
754                    ptr::copy(
755                        options_bytes.as_ptr(),
756                        buf.as_mut_ptr().add(buf_len + self.token.len()),
757                        options_bytes.len(),
758                    );
759                    buf.set_len(
760                        buf_len + self.token.len() + options_bytes.len(),
761                    );
762                }
763
764                if self.header.code != MessageClass::Empty
765                    && !self.payload.is_empty()
766                {
767                    buf.push(0xFF);
768                    buf.reserve(self.payload.len());
769                    unsafe {
770                        use core::ptr;
771                        let buf_len = buf.len();
772                        ptr::copy(
773                            self.payload.as_ptr(),
774                            buf.as_mut_ptr().add(buf.len()),
775                            self.payload.len(),
776                        );
777                        buf.set_len(buf_len + self.payload.len());
778                    }
779                }
780                Ok(buf)
781            }
782            Err(_) => Err(MessageError::InvalidHeader),
783        }
784    }
785}
786
787#[cfg(test)]
788mod test {
789    use super::*;
790    use crate::{header, option_value::OptionValueString};
791    use alloc::borrow::ToOwned;
792
793    #[test]
794    fn test_decode_packet_with_options() {
795        let buf = [
796            0x44, 0x01, 0x84, 0x9e, 0x51, 0x55, 0x77, 0xe8, 0xb2, 0x48, 0x69,
797            0x04, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x3d, 0x31,
798        ];
799        let packet = Packet::from_bytes(&buf);
800        assert!(packet.is_ok());
801        let packet = packet.unwrap();
802        assert_eq!(packet.header.get_version(), 1);
803        assert_eq!(packet.header.get_type(), header::MessageType::Confirmable);
804        assert_eq!(packet.header.get_token_length(), 4);
805        assert_eq!(
806            packet.header.code,
807            header::MessageClass::Request(header::RequestType::Get)
808        );
809        assert_eq!(packet.header.message_id, 33950);
810        assert_eq!(*packet.get_token(), vec![0x51, 0x55, 0x77, 0xE8]);
811        assert_eq!(packet.options.len(), 2);
812
813        let uri_path = packet.get_option(CoapOption::UriPath);
814        assert!(uri_path.is_some());
815        let uri_path = uri_path.unwrap();
816        let mut expected_uri_path = LinkedList::new();
817        expected_uri_path.push_back("Hi".as_bytes().to_vec());
818        expected_uri_path.push_back("Test".as_bytes().to_vec());
819        assert_eq!(*uri_path, expected_uri_path);
820
821        let uri_query = packet.get_option(CoapOption::UriQuery);
822        assert!(uri_query.is_some());
823        let uri_query = uri_query.unwrap();
824        let mut expected_uri_query = LinkedList::new();
825        expected_uri_query.push_back("a=1".as_bytes().to_vec());
826        assert_eq!(*uri_query, expected_uri_query);
827    }
828
829    #[test]
830    fn test_decode_packet_with_payload() {
831        let buf = [
832            0x64, 0x45, 0x13, 0xFD, 0xD0, 0xE2, 0x4D, 0xAC, 0xFF, 0x48, 0x65,
833            0x6C, 0x6C, 0x6F,
834        ];
835        let packet = Packet::from_bytes(&buf);
836        assert!(packet.is_ok());
837        let packet = packet.unwrap();
838        assert_eq!(packet.header.get_version(), 1);
839        assert_eq!(
840            packet.header.get_type(),
841            header::MessageType::Acknowledgement
842        );
843        assert_eq!(packet.header.get_token_length(), 4);
844        assert_eq!(
845            packet.header.code,
846            header::MessageClass::Response(header::ResponseType::Content)
847        );
848        assert_eq!(packet.header.message_id, 5117);
849        assert_eq!(*packet.get_token(), vec![0xD0, 0xE2, 0x4D, 0xAC]);
850        assert_eq!(packet.payload, "Hello".as_bytes().to_vec());
851    }
852
853    #[test]
854    fn test_encode_packet_with_options() {
855        let mut packet = Packet::new();
856        packet.header.set_version(1);
857        packet.header.set_type(header::MessageType::Confirmable);
858        packet.header.code =
859            header::MessageClass::Request(header::RequestType::Get);
860        packet.header.message_id = 33950;
861        packet.set_token(vec![0x51, 0x55, 0x77, 0xE8]);
862        packet.add_option(CoapOption::UriPath, b"Hi".to_vec());
863        packet.add_option(CoapOption::UriPath, b"Test".to_vec());
864        packet.add_option(CoapOption::UriQuery, b"a=1".to_vec());
865        assert_eq!(
866            packet.to_bytes().unwrap(),
867            vec![
868                0x44, 0x01, 0x84, 0x9e, 0x51, 0x55, 0x77, 0xe8, 0xb2, 0x48,
869                0x69, 0x04, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x3d, 0x31
870            ]
871        );
872    }
873
874    #[test]
875    fn test_encode_packet_with_payload() {
876        let mut packet = Packet::new();
877        packet.header.set_version(1);
878        packet.header.set_type(header::MessageType::Acknowledgement);
879        packet.header.code =
880            header::MessageClass::Response(header::ResponseType::Content);
881        packet.header.message_id = 5117;
882        packet.set_token(vec![0xD0, 0xE2, 0x4D, 0xAC]);
883        packet.payload = "Hello".as_bytes().to_vec();
884        assert_eq!(
885            packet.to_bytes().unwrap(),
886            vec![
887                0x64, 0x45, 0x13, 0xFD, 0xD0, 0xE2, 0x4D, 0xAC, 0xFF, 0x48,
888                0x65, 0x6C, 0x6C, 0x6F
889            ]
890        );
891    }
892
893    #[test]
894    fn test_encode_decode_content_format() {
895        let mut packet = Packet::new();
896        packet.set_content_format(ContentFormat::TextPlain);
897        assert_eq!(
898            ContentFormat::TextPlain,
899            packet.get_content_format().unwrap()
900        )
901    }
902
903    #[test]
904    fn test_encode_decode_content_format_without_msb() {
905        let mut packet = Packet::new();
906        packet.set_content_format(ContentFormat::ApplicationJSON);
907        assert_eq!(
908            ContentFormat::ApplicationJSON,
909            packet.get_content_format().unwrap()
910        )
911    }
912
913    #[test]
914    fn test_encode_decode_content_format_with_msb() {
915        let mut packet = Packet::new();
916        packet.set_content_format(ContentFormat::ApplicationSensmlXML);
917        assert_eq!(
918            ContentFormat::ApplicationSensmlXML,
919            packet.get_content_format().unwrap()
920        )
921    }
922
923    #[test]
924    fn test_decode_empty_content_format() {
925        let packet = Packet::new();
926        assert!(packet.get_content_format().is_none());
927    }
928
929    #[test]
930    fn option() {
931        for i in 0..512 {
932            assert_eq!(i, CoapOption::from(i).into());
933        }
934    }
935
936    #[test]
937    fn content_format() {
938        for i in 0..512 {
939            if let Ok(o) = ContentFormat::try_from(i) {
940                assert_eq!(i, o.into());
941            }
942        }
943    }
944
945    #[test]
946    fn observe_option() {
947        for i in 0..8 {
948            if let Ok(o) = ObserveOption::try_from(i) {
949                assert_eq!(i, o.into());
950            }
951        }
952    }
953
954    #[test]
955    fn options() {
956        let mut p = Packet::new();
957        p.add_option(CoapOption::UriHost, vec![0]);
958        p.add_option(CoapOption::UriPath, vec![1]);
959        p.add_option(CoapOption::ETag, vec![2]);
960        p.clear_option(CoapOption::ETag);
961        assert_eq!(3, p.options().len());
962
963        let bytes = p.to_bytes().unwrap();
964        let mut pp = Packet::from_bytes(&bytes).unwrap();
965        assert_eq!(2, pp.options().len());
966
967        let mut values = LinkedList::new();
968        values.push_back(vec![3]);
969        values.push_back(vec![4]);
970        pp.set_option(CoapOption::Oscore, values);
971        assert_eq!(3, pp.options().len());
972    }
973
974    #[test]
975    fn test_option_u32_format() {
976        let mut p = Packet::new();
977        let option_key = CoapOption::Observe;
978        let values = vec![0, 100, 1000, 10000, u32::MAX];
979        for &value in &values {
980            p.add_option_as(option_key, OptionValueU32(value));
981        }
982        let expected = values.iter().map(|&x| Ok(OptionValueU32(x))).collect();
983        let actual = p.get_options_as::<OptionValueU32>(option_key);
984        assert_eq!(actual, Some(expected));
985    }
986
987    #[test]
988    fn test_option_utf8_format() {
989        let mut p = Packet::new();
990        let option_key = CoapOption::UriPath;
991        let values = vec!["", "simple", "unicode 😁 stuff"];
992        for &value in &values {
993            p.add_option_as(option_key, OptionValueString(value.to_owned()));
994        }
995        let expected = values
996            .iter()
997            .map(|&x| Ok(OptionValueString(x.to_owned())))
998            .collect();
999        let actual = p.get_options_as::<OptionValueString>(option_key);
1000        assert_eq!(actual, Some(expected));
1001    }
1002
1003    #[test]
1004    fn observe() {
1005        let mut p = Packet::new();
1006        assert_eq!(None, p.get_observe_value());
1007        p.set_observe_value(0);
1008        assert_eq!(Some(Ok(0)), p.get_observe_value());
1009    }
1010
1011    #[test]
1012    fn to_bytes_limits_work() {
1013        let mut packet = Packet::new();
1014
1015        packet.payload = vec![0u8; 1200];
1016        assert!(packet.to_bytes().is_ok());
1017
1018        packet.payload = vec![0u8; 1300];
1019        assert_eq!(packet.to_bytes(), Err(MessageError::InvalidPacketLength));
1020        assert!(packet.to_bytes_with_limit(1380).is_ok());
1021        assert!(packet.to_bytes_unlimited().is_ok());
1022    }
1023
1024    #[test]
1025    fn option_delta_u8_overflow() {
1026        // Build a packet with options 1 and 258, which have a delta of 257.
1027        // Although 257 does not fit into a u8, it does fit into the
1028        // 1-byte extended option delta, because that is biased by 13.
1029        //
1030        // coap_lite 0.13.1 and earlier decoded this delta incorrectly.
1031        let mut input = Packet::new();
1032        let option_1 = CoapOption::IfMatch;
1033        let option_258 = CoapOption::NoResponse;
1034
1035        input.add_option(option_1, vec![0]);
1036        input.add_option(option_258, vec![1]);
1037        let bytes = input.to_bytes().unwrap();
1038
1039        // Verify everything round-trips
1040        let output = Packet::from_bytes(&bytes).unwrap();
1041        assert_eq!(output.options().len(), 2);
1042        assert_eq!(output.get_first_option(option_1), Some(vec![0]).as_ref());
1043        assert_eq!(
1044            output.get_first_option(option_258),
1045            Some(vec![1]).as_ref()
1046        );
1047    }
1048
1049    #[test]
1050    fn reject_excessive_option_delta() {
1051        // The 2-byte extended option delta field is biased by 269, and
1052        // therefore can represent values as high as 0x1_010C. Deltas
1053        // larger than 0x0_FFFF are illegal and should be rejected.
1054        //
1055        // coap_lite 0.13.1 and earlier did not reject this input and
1056        // instead presented an incorrect set of options.
1057        let bytes = [
1058            // header
1059            0x40, 0x01, 0x00, 0x00,
1060            // option delta = 0x1_0000, option length = 0
1061            0xe0, 0xfe, 0xf3,
1062        ];
1063
1064        let result = Packet::from_bytes(&bytes);
1065        assert_eq!(result, Err(MessageError::InvalidOptionDelta));
1066    }
1067
1068    #[test]
1069    fn reject_excessive_option_number() {
1070        // It's possible to arrive at an option number > 0xFFFF by
1071        // adding deltas. Option numbers > 0xFFFF are illegal and
1072        // should be rejected.
1073        //
1074        // coap_lite 0.13.1 and earlier did not reject this input and
1075        // instead presented an incorrect set of options.
1076        let bytes = [
1077            // header
1078            0x40, 0x01, 0x00, 0x00,
1079            // option delta = 0xFFFF, option length = 0
1080            0xe0, 0xfe, 0xf2,
1081            // option delta = 0x01, option length = 0
1082            0x10,
1083        ];
1084
1085        let result = Packet::from_bytes(&bytes);
1086        assert_eq!(result, Err(MessageError::InvalidOptionDelta));
1087    }
1088}