1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * message.rs - Types related to CoAP messages.
4
 * This file is part of the libcoap-rs crate, see the README and LICENSE files for
5
 * more information and terms of use.
6
 * Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved.
7
 * See the README as well as the LICENSE file for more information.
8
 */
9

            
10
//! Types related to message handling, parsing and creation.
11
//!
12
//! The base unit that is transmitted between a CoAP client and a CoAP server is called a CoAP
13
//! message (in libcoap also referred to as a "pdu"). This module contains both the basic
14
//! representation for messages in libcoap-rs ([CoapMessage]) as well as builders that simplify the
15
//! process of creating requests and responses and setting the appropriate options ([CoapRequest]
16
//! and [CoapResponse]).
17

            
18
use std::{ffi::c_void, mem::MaybeUninit, slice::Iter};
19
use std::fmt::Write;
20

            
21
use num_traits::FromPrimitive;
22

            
23
use libcoap_sys::{
24
    coap_add_data, coap_add_data_large_request, coap_add_optlist_pdu, coap_add_token, coap_delete_optlist,
25
    coap_delete_pdu, coap_get_data, coap_insert_optlist, coap_new_optlist, coap_opt_length, coap_opt_t, coap_opt_value,
26
    coap_option_iterator_init, coap_option_next, coap_option_num_t, coap_optlist_t, coap_pdu_get_code,
27
    coap_pdu_get_mid, coap_pdu_get_token, coap_pdu_get_type, coap_pdu_init, coap_pdu_set_code, coap_pdu_set_type,
28
    coap_pdu_t, coap_session_t,
29
};
30
pub use request::CoapRequest;
31
pub use response::CoapResponse;
32

            
33
use crate::{
34
    error::{MessageConversionError, OptionValueError},
35
    protocol::{
36
        Block, CoapMatch, CoapMessageCode, CoapMessageType, CoapOptionNum, CoapOptionType, ContentFormat, ETag,
37
        HopLimit, MaxAge, NoResponse, Observe, ProxyScheme, ProxyUri, Size, UriHost, UriPath, UriPort, UriQuery,
38
    },
39
    session::CoapSessionCommon,
40
    types::CoapMessageId,
41
};
42
use crate::context::ensure_coap_started;
43
use crate::protocol::{Echo, Oscore, RequestTag};
44
use crate::types::{
45
    decode_var_len_u16, decode_var_len_u32, decode_var_len_u8, encode_var_len_u16, encode_var_len_u32,
46
    encode_var_len_u8,
47
};
48

            
49
pub mod request;
50
pub mod response;
51

            
52
/// Representation of a CoAP option including its value.
53
///
54
/// For an enum describing the possible option types (and their associated option numbers), see
55
/// [CoapOptionType], for the data type representing option numbers, see [CoapOptionNum]
56
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
57
pub enum CoapOption {
58
    IfMatch(CoapMatch),
59
    IfNoneMatch,
60
    UriHost(UriHost),
61
    UriPort(UriPort),
62
    UriPath(UriPath),
63
    UriQuery(UriQuery),
64
    LocationPath(UriPath),
65
    LocationQuery(UriQuery),
66
    ProxyUri(ProxyUri),
67
    ProxyScheme(ProxyScheme),
68
    ContentFormat(ContentFormat),
69
    Accept(ContentFormat),
70
    Size1(Size),
71
    Size2(Size),
72
    Block1(Block),
73
    Block2(Block),
74
    HopLimit(HopLimit),
75
    NoResponse(NoResponse),
76
    ETag(ETag),
77
    MaxAge(MaxAge),
78
    Observe(Observe),
79
    Oscore(Oscore),
80
    Echo(Echo),
81
    RTag(RequestTag),
82
    QBlock1(Block),
83
    QBlock2(Block),
84
    Other(CoapOptionNum, Box<[u8]>),
85
}
86

            
87
impl CoapOption {
88
    /// Create a CoAP option from its raw representation in the C library.
89
    ///
90
    /// # Safety
91
    /// `opt` must be a valid pointer to a well-formed coap_opt_t value as returned by
92
    /// [coap_option_next()].
93
466
    pub(crate) unsafe fn from_raw_opt(
94
466
        number: coap_option_num_t,
95
466
        opt: *const coap_opt_t,
96
466
    ) -> Result<CoapOption, OptionValueError> {
97
466
        let value = Vec::from(std::slice::from_raw_parts(
98
466
            coap_opt_value(opt),
99
466
            coap_opt_length(opt) as usize,
100
466
        ));
101
466
        Self::from_type_value(number, value)
102
466
    }
103

            
104
    /// Returns the option number associated with this option.
105
699
    pub fn number(&self) -> CoapOptionNum {
106
699
        match self {
107
            CoapOption::IfMatch(_) => CoapOptionType::IfMatch as u16,
108
            CoapOption::IfNoneMatch => CoapOptionType::IfNoneMatch as u16,
109
            CoapOption::UriHost(_) => CoapOptionType::UriHost as u16,
110
            CoapOption::UriPort(_) => CoapOptionType::UriPort as u16,
111
699
            CoapOption::UriPath(_) => CoapOptionType::UriPath as u16,
112
            CoapOption::UriQuery(_) => CoapOptionType::UriQuery as u16,
113
            CoapOption::LocationPath(_) => CoapOptionType::LocationPath as u16,
114
            CoapOption::LocationQuery(_) => CoapOptionType::LocationQuery as u16,
115
            CoapOption::ProxyUri(_) => CoapOptionType::ProxyUri as u16,
116
            CoapOption::ProxyScheme(_) => CoapOptionType::ProxyScheme as u16,
117
            CoapOption::ContentFormat(_) => CoapOptionType::ContentFormat as u16,
118
            CoapOption::Accept(_) => CoapOptionType::Accept as u16,
119
            CoapOption::Size1(_) => CoapOptionType::Size1 as u16,
120
            CoapOption::Size2(_) => CoapOptionType::Size2 as u16,
121
            CoapOption::Block1(_) => CoapOptionType::Block1 as u16,
122
            CoapOption::Block2(_) => CoapOptionType::Block2 as u16,
123
            CoapOption::HopLimit(_) => CoapOptionType::HopLimit as u16,
124
            CoapOption::NoResponse(_) => CoapOptionType::NoResponse as u16,
125
            CoapOption::ETag(_) => CoapOptionType::ETag as u16,
126
            CoapOption::MaxAge(_) => CoapOptionType::MaxAge as u16,
127
            CoapOption::Observe(_) => CoapOptionType::Observe as u16,
128
            CoapOption::Oscore(_) => CoapOptionType::Oscore as u16,
129
            CoapOption::Echo(_) => CoapOptionType::Echo as u16,
130
            CoapOption::RTag(_) => CoapOptionType::RTag as u16,
131
            CoapOption::QBlock1(_) => CoapOptionType::QBlock1 as u16,
132
            CoapOption::QBlock2(_) => CoapOptionType::QBlock2 as u16,
133
            CoapOption::Other(num, _) => *num,
134
        }
135
699
    }
136

            
137
    /// Converts the option into a `Box<[u8]>` containing the value bytes.
138
233
    pub fn into_value_bytes(self) -> Result<Box<[u8]>, OptionValueError> {
139
233
        let num = self.number();
140
233
        let bytes = match self {
141
            CoapOption::IfMatch(val) => match val {
142
                CoapMatch::ETag(tag) => tag,
143
                CoapMatch::Empty => Box::new([]),
144
            },
145
            CoapOption::IfNoneMatch => Box::new([]),
146
            CoapOption::UriHost(value) => value.into_boxed_str().into_boxed_bytes(),
147
            CoapOption::UriPort(value) => encode_var_len_u16(value),
148
233
            CoapOption::UriPath(value) => value.into_boxed_str().into_boxed_bytes(),
149
            CoapOption::UriQuery(value) => value.into_boxed_str().into_boxed_bytes(),
150
            CoapOption::LocationPath(value) => value.into_boxed_str().into_boxed_bytes(),
151
            CoapOption::LocationQuery(value) => value.into_boxed_str().into_boxed_bytes(),
152
            CoapOption::ProxyUri(value) => value.into_boxed_str().into_boxed_bytes(),
153
            CoapOption::ProxyScheme(value) => value.into_boxed_str().into_boxed_bytes(),
154
            CoapOption::ContentFormat(value) => encode_var_len_u16(value),
155
            CoapOption::Accept(value) => encode_var_len_u16(value),
156
            CoapOption::Size1(value) => encode_var_len_u32(value),
157
            CoapOption::Size2(value) => encode_var_len_u32(value),
158
            CoapOption::Block1(value) => encode_var_len_u32(value),
159
            CoapOption::Block2(value) => encode_var_len_u32(value),
160
            CoapOption::HopLimit(value) => encode_var_len_u16(value),
161
            CoapOption::NoResponse(value) => encode_var_len_u8(value),
162
            CoapOption::ETag(value) => value,
163
            CoapOption::MaxAge(value) => encode_var_len_u32(value),
164
            CoapOption::Observe(value) => encode_var_len_u32(value),
165
            CoapOption::Oscore(value) => value,
166
            CoapOption::Echo(value) => value,
167
            CoapOption::RTag(value) => value,
168
            CoapOption::QBlock1(value) => encode_var_len_u32(value),
169
            CoapOption::QBlock2(value) => encode_var_len_u32(value),
170
            CoapOption::Other(_num, data) => data,
171
        };
172
233
        if let Some(opt_type) = <CoapOptionType as FromPrimitive>::from_u16(num) {
173
233
            if bytes.len() < opt_type.min_len() {
174
                return Err(OptionValueError::TooShort);
175
233
            } else if bytes.len() > opt_type.max_len() {
176
                return Err(OptionValueError::TooLong);
177
233
            }
178
        }
179
233
        Ok(bytes)
180
233
    }
181

            
182
    /// Converts this option into a raw coap_optlist_t instance, suitable for addition to a raw
183
    /// coap_pdu_t.
184
233
    pub(crate) fn into_optlist_entry(self) -> Result<*mut coap_optlist_t, OptionValueError> {
185
233
        let num = self.number();
186
233
        let value = self.into_value_bytes()?;
187
233
        Ok(unsafe { coap_new_optlist(num, value.len(), value.as_ptr()) })
188
233
    }
189

            
190
    /// Attempts to convert a raw coap_optlist_t instance into a [CoapOption].
191
    ///
192
    /// # Errors
193
    ///
194
    /// Returns an [OptionValueError] if the provided `optlist_entry` has an invalid value.
195
    ///
196
    /// # Safety
197
    ///
198
    /// optlist_entry must be a valid coap_optlist_t instance whose `number`, `data` and `length`
199
    /// fields describe a CoAP option number and the option value respectively.
200
233
    pub(crate) unsafe fn from_optlist_entry(optlist_entry: &coap_optlist_t) -> Result<Self, OptionValueError> {
201
233
        let value = Vec::from(std::slice::from_raw_parts(optlist_entry.data, optlist_entry.length));
202
233
        Self::from_type_value(optlist_entry.number, value)
203
233
    }
204

            
205
    /// Convert coap_option_num_t and option value into a parsed [CoapOption] if possible.
206
699
    fn from_type_value(type_: coap_option_num_t, value: Vec<u8>) -> Result<Self, OptionValueError> {
207
699
        match CoapOptionType::try_from(type_) {
208
699
            Ok(opt_type) => {
209
699
                if opt_type.min_len() > value.len() {
210
                    return Err(OptionValueError::TooShort);
211
699
                } else if opt_type.max_len() < value.len() {
212
                    return Err(OptionValueError::TooLong);
213
699
                }
214
699
                match opt_type {
215
                    CoapOptionType::IfMatch => Ok(CoapOption::IfMatch(if value.is_empty() {
216
                        CoapMatch::Empty
217
                    } else {
218
                        CoapMatch::ETag(value.into_boxed_slice())
219
                    })),
220
                    CoapOptionType::UriHost => Ok(CoapOption::UriHost(String::from_utf8(value)?)),
221
                    CoapOptionType::ETag => Ok(CoapOption::ETag(value.into_boxed_slice())),
222
                    CoapOptionType::IfNoneMatch => Ok(CoapOption::IfNoneMatch),
223
                    CoapOptionType::UriPort => Ok(CoapOption::UriPort(decode_var_len_u16(value.as_slice()))),
224
                    CoapOptionType::LocationPath => Ok(CoapOption::LocationPath(String::from_utf8(value)?)),
225
466
                    CoapOptionType::UriPath => Ok(CoapOption::UriPath(String::from_utf8(value)?)),
226
                    CoapOptionType::ContentFormat => {
227
                        Ok(CoapOption::ContentFormat(decode_var_len_u16(value.as_slice())))
228
                    },
229
                    CoapOptionType::MaxAge => Ok(CoapOption::MaxAge(decode_var_len_u32(value.as_slice()))),
230
                    CoapOptionType::UriQuery => Ok(CoapOption::UriQuery(String::from_utf8(value)?)),
231
                    CoapOptionType::Accept => Ok(CoapOption::Accept(decode_var_len_u16(value.as_slice()))),
232
                    CoapOptionType::LocationQuery => Ok(CoapOption::LocationQuery(String::from_utf8(value)?)),
233
                    CoapOptionType::ProxyUri => Ok(CoapOption::ProxyUri(String::from_utf8(value)?)),
234
                    CoapOptionType::ProxyScheme => Ok(CoapOption::ProxyScheme(String::from_utf8(value)?)),
235
                    CoapOptionType::Size1 => Ok(CoapOption::Size1(decode_var_len_u32(value.as_slice()))),
236
                    CoapOptionType::Size2 => Ok(CoapOption::Size2(decode_var_len_u32(value.as_slice()))),
237
                    CoapOptionType::Block1 => Ok(CoapOption::Block1(decode_var_len_u32(value.as_slice()))),
238
                    CoapOptionType::Block2 => Ok(CoapOption::Block2(decode_var_len_u32(value.as_slice()))),
239
                    CoapOptionType::HopLimit => Ok(CoapOption::HopLimit(decode_var_len_u16(value.as_slice()))),
240
                    CoapOptionType::NoResponse => {
241
                        Ok(CoapOption::NoResponse(decode_var_len_u8(value.as_slice()) as NoResponse))
242
                    },
243
                    CoapOptionType::Observe => Ok(CoapOption::Observe(decode_var_len_u32(value.as_slice()))),
244
                    CoapOptionType::Oscore => Ok(CoapOption::Oscore(value.into_boxed_slice())),
245
                    CoapOptionType::Echo => Ok(CoapOption::Echo(value.into_boxed_slice())),
246
233
                    CoapOptionType::RTag => Ok(CoapOption::RTag(value.into_boxed_slice())),
247
                    CoapOptionType::QBlock1 => Ok(CoapOption::QBlock1(decode_var_len_u32(value.as_slice()))),
248
                    CoapOptionType::QBlock2 => Ok(CoapOption::QBlock2(decode_var_len_u32(value.as_slice()))),
249
                }
250
            },
251
            _ => Ok(CoapOption::Other(type_, value.into_boxed_slice())),
252
        }
253
699
    }
254
}
255

            
256
/// Constructs a path string from a [Vec] of strings containing the separate path components.
257
233
pub(crate) fn construct_path_string(path_components: Vec<String>) -> String {
258
233
    path_components.into_iter().fold(String::new(), |mut a: String, v| {
259
233
        // Writing to a String _shouldn't_ cause an error.
260
233
        // If it does, something is terribly wrong and we should panic.
261
233
        write!(&mut a, "/{}", v).expect("unable to create path string");
262
233
        a
263
233
    })
264
233
}
265

            
266
/// Constructs a query string from a [Vec] of strings containing the separate query components.
267
pub(crate) fn construct_query_string(query_components: Vec<String>) -> String {
268
    let mut iter = query_components.iter();
269
    let mut out_str = String::from("?");
270
    if let Some(q) = iter.next() {
271
        out_str = out_str + q
272
    }
273
    for q in iter {
274
        out_str += format!("&{}", q).as_ref();
275
    }
276
    out_str
277
}
278

            
279
/// Interface for CoAP messages common between requests, responses and other messages.
280
pub trait CoapMessageCommon {
281
    /// Add the supplied CoAP option to this message.
282
233
    fn add_option(&mut self, option: CoapOption) {
283
233
        self.as_message_mut().options.push(option);
284
233
    }
285

            
286
    /// Clear the list of options that were added to this message using [add_option()](CoapMessageCommon::add_option()).
287
25
    fn clear_options(&mut self) {
288
25
        self.as_message_mut().options.clear();
289
25
    }
290

            
291
    /// Returns an iterator over the options contained in this message.
292
699
    fn options_iter(&self) -> Iter<CoapOption> {
293
699
        self.as_message().options.iter()
294
699
    }
295

            
296
    /// Returns the CoAP message type (confirmable, non-confirmable, acknowledgement, rst) of this message.
297
    fn type_(&self) -> CoapMessageType {
298
        self.as_message().type_
299
    }
300

            
301
    /// Sets the CoAP message type (confirmable, non-confirmable, acknowledgement, rst) of this message.
302
    fn set_type_(&mut self, type_: CoapMessageType) {
303
        self.as_message_mut().type_ = type_;
304
    }
305

            
306
    /// Returns the message code of this message.
307
    /// To determine whether the message is a request or response, use [CoapMessageCode::try_from()]
308
    /// and match for the enum variants.
309
50
    fn code(&self) -> CoapMessageCode {
310
50
        self.as_message().code
311
50
    }
312

            
313
    /// Sets the message code of this message.
314
25
    fn set_code<C: Into<CoapMessageCode>>(&mut self, code: C) {
315
25
        self.as_message_mut().code = code.into();
316
25
    }
317

            
318
    /// Returns the CoAP message ID for this message.
319
25
    fn mid(&self) -> Option<CoapMessageId> {
320
25
        self.as_message().mid
321
25
    }
322

            
323
    /// Sets the CoAP message ID for this message.
324
25
    fn set_mid(&mut self, mid: Option<CoapMessageId>) {
325
25
        self.as_message_mut().mid = mid;
326
25
    }
327

            
328
    /// Returns a reference to the data/body of this message.
329
25
    fn data(&self) -> Option<&[u8]> {
330
25
        self.as_message().data.as_ref().map(|v| v.as_ref())
331
25
    }
332

            
333
    /// Sets the data/body of this message.
334
25
    fn set_data<D: Into<Box<[u8]>>>(&mut self, data: Option<D>) {
335
25
        self.as_message_mut().data = data.map(Into::into);
336
25
    }
337

            
338
    /// Returns the message token.
339
283
    fn token(&self) -> Option<&[u8]> {
340
283
        self.as_message().token.as_ref().map(|v| v.as_ref())
341
283
    }
342

            
343
    /// Sets the message token.
344
    ///
345
    /// Note that [CoapSessionCommon::send_request()] will automatically set the token to a random
346
    /// value if you don't.
347
25
    fn set_token<D: Into<Box<[u8]>>>(&mut self, token: Option<D>) {
348
25
        self.as_message_mut().token = token.map(Into::into);
349
25
    }
350

            
351
    /// Returns a reference to this message.
352
    fn as_message(&self) -> &CoapMessage;
353
    /// Returns a mutable reference to this message.
354
    fn as_message_mut(&mut self) -> &mut CoapMessage;
355
}
356

            
357
/// Representation of a CoAP message.
358
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
359
pub struct CoapMessage {
360
    /// CoAP message type (CON, NON, ACK, RST).
361
    type_: CoapMessageType,
362
    /// CoAP message code (e.g. 2.00 Content or 4.04 Not Found)
363
    code: CoapMessageCode,
364
    /// ID of this message – unique to each message sent over this session.
365
    mid: Option<CoapMessageId>,
366
    /// List of CoAP options associated with this message.
367
    options: Vec<CoapOption>,
368
    /// CoAP message token – used for request-response-matching.
369
    token: Option<Box<[u8]>>,
370
    /// Message body of this message.
371
    data: Option<Box<[u8]>>,
372
}
373

            
374
impl CoapMessage {
375
    /// Creates a new CoAP message with the given type and code.
376
233
    pub fn new(type_: CoapMessageType, code: CoapMessageCode) -> CoapMessage {
377
233
        ensure_coap_started();
378
233
        CoapMessage {
379
233
            type_,
380
233
            code,
381
233
            mid: None,
382
233
            options: Vec::new(),
383
233
            token: None,
384
233
            data: None,
385
233
        }
386
233
    }
387

            
388
    /// Parses the given raw coap_pdu_t into a CoapMessage.
389
    ///
390
    /// # Safety
391
    /// raw_pdu must point to a valid instance of coap_pdu_t.
392
699
    pub unsafe fn from_raw_pdu(raw_pdu: *const coap_pdu_t) -> Result<CoapMessage, MessageConversionError> {
393
699
        ensure_coap_started();
394
699
        let mut option_iter = MaybeUninit::zeroed();
395
699
        coap_option_iterator_init(raw_pdu, option_iter.as_mut_ptr(), std::ptr::null());
396
699
        let mut option_iter = option_iter.assume_init();
397
699
        let mut options = Vec::new();
398
1165
        while let Some(read_option) = coap_option_next(&mut option_iter).as_ref() {
399
466
            options.push(CoapOption::from_raw_opt(option_iter.number, read_option).map_err(|e| {
400
                MessageConversionError::InvalidOptionValue(CoapOptionType::try_from(option_iter.number).ok(), e)
401
466
            })?);
402
        }
403
699
        let mut len: usize = 0;
404
699
        let mut data = std::ptr::null();
405
699
        coap_get_data(raw_pdu, &mut len, &mut data);
406
699
        let data = match len {
407
466
            0 => None,
408
233
            len => Some(Vec::from(std::slice::from_raw_parts(data, len)).into_boxed_slice()),
409
        };
410
699
        let raw_token = coap_pdu_get_token(raw_pdu);
411
699
        let token = Vec::from(std::slice::from_raw_parts(raw_token.s, raw_token.length));
412
699
        Ok(CoapMessage {
413
699
            type_: coap_pdu_get_type(raw_pdu).into(),
414
699
            code: coap_pdu_get_code(raw_pdu).try_into().unwrap(),
415
699
            mid: Some(coap_pdu_get_mid(raw_pdu)),
416
699
            options,
417
699
            token: Some(token.into_boxed_slice()),
418
699
            data,
419
699
        })
420
699
    }
421

            
422
    /// Converts this message into a raw PDU suitable for sending using the raw [coap_send()](libcoap_sys::coap_send())
423
    /// function.
424
    ///
425
    /// The caller is responsible for freeing the returned PDU, either by calling [coap_send()](libcoap_sys::coap_send()) or
426
    /// [coap_delete_pdu()].
427
50
    pub fn into_raw_pdu<'a, S: CoapSessionCommon<'a> + ?Sized>(
428
50
        mut self,
429
50
        session: &S,
430
50
    ) -> Result<*mut coap_pdu_t, MessageConversionError> {
431
50
        let message = self.as_message_mut();
432

            
433
        // SAFETY: all values are valid, cannot cause UB.
434
50
        let pdu = unsafe {
435
            coap_pdu_init(
436
50
                message.type_.to_raw_pdu_type(),
437
50
                message.code.to_raw_pdu_code(),
438
50
                message.mid.ok_or(MessageConversionError::MissingMessageId)?,
439
50
                session.max_pdu_size(),
440
50
            )
441
50
        };
442
50
        if pdu.is_null() {
443
            return Err(MessageConversionError::Unknown);
444
50
        }
445
50
        // SAFETY: We just checked that pdu is a valid pointer.
446
50
        unsafe {
447
50
            let result = self.apply_to_raw_pdu(pdu, session);
448
50
            if result.is_err() {
449
                coap_delete_pdu(pdu);
450
50
            }
451
50
            result
452
        }
453
50
    }
454

            
455
    /// Applies this message to the given raw PDU.
456
    ///
457
    /// Note that existing parameters of the PDU are not explicitly removed. However, because this
458
    /// function calls [coap_add_token()](libcoap_sys::coap_add_token()), any pre-added options and
459
    /// data will probably be destroyed.
460
    ///
461
    /// If you don't have a raw pdu instance, use [CoapMessage::into_raw_pdu()] instead of this
462
    /// function.
463
    ///
464
    /// # Panics
465
    /// Panics if the provided `raw_pdu` is a null pointer.
466
    ///
467
    /// # Safety
468
    /// raw_pdu must point to a valid mutable instance of coap_pdu_t.
469
50
    pub(crate) unsafe fn apply_to_raw_pdu<'a, S: CoapSessionCommon<'a> + ?Sized>(
470
50
        mut self,
471
50
        raw_pdu: *mut coap_pdu_t,
472
50
        session: &S,
473
50
    ) -> Result<*mut coap_pdu_t, MessageConversionError> {
474
50
        assert!(!raw_pdu.is_null(), "attempted to apply CoapMessage to null pointer");
475
50
        coap_pdu_set_type(raw_pdu, self.type_.to_raw_pdu_type());
476
50
        coap_pdu_set_code(raw_pdu, self.code.to_raw_pdu_code());
477
50
        let message = self.as_message_mut();
478
50
        let token: &[u8] = message.token.as_ref().ok_or(MessageConversionError::MissingToken)?;
479
50
        if coap_add_token(raw_pdu, token.len(), token.as_ptr()) == 0 {
480
            return Err(MessageConversionError::Unknown);
481
50
        }
482
50
        let mut optlist = None;
483
50
        let option_iter = std::mem::take(&mut message.options).into_iter();
484
75
        for option in option_iter {
485
25
            let optnum = option.number();
486
25
            let entry = option
487
25
                .into_optlist_entry()
488
25
                .map_err(|e| MessageConversionError::InvalidOptionValue(CoapOptionType::try_from(optnum).ok(), e))?;
489
25
            if entry.is_null() {
490
                if let Some(optlist) = optlist {
491
                    coap_delete_optlist(optlist);
492
                    return Err(MessageConversionError::Unknown);
493
                }
494
25
            }
495
25
            match optlist {
496
25
                None => {
497
25
                    optlist = Some(entry);
498
25
                },
499
                Some(mut optlist) => {
500
                    coap_insert_optlist(&mut optlist, entry);
501
                },
502
            }
503
        }
504
50
        if let Some(mut optlist) = optlist {
505
25
            let optlist_add_success = coap_add_optlist_pdu(raw_pdu, &mut optlist);
506
25
            coap_delete_optlist(optlist);
507
25
            if optlist_add_success == 0 {
508
                return Err(MessageConversionError::Unknown);
509
25
            }
510
25
        }
511
50
        if let Some(data) = message.data.take() {
512
25
            match message.code {
513
                CoapMessageCode::Empty => return Err(MessageConversionError::DataInEmptyMessage),
514
                CoapMessageCode::Request(_) => {
515
                    let len = data.len();
516
                    let box_ptr = Box::into_raw(data);
517
                    coap_add_data_large_request(
518
                        session.raw_session_mut(),
519
                        raw_pdu,
520
                        len,
521
                        box_ptr as *mut u8,
522
                        Some(large_data_cleanup_handler),
523
                        box_ptr as *mut c_void,
524
                    );
525
                },
526
                CoapMessageCode::Response(_) => {
527
                    // TODO blockwise transfer here as well.
528
                    // (for some reason libcoap needs the request PDU here?)
529
25
                    let data: &[u8] = data.as_ref();
530
25
                    if coap_add_data(raw_pdu, data.len(), data.as_ptr()) == 0 {
531
                        return Err(MessageConversionError::Unknown);
532
25
                    }
533
                },
534
            }
535
25
        }
536
50
        Ok(raw_pdu)
537
50
    }
538
}
539

            
540
impl CoapMessageCommon for CoapMessage {
541
699
    fn as_message(&self) -> &CoapMessage {
542
699
        self
543
699
    }
544

            
545
1631
    fn as_message_mut(&mut self) -> &mut CoapMessage {
546
1631
        self
547
1631
    }
548
}
549

            
550
impl From<CoapRequest> for CoapMessage {
551
    fn from(val: CoapRequest) -> Self {
552
        val.into_message()
553
    }
554
}
555

            
556
impl From<CoapResponse> for CoapMessage {
557
233
    fn from(val: CoapResponse) -> Self {
558
233
        val.into_message()
559
233
    }
560
}
561

            
562
/// Handler provided to libcoap to cleanup large message bodies.
563
unsafe extern "C" fn large_data_cleanup_handler(_session: *mut coap_session_t, app_ptr: *mut c_void) {
564
    std::mem::drop(Box::from_raw(app_ptr as *mut u8));
565
}