libcoap_rs/session/
mod.rs

1// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright © The libcoap-rs Contributors, all rights reserved.
4 * This file is part of the libcoap-rs project, see the README file for
5 * general information on this project and the NOTICE.md and LICENSE files
6 * for information regarding copyright ownership and terms of use.
7 *
8 * session/mod.rs - Types relating to generic CoAP sessions.
9 */
10
11use std::{
12    any::Any,
13    borrow::BorrowMut,
14    cell::{Ref, RefMut},
15    collections::{HashMap, VecDeque},
16    marker::PhantomData,
17    net::{SocketAddr, ToSocketAddrs},
18    rc::Rc,
19};
20
21use libcoap_sys::{
22    coap_context_t, coap_fixed_point_t, coap_mcast_set_hops, coap_mid_t, coap_new_message_id, coap_pdu_get_token,
23    coap_pdu_t, coap_response_t, coap_response_t_COAP_RESPONSE_FAIL, coap_response_t_COAP_RESPONSE_OK, coap_send,
24    coap_session_get_ack_random_factor, coap_session_get_ack_timeout, coap_session_get_addr_local,
25    coap_session_get_addr_remote, coap_session_get_ifindex, coap_session_get_max_retransmit, coap_session_get_proto,
26    coap_session_get_state, coap_session_get_type, coap_session_init_token, coap_session_max_pdu_size,
27    coap_session_new_token, coap_session_send_ping, coap_session_set_ack_random_factor, coap_session_set_ack_timeout,
28    coap_session_set_max_retransmit, coap_session_set_mtu, coap_session_state_t,
29    coap_session_state_t_COAP_SESSION_STATE_CONNECTING, coap_session_state_t_COAP_SESSION_STATE_CSM,
30    coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED, coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE,
31    coap_session_state_t_COAP_SESSION_STATE_NONE, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_CLIENT,
32    coap_session_type_t_COAP_SESSION_TYPE_HELLO, coap_session_type_t_COAP_SESSION_TYPE_NONE,
33    coap_session_type_t_COAP_SESSION_TYPE_SERVER,
34};
35#[cfg(feature = "dtls-psk")]
36use libcoap_sys::{coap_session_get_psk_hint, coap_session_get_psk_identity, coap_session_get_psk_key};
37
38use self::sealed::{CoapSessionCommonInternal, CoapSessionInnerProvider};
39pub use self::{client::CoapClientSession, server::CoapServerSession};
40use crate::{
41    error::{MessageConversionError, MulticastHopLimitError, SessionGetAppDataError},
42    message::{request::CoapRequest, response::CoapResponse, CoapMessage, CoapMessageCommon},
43    protocol::CoapToken,
44    types::{CoapAddress, CoapMessageId, CoapProtocol, IfIndex, MaxRetransmit},
45};
46
47pub mod client;
48
49pub mod server;
50
51/// Representation of the states that a session can be in.
52#[repr(u32)]
53pub enum CoapSessionState {
54    None = coap_session_state_t_COAP_SESSION_STATE_NONE as u32,
55    Connecting = coap_session_state_t_COAP_SESSION_STATE_CONNECTING as u32,
56    Handshake = coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE as u32,
57    Csm = coap_session_state_t_COAP_SESSION_STATE_CSM as u32,
58    Established = coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED as u32,
59}
60
61impl From<coap_session_state_t> for CoapSessionState {
62    fn from(raw_state: coap_session_state_t) -> Self {
63        // Variant names are named by bindgen, we have no influence on this.
64        // Ref: https://github.com/rust-lang/rust/issues/39371
65        #[allow(non_upper_case_globals)]
66        match raw_state {
67            coap_session_state_t_COAP_SESSION_STATE_NONE => CoapSessionState::None,
68            coap_session_state_t_COAP_SESSION_STATE_CONNECTING => CoapSessionState::Connecting,
69            coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE => CoapSessionState::Handshake,
70            coap_session_state_t_COAP_SESSION_STATE_CSM => CoapSessionState::Csm,
71            coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED => CoapSessionState::Established,
72            _ => unreachable!("unknown session state added"),
73        }
74    }
75}
76
77mod sealed {
78    use super::*;
79
80    /// Internal Trait for types that can provide a Ref(Mut) to CoapSessionInner instances.
81    /// Implemented by the different session types ([CoapClientSession] and [CoapServerSession])
82    pub trait CoapSessionInnerProvider<'a> {
83        /// Provide a Ref to the instance of [CoapSessionInner] contained in this type.
84        fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>>;
85
86        /// Provide a RefMut to the instance of [CoapSessionInner] contained in this type.
87        fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>>;
88    }
89
90    /// Functions common between all sessions that should not be public.
91    ///
92    /// This trait does not have any mandatory functions and will be automatically implemented for
93    /// all types that implement [CoapSessionInnerProvider].
94    pub trait CoapSessionCommonInternal<'a>: CoapSessionInnerProvider<'a> {
95        fn add_response(&self, pdu: CoapResponse) {
96            let token = pdu.token();
97            if let Some(token) = token {
98                if self.inner_ref().received_responses.contains_key(token) {
99                    self.inner_mut()
100                        .received_responses
101                        .get_mut(token)
102                        .unwrap()
103                        .push_back(pdu);
104                }
105            }
106        }
107    }
108
109    impl<'a, T: CoapSessionInnerProvider<'a>> CoapSessionCommonInternal<'a> for T {}
110}
111
112/// Trait for functions that are common between client and server sessions.
113pub trait CoapSessionCommon<'a>: CoapSessionCommonInternal<'a> {
114    /// Returns the application specific data stored alongside this session.
115    fn app_data<T: Any>(&self) -> Result<Option<Rc<T>>, SessionGetAppDataError> {
116        self.inner_ref()
117            .app_data
118            .as_ref()
119            .map(|v| v.clone().downcast().map_err(|_v| SessionGetAppDataError::WrongType))
120            .transpose()
121    }
122
123    /// Sets the application-specific data stored alongside this session.
124    fn set_app_data<T: 'static + Any>(&self, value: Option<T>) {
125        let mut inner = self.inner_mut();
126        let new_box: Option<Rc<dyn Any>> = value.map(|v| Rc::new(v) as Rc<dyn Any>);
127        inner.app_data = new_box;
128    }
129
130    /// Clears the application-specific data stored alongside this session.
131    fn clear_app_data(&self) {
132        let mut inner = self.inner_mut();
133        inner.app_data = None;
134    }
135
136    /// Returns the Ack-Random-Factor used by libcoap.
137    ///
138    /// The returned value is a tuple consisting of an integer and a fractional part, where the
139    /// fractional part is a value from 0-999 and represents the first three digits after the comma.
140    fn ack_random_factor(&self) -> (u16, u16) {
141        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
142        let random_factor = unsafe { coap_session_get_ack_random_factor(self.inner_ref().raw_session) };
143        (random_factor.integer_part, random_factor.fractional_part)
144    }
145
146    /// Sets the Ack-Random-Factor used by libcoap.
147    fn set_ack_random_factor(&self, integer_part: u16, fractional_part: u16) {
148        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
149        unsafe {
150            coap_session_set_ack_random_factor(
151                self.inner_mut().raw_session,
152                coap_fixed_point_t {
153                    integer_part,
154                    fractional_part,
155                },
156            )
157        };
158    }
159
160    /// Returns the current value of the Acknowledgement Timeout for this session (in seconds).
161    ///
162    /// The returned value is a tuple consisting of an integer and a fractional part, where the
163    /// fractional part is a value from 0-999 and represents the first three digits after the comma.
164    fn ack_timeout(&self) -> (u16, u16) {
165        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
166        let random_factor = unsafe { coap_session_get_ack_timeout(self.inner_ref().raw_session) };
167        (random_factor.integer_part, random_factor.fractional_part)
168    }
169
170    /// Sets the value of the Acknowledgement Timeout for this session.
171    fn set_ack_timeout(&self, integer_part: u16, fractional_part: u16) {
172        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
173        unsafe {
174            coap_session_set_ack_timeout(
175                self.inner_ref().raw_session,
176                coap_fixed_point_t {
177                    integer_part,
178                    fractional_part,
179                },
180            )
181        };
182    }
183
184    /// Returns the local address for this session.
185    fn addr_local(&self) -> SocketAddr {
186        CoapAddress::from(unsafe {
187            // This is infallible as long as the raw session is valid (which it always should be
188            // as long as the invariants are kept).
189            coap_session_get_addr_local(self.inner_ref().raw_session)
190                .as_ref()
191                .unwrap()
192        })
193        .to_socket_addrs()
194        .unwrap()
195        .next()
196        .unwrap()
197    }
198
199    /// Returns the remote address for this session.
200    fn addr_remote(&self) -> SocketAddr {
201        CoapAddress::from(unsafe {
202            // This is infallible as long as the raw session is valid (which it always should be
203            // as long as the invariants are kept).
204            coap_session_get_addr_remote(self.inner_ref().raw_session)
205                .as_ref()
206                .unwrap()
207        })
208        .to_socket_addrs()
209        .unwrap()
210        .next()
211        .unwrap()
212    }
213
214    /// Returns the interface index for this session.
215    fn if_index(&self) -> IfIndex {
216        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
217        unsafe { coap_session_get_ifindex(self.inner_ref().raw_session) }
218    }
219
220    /// Returns the maximum number of retransmissions for this session.
221    fn max_retransmit(&self) -> MaxRetransmit {
222        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
223        unsafe { coap_session_get_max_retransmit(self.inner_ref().raw_session) }
224    }
225
226    /// Sets the maximum number of retransmissions for this session.
227    fn set_max_retransmit(&mut self, value: MaxRetransmit) {
228        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
229        unsafe { coap_session_set_max_retransmit(self.inner_ref().raw_session, value) }
230    }
231
232    /// Returns the underlying transport protocol used for this session.
233    fn proto(&self) -> CoapProtocol {
234        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
235        unsafe { coap_session_get_proto(self.inner_ref().raw_session) }.into()
236    }
237
238    /// Returns the current PSK hint for this session.
239    #[cfg(feature = "dtls-psk")]
240    fn psk_hint(&self) -> Option<Box<[u8]>> {
241        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
242        unsafe {
243            coap_session_get_psk_hint(self.inner_ref().raw_session)
244                .as_ref()
245                .map(|raw_hint| Box::from(std::slice::from_raw_parts(raw_hint.s, raw_hint.length)))
246        }
247    }
248
249    /// Returns the current PSK identity for this session.
250    #[cfg(feature = "dtls-psk")]
251    fn psk_identity(&self) -> Option<Box<[u8]>> {
252        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
253        unsafe {
254            coap_session_get_psk_identity(self.inner_ref().raw_session)
255                .as_ref()
256                .map(|raw_hint| Box::from(std::slice::from_raw_parts(raw_hint.s, raw_hint.length)))
257        }
258    }
259
260    /// Returns the current PSK key for this session.
261    #[cfg(feature = "dtls-psk")]
262    fn psk_key(&self) -> Option<Box<[u8]>> {
263        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
264        unsafe {
265            coap_session_get_psk_key(self.inner_ref().raw_session)
266                .as_ref()
267                .map(|raw_hint| Box::from(std::slice::from_raw_parts(raw_hint.s, raw_hint.length)))
268        }
269    }
270
271    /// Returns the current state of this session.
272    #[must_use = "getting the current session state without using it is a no-op"]
273    fn state(&self) -> CoapSessionState {
274        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
275        unsafe { coap_session_get_state(self.inner_ref().raw_session).into() }
276    }
277
278    /// Initializes the initial token value used by libcoap for this session.
279    ///
280    /// This value will be used as the token and incremented for each message sent through this
281    /// session that does not already have a token set.
282    fn init_token(&self, token: &[u8; 8]) {
283        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
284        unsafe { coap_session_init_token(self.inner_mut().raw_session, token.len(), token.as_ptr()) }
285    }
286
287    /// Returns the maximum size of a PDU for this session.
288    fn max_pdu_size(&self) -> usize {
289        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
290        unsafe { coap_session_max_pdu_size(self.inner_ref().raw_session) }
291    }
292
293    /// Sets the maximum size of a PDU for this session.
294    fn set_mtu(&self, mtu: u32) {
295        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
296        unsafe { coap_session_set_mtu(self.inner_mut().raw_session, mtu) }
297    }
298
299    /// Sets maximum number of hops for multicast request
300    fn set_mcast_hops_limit(&self, hops: usize) -> Result<(), MulticastHopLimitError> {
301        // In some cases the default hoplimit of 1 is not enough to reach the destination. Choose limit accordingly
302        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
303        unsafe {
304            let ret = coap_mcast_set_hops(self.inner_mut().raw_session, hops);
305            if ret == 0 {
306                return Err(MulticastHopLimitError::Unknown);
307            }
308        };
309        Ok(())
310    }
311
312    /// Returns the next message ID that should be used for this session.
313    fn next_message_id(&self) -> CoapMessageId {
314        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
315        unsafe { coap_new_message_id(self.inner_mut().raw_session) as CoapMessageId }
316    }
317
318    /// Returns the next token that should be used for requests.
319    fn new_token(&mut self, token: &mut [u8; 8]) -> usize {
320        let mut length = 8;
321        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
322        unsafe { coap_session_new_token(self.inner_mut().raw_session, &mut length, token.as_mut_ptr()) }
323        length
324    }
325
326    /// Send a ping message to the remote peer.
327    fn send_ping(&mut self) -> CoapMessageId {
328        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
329        unsafe { coap_session_send_ping(self.inner_mut().raw_session) }
330    }
331
332    /// Send the given message-like object to the peer.
333    ///
334    /// # Errors
335    /// Returns a [MessageConversionError] if the supplied object cannot be converted to a message.
336    fn send<P: Into<CoapMessage>>(&self, pdu: P) -> Result<CoapMessageId, MessageConversionError> {
337        let raw_pdu = pdu.into().into_raw_pdu(self)?;
338        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner,
339        // raw pdu should be valid as we got it from `into_raw_pdu()`.
340        let mid = unsafe { coap_send(self.inner_mut().raw_session, raw_pdu) };
341        Ok(mid)
342    }
343
344    /// Sends the given CoapRequest, returning a CoapRequestHandle that can be used to poll the
345    /// request for completion.
346    ///
347    /// # Errors
348    /// Returns a [MessageConversionError] if the given Request could not be converted into a raw
349    /// message.
350    fn send_request(&self, mut req: CoapRequest) -> Result<CoapRequestHandle, MessageConversionError> {
351        if req.token().is_none() {
352            let mut token_len = libcoap_sys::COAP_TOKEN_DEFAULT_MAX as usize;
353            let mut token_tmp: Vec<u8> = vec![0; token_len];
354            // SAFETY: Provided pointer is valid, length matches.
355            unsafe {
356                coap_session_new_token(self.inner_mut().raw_session, &mut token_len, token_tmp.as_mut_ptr());
357            }
358            req.set_token(Some(Vec::from(&token_tmp[0..token_len])))
359        }
360        let token: Box<[u8]> = Box::from(req.token().unwrap());
361        if req.mid().is_none() {
362            req.set_mid(Some(self.next_message_id()))
363        }
364        self.inner_mut()
365            .received_responses
366            .insert(token.clone(), VecDeque::new());
367        self.send(req.into_message()).map(|v| CoapRequestHandle::new(v, token))
368    }
369
370    /// Polls whether the request for the given handle already has pending responses.
371    ///
372    /// Returns an iterator over all responses associated with the request.
373    ///
374    /// # Panics
375    ///
376    /// Panics if the provided handle does not refer to a valid token, i.e., because it belongs to
377    /// a different session.
378    fn poll_handle(&self, handle: &CoapRequestHandle) -> std::collections::vec_deque::IntoIter<CoapResponse> {
379        self.inner_mut()
380            .received_responses
381            .insert(handle.token.clone(), VecDeque::new())
382            .expect("Attempted to poll handle that does not refer to a valid token")
383            .into_iter()
384    }
385
386    /// Returns whether this session waits for the provided token.
387    fn is_waiting_for_token(&self, token: &CoapToken) -> bool {
388        self.inner_ref().received_responses.contains_key(token)
389    }
390
391    /// Stops listening for responses to this request handle.
392    ///
393    /// Any future responses to the request associated with this handle will be responded to with an
394    /// RST message.
395    fn remove_handle(&self, handle: CoapRequestHandle) {
396        self.inner_mut().received_responses.remove(&handle.token);
397    }
398
399    /// Returns a mutable reference to the underlying raw session.
400    ///
401    /// # Safety
402    /// Do not do anything that would interfere with the functionality of this wrapper.
403    /// Most importantly, *do not* free the session yourself.
404    unsafe fn raw_session_mut(&self) -> *mut coap_session_t {
405        self.inner_mut().raw_session
406    }
407
408    /// Returns a reference to the underlying raw session.
409    ///
410    /// # Safety
411    /// Do not do anything that would interfere with the functionality of this wrapper.
412    /// Most importantly, *do not* free the session yourself.
413    unsafe fn raw_session(&self) -> *const coap_session_t {
414        self.inner_ref().raw_session
415    }
416}
417
418impl<'a, T: CoapSessionCommonInternal<'a>> CoapSessionCommon<'a> for T {}
419
420#[derive(Debug)]
421/// The representation of a CoAP session.
422///
423/// This enum only provides functionality that is common between clients and servers (by
424/// implementing [CoapSessionCommon]). If you require functionality specific to client- or
425/// server-side sessions, match this enum on the required session type.
426pub enum CoapSession<'a> {
427    Client(CoapClientSession<'a>),
428
429    Server(CoapServerSession<'a>),
430}
431
432impl<'a> CoapSessionInnerProvider<'a> for CoapSession<'a> {
433    fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>> {
434        match self {
435            CoapSession::Client(sess) => sess.inner_ref(),
436
437            CoapSession::Server(sess) => sess.inner_ref(),
438        }
439    }
440
441    fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>> {
442        match self {
443            CoapSession::Client(sess) => sess.inner_mut(),
444
445            CoapSession::Server(sess) => sess.inner_mut(),
446        }
447    }
448}
449
450impl<'a> CoapSession<'a> {
451    /// Restores a CoapSession from its raw counterpart.
452    ///
453    /// Note that it is not possible to statically infer the lifetime of the created session from
454    /// the raw pointer, i.e., the session will be created with an arbitrary lifetime.
455    /// Therefore, callers of this function should ensure that the created session instance does not
456    /// outlive the context it is bound to.
457    /// Failing to do so will result in a panic/abort in the context destructor as it is unable to
458    /// claim exclusive ownership of the session.
459    ///
460    /// # Panics
461    ///
462    /// Panics if the given pointer is a null pointer.
463    ///
464    /// # Safety
465    /// The provided pointer must be valid, the provided session's app data must be a valid argument
466    /// to [`CoapFfiRcCell<CoapClientSessionInner>::clone_raw_rc`](crate::mem::CoapFfiRcCell::clone_raw_rc)
467    /// or [`CoapFfiRcCell<CoapServerSessionInner>::clone_raw_rc`](crate::mem::CoapFfiRcCell::clone_raw_rc)
468    /// (depending on the session type).
469    pub(crate) unsafe fn from_raw(raw_session: *mut coap_session_t) -> CoapSession<'a> {
470        assert!(!raw_session.is_null(), "provided raw session was null");
471        let raw_session_type = coap_session_get_type(raw_session);
472
473        // Variant names are named by bindgen, we have no influence on this.
474        // Ref: https://github.com/rust-lang/rust/issues/39371
475        #[allow(non_upper_case_globals)]
476        match raw_session_type {
477            coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
478
479            coap_session_type_t_COAP_SESSION_TYPE_CLIENT => CoapClientSession::from_raw(raw_session).into(),
480
481            coap_session_type_t_COAP_SESSION_TYPE_SERVER | coap_session_type_t_COAP_SESSION_TYPE_HELLO => {
482                CoapServerSession::from_raw(raw_session).into()
483            },
484            _ => unreachable!("unknown session type"),
485        }
486    }
487}
488
489impl<'a> From<CoapClientSession<'a>> for CoapSession<'a> {
490    fn from(session: CoapClientSession<'a>) -> Self {
491        CoapSession::Client(session)
492    }
493}
494
495impl<'a> From<CoapServerSession<'a>> for CoapSession<'a> {
496    fn from(session: CoapServerSession<'a>) -> Self {
497        CoapSession::Server(session)
498    }
499}
500
501impl PartialEq for CoapSession<'_> {
502    fn eq(&self, other: &Self) -> bool {
503        match self {
504            CoapSession::Client(cli_sess) => cli_sess.eq(other),
505
506            CoapSession::Server(srv_sess) => srv_sess.eq(other),
507        }
508    }
509}
510
511impl Eq for CoapSession<'_> {}
512
513/// Inner part of the representation of a CoapSession.
514///
515/// For internal use only, this is only public because of some limitations in Rusts type system
516/// (as we would leak a private type).
517#[derive(Debug)]
518#[doc(hidden)]
519pub struct CoapSessionInner<'a> {
520    raw_session: *mut coap_session_t,
521    app_data: Option<Rc<dyn Any>>,
522    received_responses: HashMap<CoapToken, VecDeque<CoapResponse>>,
523    _context_lifetime_marker: PhantomData<&'a coap_context_t>,
524}
525
526impl CoapSessionInner<'_> {
527    /// Initializes a new session from its raw counterpart.
528    ///
529    /// Callers of this function should probably also insert themselves into the `app_data` pointer
530    /// of the raw session to allow later retrieval (as is done for `CoapClientSession` and
531    /// `CoapServerSession`.
532    ///
533    /// # Safety
534    /// Provided pointer must point to a valid raw_session.
535    ///
536    /// Note that the chosen lifetime for the inner session is arbitrarily, so you should ensure
537    /// that the CoapSessionInner instance does not outlive the underlying session.
538    /// To do so, you probably want to increase the reference counter of the session by one and
539    /// never provide values of this session with a lifetime that exceeds the one of the
540    /// [CoapContext] this session is bound to.
541    pub(crate) unsafe fn new<'a>(raw_session: *mut coap_session_t) -> CoapSessionInner<'a> {
542        CoapSessionInner {
543            raw_session,
544            app_data: None,
545            received_responses: HashMap::new(),
546            _context_lifetime_marker: Default::default(),
547        }
548    }
549}
550
551/// A handle returned by CoAP sessions upon sending a request.
552///
553/// Can be used in calls to [CoapSessionCommon::poll_handle()] to check for responses to the sent
554/// request.
555#[derive(Debug, Clone, PartialEq, Eq, Hash)]
556pub struct CoapRequestHandle {
557    _mid: CoapMessageId,
558    token: CoapToken,
559}
560
561impl CoapRequestHandle {
562    fn new<T: Into<Box<[u8]>>>(mid: CoapMessageId, token: T) -> CoapRequestHandle {
563        CoapRequestHandle {
564            _mid: mid,
565            token: token.into(),
566        }
567    }
568}
569
570// This is fine, we don't read the C-type struct, we return it.
571pub(crate) unsafe extern "C" fn session_response_handler(
572    session: *mut coap_session_t,
573    _sent: *const coap_pdu_t,
574    received: *const coap_pdu_t,
575    _id: coap_mid_t,
576) -> coap_response_t {
577    let mut session = CoapSession::from_raw(session);
578    let client = session.borrow_mut();
579    // First check if the token is actually one we are currently waiting for.
580    let raw_token = coap_pdu_get_token(received);
581    let token: CoapToken = CoapToken::from(std::slice::from_raw_parts(raw_token.s, raw_token.length));
582    if !client.is_waiting_for_token(&token) {
583        return coap_response_t_COAP_RESPONSE_FAIL;
584    }
585    if let Ok(message) = CoapMessage::from_raw_pdu(received).and_then(CoapResponse::from_message) {
586        client.add_response(message);
587        coap_response_t_COAP_RESPONSE_OK
588    } else {
589        coap_response_t_COAP_RESPONSE_FAIL
590    }
591}