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

            
11
use 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

            
21
use 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")]
36
use libcoap_sys::{coap_session_get_psk_hint, coap_session_get_psk_identity, coap_session_get_psk_key};
37

            
38
use self::sealed::{CoapSessionCommonInternal, CoapSessionInnerProvider};
39
pub use self::{client::CoapClientSession, server::CoapServerSession};
40
use 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

            
47
pub mod client;
48

            
49
pub mod server;
50

            
51
/// Representation of the states that a session can be in.
52
#[repr(u32)]
53
pub 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

            
61
impl 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

            
77
mod 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
233
        fn add_response(&self, pdu: CoapResponse) {
96
233
            let token = pdu.token();
97
233
            if let Some(token) = token {
98
233
                if self.inner_ref().received_responses.contains_key(token) {
99
233
                    self.inner_mut()
100
233
                        .received_responses
101
233
                        .get_mut(token)
102
233
                        .unwrap()
103
233
                        .push_back(pdu);
104
233
                }
105
            }
106
233
        }
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.
113
pub 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
25
    fn proto(&self) -> CoapProtocol {
234
25
        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
235
25
        unsafe { coap_session_get_proto(self.inner_ref().raw_session) }.into()
236
25
    }
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
50
    fn max_pdu_size(&self) -> usize {
289
50
        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
290
50
        unsafe { coap_session_max_pdu_size(self.inner_ref().raw_session) }
291
50
    }
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
25
    fn next_message_id(&self) -> CoapMessageId {
314
25
        // SAFETY: Provided session pointer being valid is an invariant of CoapSessionInner
315
25
        unsafe { coap_new_message_id(self.inner_mut().raw_session) as CoapMessageId }
316
25
    }
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
50
    fn send<P: Into<CoapMessage>>(&self, pdu: P) -> Result<CoapMessageId, MessageConversionError> {
337
50
        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
50
        let mid = unsafe { coap_send(self.inner_mut().raw_session, raw_pdu) };
341
50
        Ok(mid)
342
50
    }
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
25
    fn send_request(&self, mut req: CoapRequest) -> Result<CoapRequestHandle, MessageConversionError> {
351
25
        if req.token().is_none() {
352
25
            let mut token_len = libcoap_sys::COAP_TOKEN_DEFAULT_MAX as usize;
353
25
            let mut token_tmp: Vec<u8> = vec![0; token_len];
354
25
            // SAFETY: Provided pointer is valid, length matches.
355
25
            unsafe {
356
25
                coap_session_new_token(self.inner_mut().raw_session, &mut token_len, token_tmp.as_mut_ptr());
357
25
            }
358
25
            req.set_token(Some(Vec::from(&token_tmp[0..token_len])))
359
        }
360
25
        let token: Box<[u8]> = Box::from(req.token().unwrap());
361
25
        if req.mid().is_none() {
362
25
            req.set_mid(Some(self.next_message_id()))
363
        }
364
25
        self.inner_mut()
365
25
            .received_responses
366
25
            .insert(token.clone(), VecDeque::new());
367
25
        self.send(req.into_message()).map(|v| CoapRequestHandle::new(v, token))
368
25
    }
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
110
    fn poll_handle(&self, handle: &CoapRequestHandle) -> std::collections::vec_deque::IntoIter<CoapResponse> {
379
110
        self.inner_mut()
380
110
            .received_responses
381
110
            .insert(handle.token.clone(), VecDeque::new())
382
110
            .expect("Attempted to poll handle that does not refer to a valid token")
383
110
            .into_iter()
384
110
    }
385

            
386
    /// Returns whether this session waits for the provided token.
387
233
    fn is_waiting_for_token(&self, token: &CoapToken) -> bool {
388
233
        self.inner_ref().received_responses.contains_key(token)
389
233
    }
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

            
418
impl<'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.
426
pub enum CoapSession<'a> {
427
    Client(CoapClientSession<'a>),
428

            
429
    Server(CoapServerSession<'a>),
430
}
431

            
432
impl<'a> CoapSessionInnerProvider<'a> for CoapSession<'a> {
433
466
    fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>> {
434
466
        match self {
435
466
            CoapSession::Client(sess) => sess.inner_ref(),
436

            
437
            CoapSession::Server(sess) => sess.inner_ref(),
438
        }
439
466
    }
440

            
441
233
    fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>> {
442
233
        match self {
443
233
            CoapSession::Client(sess) => sess.inner_mut(),
444

            
445
            CoapSession::Server(sess) => sess.inner_mut(),
446
        }
447
233
    }
448
}
449

            
450
impl<'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
524
    pub(crate) unsafe fn from_raw(raw_session: *mut coap_session_t) -> CoapSession<'a> {
470
524
        assert!(!raw_session.is_null(), "provided raw session was null");
471
524
        let raw_session_type = coap_session_get_type(raw_session);
472
524

            
473
524
        // Variant names are named by bindgen, we have no influence on this.
474
524
        // Ref: https://github.com/rust-lang/rust/issues/39371
475
524
        #[allow(non_upper_case_globals)]
476
524
        match raw_session_type {
477
            coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
478

            
479
396
            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
128
                CoapServerSession::from_raw(raw_session).into()
483
            },
484
            _ => unreachable!("unknown session type"),
485
        }
486
524
    }
487
}
488

            
489
impl<'a> From<CoapClientSession<'a>> for CoapSession<'a> {
490
396
    fn from(session: CoapClientSession<'a>) -> Self {
491
396
        CoapSession::Client(session)
492
396
    }
493
}
494

            
495
impl<'a> From<CoapServerSession<'a>> for CoapSession<'a> {
496
396
    fn from(session: CoapServerSession<'a>) -> Self {
497
396
        CoapSession::Server(session)
498
396
    }
499
}
500

            
501
impl 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

            
511
impl 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)]
519
pub 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

            
526
impl 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
501
    pub(crate) unsafe fn new<'a>(raw_session: *mut coap_session_t) -> CoapSessionInner<'a> {
542
501
        CoapSessionInner {
543
501
            raw_session,
544
501
            app_data: None,
545
501
            received_responses: HashMap::new(),
546
501
            _context_lifetime_marker: Default::default(),
547
501
        }
548
501
    }
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)]
556
pub struct CoapRequestHandle {
557
    _mid: CoapMessageId,
558
    token: CoapToken,
559
}
560

            
561
impl CoapRequestHandle {
562
25
    fn new<T: Into<Box<[u8]>>>(mid: CoapMessageId, token: T) -> CoapRequestHandle {
563
25
        CoapRequestHandle {
564
25
            _mid: mid,
565
25
            token: token.into(),
566
25
        }
567
25
    }
568
}
569

            
570
// This is fine, we don't read the C-type struct, we return it.
571
233
pub(crate) unsafe extern "C" fn session_response_handler(
572
233
    session: *mut coap_session_t,
573
233
    _sent: *const coap_pdu_t,
574
233
    received: *const coap_pdu_t,
575
233
    _id: coap_mid_t,
576
233
) -> coap_response_t {
577
233
    let mut session = CoapSession::from_raw(session);
578
233
    let client = session.borrow_mut();
579
233
    // First check if the token is actually one we are currently waiting for.
580
233
    let raw_token = coap_pdu_get_token(received);
581
233
    let token: CoapToken = CoapToken::from(std::slice::from_raw_parts(raw_token.s, raw_token.length));
582
233
    if !client.is_waiting_for_token(&token) {
583
        return coap_response_t_COAP_RESPONSE_FAIL;
584
233
    }
585
233
    if let Ok(message) = CoapMessage::from_raw_pdu(received).and_then(CoapResponse::from_message) {
586
233
        client.add_response(message);
587
233
        coap_response_t_COAP_RESPONSE_OK
588
    } else {
589
        coap_response_t_COAP_RESPONSE_FAIL
590
    }
591
233
}