libcoap_rs/
context.rs

1// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * context.rs - CoAP context related code.
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//! Module containing context-internal types and traits.
11
12use std::{any::Any, ffi::c_void, fmt::Debug, marker::PhantomData, net::SocketAddr, ops::Sub, time::Duration};
13
14use libc::c_uint;
15
16use libcoap_sys::{
17    coap_add_resource, coap_bin_const_t, coap_can_exit, coap_context_get_csm_max_message_size,
18    coap_context_get_csm_timeout, coap_context_get_max_handshake_sessions, coap_context_get_max_idle_sessions,
19    coap_context_get_session_timeout, coap_context_set_block_mode, coap_context_set_csm_max_message_size,
20    coap_context_set_csm_timeout, coap_context_set_keepalive, coap_context_set_max_handshake_sessions,
21    coap_context_set_max_idle_sessions, coap_context_set_psk2, coap_context_set_session_timeout, coap_context_t,
22    coap_dtls_spsk_info_t, coap_dtls_spsk_t, coap_event_t, coap_free_context, coap_get_app_data, coap_io_process,
23    coap_new_context, coap_register_event_handler, coap_register_response_handler, coap_set_app_data,
24    COAP_BLOCK_SINGLE_BODY, COAP_BLOCK_USE_LIBCOAP, COAP_DTLS_SPSK_SETUP_VERSION, COAP_IO_WAIT,
25};
26
27#[cfg(feature = "dtls")]
28use crate::crypto::{dtls_server_id_callback, dtls_server_sni_callback, CoapServerCryptoProvider};
29#[cfg(feature = "dtls")]
30use crate::crypto::{CoapCryptoProviderResponse, CoapCryptoPskIdentity, CoapCryptoPskInfo};
31use crate::event::{event_handler_callback, CoapEventHandler};
32use crate::mem::{CoapLendableFfiRcCell, CoapLendableFfiWeakCell, DropInnerExclusively};
33
34use crate::session::CoapSessionCommon;
35
36use crate::session::CoapServerSession;
37use crate::session::CoapSession;
38#[cfg(feature = "dtls")]
39use crate::transport::CoapDtlsEndpoint;
40use crate::{
41    error::{ContextCreationError, EndpointCreationError, IoProcessError},
42    resource::{CoapResource, UntypedCoapResource},
43    session::session_response_handler,
44    transport::{CoapEndpoint, CoapUdpEndpoint},
45};
46
47#[derive(Debug)]
48struct CoapContextInner<'a> {
49    /// Reference to the raw context this context wraps around.
50    raw_context: *mut coap_context_t,
51    /// A list of endpoints that this context is currently associated with.
52    endpoints: Vec<CoapEndpoint>,
53    /// A list of resources associated with this context.
54    resources: Vec<Box<dyn UntypedCoapResource>>,
55    /// A list of server-side sessions that are currently active.
56    server_sessions: Vec<CoapServerSession<'a>>,
57    /// The event handler responsible for library-user side handling of events.
58    event_handler: Option<Box<dyn CoapEventHandler>>,
59    /// The provider for cryptography information for server-side sessions.
60    #[cfg(feature = "dtls")]
61    crypto_provider: Option<Box<dyn CoapServerCryptoProvider>>,
62    /// Last default cryptography info provided to libcoap.
63    #[cfg(feature = "dtls")]
64    crypto_default_info: Option<CoapCryptoPskInfo>,
65    /// Container for SNI information so that the libcoap C library can keep referring to the memory
66    /// locations.
67    #[cfg(feature = "dtls")]
68    crypto_sni_info_container: Vec<CoapCryptoPskInfo>,
69    /// Last provided cryptography information for server-side sessions (temporary storage as
70    /// libcoap makes defensive copies).
71    #[cfg(feature = "dtls")]
72    crypto_current_data: Option<CoapCryptoPskInfo>,
73    /// Structure referring to the last provided cryptography information for server-side sessions.
74    /// coap_dtls_spsk_info_t created upon calling dtls_server_sni_callback() as the SNI validation callback.
75    /// The caller of the validate_sni_call_back will make a defensive copy, so this one only has
76    /// to be valid for a very short time and can always be overridden by dtls_server_sni_callback().
77    #[cfg(feature = "dtls")]
78    crypto_last_info_ref: coap_dtls_spsk_info_t,
79    _context_lifetime_marker: PhantomData<&'a coap_context_t>,
80}
81
82/// A CoAP Context — container for general state and configuration information relating to CoAP
83///
84/// The equivalent to the [coap_context_t] type in libcoap.
85#[derive(Debug)]
86pub struct CoapContext<'a> {
87    inner: CoapLendableFfiRcCell<CoapContextInner<'a>>,
88}
89
90impl<'a> CoapContext<'a> {
91    /// Creates a new context.
92    ///
93    /// # Errors
94    /// Returns an error if the underlying libcoap library was unable to create a new context
95    /// (probably an allocation error?).
96    pub fn new() -> Result<CoapContext<'a>, ContextCreationError> {
97        // SAFETY: Providing null here is fine, the context will just not be bound to an endpoint
98        // yet.
99        let raw_context = unsafe { coap_new_context(std::ptr::null()) };
100        if raw_context.is_null() {
101            return Err(ContextCreationError::Unknown);
102        }
103        // SAFETY: We checked that raw_context is not null.
104        unsafe {
105            coap_context_set_block_mode(raw_context, (COAP_BLOCK_USE_LIBCOAP | COAP_BLOCK_SINGLE_BODY) as u8);
106            coap_register_response_handler(raw_context, Some(session_response_handler));
107        }
108        let inner = CoapLendableFfiRcCell::new(CoapContextInner {
109            raw_context,
110            endpoints: Vec::new(),
111            resources: Vec::new(),
112            server_sessions: Vec::new(),
113            event_handler: None,
114            #[cfg(feature = "dtls")]
115            crypto_provider: None,
116            #[cfg(feature = "dtls")]
117            crypto_default_info: None,
118            #[cfg(feature = "dtls")]
119            crypto_sni_info_container: Vec::new(),
120            #[cfg(feature = "dtls")]
121            crypto_current_data: None,
122            #[cfg(feature = "dtls")]
123            crypto_last_info_ref: coap_dtls_spsk_info_t {
124                hint: coap_bin_const_t {
125                    length: 0,
126                    s: std::ptr::null(),
127                },
128                key: coap_bin_const_t {
129                    length: 0,
130                    s: std::ptr::null(),
131                },
132            },
133            _context_lifetime_marker: Default::default(),
134        });
135
136        // SAFETY: We checked that the raw context is not null, the provided function is valid and
137        // the app data pointer provided must be valid as we just created it using
138        // `create_raw_weak_box()`.
139        unsafe {
140            coap_set_app_data(raw_context, inner.create_raw_weak_box() as *mut c_void);
141            coap_register_event_handler(raw_context, Some(event_handler_callback));
142        }
143
144        Ok(CoapContext { inner })
145    }
146
147    /// Restores a CoapContext from its raw counterpart.
148    ///
149    /// # Safety
150    /// Provided pointer must point to as valid instance of a raw context whose application data
151    /// points to a `*mut CoapLendableFfiWeakCell<CoapContextInner>`.
152    pub(crate) unsafe fn from_raw(raw_context: *mut coap_context_t) -> CoapContext<'a> {
153        assert!(!raw_context.is_null());
154        let inner = CoapLendableFfiRcCell::clone_raw_weak_box(
155            coap_get_app_data(raw_context) as *mut CoapLendableFfiWeakCell<CoapContextInner>
156        );
157
158        CoapContext { inner }
159    }
160
161    /// Handle an incoming event provided by libcoap.
162    pub(crate) fn handle_event(&self, mut session: CoapSession<'a>, event: coap_event_t) {
163        let inner_ref = &mut *self.inner.borrow_mut();
164        // Call event handler for event.
165        if let Some(handler) = &mut inner_ref.event_handler {
166            match event {
167                coap_event_t::COAP_EVENT_DTLS_CLOSED => handler.handle_dtls_closed(&mut session),
168                coap_event_t::COAP_EVENT_DTLS_CONNECTED => handler.handle_dtls_connected(&mut session),
169                coap_event_t::COAP_EVENT_DTLS_RENEGOTIATE => handler.handle_dtls_renegotiate(&mut session),
170                coap_event_t::COAP_EVENT_DTLS_ERROR => handler.handle_dtls_error(&mut session),
171                coap_event_t::COAP_EVENT_TCP_CONNECTED => handler.handle_tcp_connected(&mut session),
172                coap_event_t::COAP_EVENT_TCP_CLOSED => handler.handle_tcp_closed(&mut session),
173                coap_event_t::COAP_EVENT_TCP_FAILED => handler.handle_tcp_failed(&mut session),
174                coap_event_t::COAP_EVENT_SESSION_CONNECTED => handler.handle_session_connected(&mut session),
175                coap_event_t::COAP_EVENT_SESSION_CLOSED => handler.handle_session_closed(&mut session),
176                coap_event_t::COAP_EVENT_SESSION_FAILED => handler.handle_session_failed(&mut session),
177                coap_event_t::COAP_EVENT_PARTIAL_BLOCK => handler.handle_partial_block(&mut session),
178                coap_event_t::COAP_EVENT_SERVER_SESSION_NEW => {
179                    if let CoapSession::Server(server_session) = &mut session {
180                        handler.handle_server_session_new(server_session)
181                    } else {
182                        panic!("server-side session event fired for non-server-side session");
183                    }
184                },
185                coap_event_t::COAP_EVENT_SERVER_SESSION_DEL => {
186                    if let CoapSession::Server(server_session) = &mut session {
187                        handler.handle_server_session_del(server_session)
188                    } else {
189                        panic!("server-side session event fired for non-server-side session");
190                    }
191                },
192                _ => {
193                    // TODO probably a log message is justified here.
194                },
195            }
196        }
197        // For server-side sessions: Ensure that server-side session wrappers are either kept in memory or dropped when needed.
198        if let CoapSession::Server(serv_sess) = session {
199            match event {
200                coap_event_t::COAP_EVENT_SERVER_SESSION_NEW => inner_ref.server_sessions.push(serv_sess),
201                coap_event_t::COAP_EVENT_SERVER_SESSION_DEL => {
202                    std::mem::drop(inner_ref.server_sessions.remove(
203                        inner_ref.server_sessions.iter().position(|v| v.eq(&serv_sess)).expect(
204                            "attempted to remove session wrapper from context that was never associated with it",
205                        ),
206                    ));
207                    serv_sess.drop_exclusively();
208                },
209                _ => {},
210            }
211        }
212    }
213}
214
215impl CoapContext<'_> {
216    /// Performs a controlled shutdown of the CoAP context.
217    ///
218    /// This will perform all still outstanding IO operations until [coap_can_exit()] confirms that
219    /// the context has no more outstanding IO and can be dropped without interrupting sessions.
220    pub fn shutdown(mut self, exit_wait_timeout: Option<Duration>) -> Result<(), IoProcessError> {
221        let mut remaining_time = exit_wait_timeout;
222        // Send remaining packets until we can cleanly shutdown.
223        // SAFETY: Provided context is always valid as an invariant of this struct.
224        while unsafe { coap_can_exit(self.inner.borrow_mut().raw_context) } == 0 {
225            let spent_time = self.do_io(remaining_time)?;
226            remaining_time = remaining_time.map(|v| v.sub(spent_time));
227        }
228        Ok(())
229    }
230
231    /// Creates a new UDP endpoint that is bound to the given address.
232    pub fn add_endpoint_udp(&mut self, addr: SocketAddr) -> Result<(), EndpointCreationError> {
233        // SAFETY: Because we never return an owned reference to the endpoint, it cannot outlive the
234        // context it is bound to (i.e. this one).
235        let endpoint = unsafe { CoapUdpEndpoint::new(self, addr)? }.into();
236        let mut inner_ref = self.inner.borrow_mut();
237        inner_ref.endpoints.push(endpoint);
238        // Cannot fail, we just pushed to the Vec.
239        Ok(())
240    }
241
242    /// TODO
243    #[cfg(feature = "tcp")]
244    pub fn add_endpoint_tcp(&mut self, _addr: SocketAddr) -> Result<(), EndpointCreationError> {
245        todo!()
246    }
247
248    /// Creates a new DTLS endpoint that is bound to the given address.
249    ///
250    /// Note that in order to actually connect to DTLS clients, you need to set a crypto provider
251    /// using [set_server_crypto_provider()](CoapContext::set_server_crypto_provider())
252    #[cfg(feature = "dtls")]
253    pub fn add_endpoint_dtls(&mut self, addr: SocketAddr) -> Result<(), EndpointCreationError> {
254        // SAFETY: Provided context (i.e., this instance) will not outlive the endpoint, as we will
255        // drop it alongside this context and never hand out copies of it.
256        let endpoint = unsafe { CoapDtlsEndpoint::new(self, addr)? }.into();
257        let mut inner_ref = self.inner.borrow_mut();
258        inner_ref.endpoints.push(endpoint);
259        // Cannot fail, we just pushed to the Vec.
260        Ok(())
261    }
262
263    /// TODO
264    #[cfg(all(feature = "tcp", feature = "dtls"))]
265    pub fn add_endpoint_tls(&mut self, _addr: SocketAddr) -> Result<(), EndpointCreationError> {
266        todo!()
267    }
268
269    /// Adds the given resource to the resource pool of this context.
270    pub fn add_resource<D: Any + ?Sized + Debug>(&mut self, res: CoapResource<D>) {
271        let mut inner_ref = self.inner.borrow_mut();
272        inner_ref.resources.push(Box::new(res));
273        // SAFETY: raw context is valid, raw resource is also guaranteed to be valid as long as
274        // contract of CoapResource is upheld.
275        unsafe {
276            coap_add_resource(
277                inner_ref.raw_context,
278                inner_ref.resources.last_mut().unwrap().raw_resource(),
279            );
280        };
281    }
282
283    /// Sets the server-side cryptography information provider.
284    #[cfg(feature = "dtls")]
285    pub fn set_server_crypto_provider(&mut self, provider: Option<Box<dyn CoapServerCryptoProvider>>) {
286        let mut inner_ref = self.inner.borrow_mut();
287        // TODO replace Option<Box<Something>> with Option<Borrow<Something>> in libcoap-rs to simplify API.
288        inner_ref.crypto_provider = provider;
289        if let Some(provider) = &mut inner_ref.crypto_provider {
290            inner_ref.crypto_default_info = Some(provider.provide_default_info());
291            let initial_data = coap_dtls_spsk_info_t {
292                hint: coap_bin_const_t {
293                    length: inner_ref.crypto_default_info.as_ref().unwrap().key.len(),
294                    s: inner_ref.crypto_default_info.as_ref().unwrap().key.as_ptr(),
295                },
296                key: coap_bin_const_t {
297                    length: inner_ref.crypto_default_info.as_ref().unwrap().key.len(),
298                    s: inner_ref.crypto_default_info.as_ref().unwrap().key.as_ptr(),
299                },
300            };
301            // SAFETY: raw context is valid, setup_data is of the right type and contains
302            // only valid information.
303            unsafe {
304                coap_context_set_psk2(
305                    inner_ref.raw_context,
306                    Box::into_raw(Box::new(coap_dtls_spsk_t {
307                        version: COAP_DTLS_SPSK_SETUP_VERSION as u8,
308                        reserved: [0; 7],
309                        validate_id_call_back: Some(dtls_server_id_callback),
310                        id_call_back_arg: inner_ref.raw_context as *mut c_void,
311                        validate_sni_call_back: {
312                            // Unsupported by TinyDTLS
313                            #[cfg(not(feature = "dtls_tinydtls"))]
314                            {
315                                Some(dtls_server_sni_callback)
316                            }
317                            #[cfg(feature = "dtls_tinydtls")]
318                            {
319                                None
320                            }
321                        },
322                        sni_call_back_arg: inner_ref.raw_context as *mut c_void,
323                        psk_info: initial_data,
324                    })),
325                )
326            };
327        }
328    }
329
330    /// Performs currently outstanding IO operations, waiting for a maximum duration of `timeout`.
331    ///
332    /// This is the function where most of the IO operations made using this library are actually
333    /// executed. It is recommended to call this function in a loop for as long as the CoAP context
334    /// is used.
335    pub fn do_io(&mut self, timeout: Option<Duration>) -> Result<Duration, IoProcessError> {
336        let mut inner_ref = self.inner.borrow_mut();
337        // Round up the duration if it is not a clean number of seconds.
338        let timeout = if let Some(timeout) = timeout {
339            let mut temp_timeout = u32::try_from(timeout.as_millis()).unwrap_or(u32::MAX);
340            if timeout.subsec_micros() > 0 || timeout.subsec_nanos() > 0 {
341                temp_timeout = temp_timeout.saturating_add(1);
342            }
343            temp_timeout
344        } else {
345            // If no timeout is set, wait indefinitely.
346            COAP_IO_WAIT
347        };
348        let raw_ctx_ptr = inner_ref.raw_context;
349        // Lend the current mutable reference to potential callers of CoapContext functions on the
350        // other side of the FFI barrier.
351        let lend_handle = self.inner.lend_ref_mut(&mut inner_ref);
352        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
353        // deleted until the CoapContextInner is dropped.
354        // Other raw structs used by libcoap are encapsulated in a way that they cannot be in use
355        // while in this function (considering that they are all !Send).
356        let spent_time = unsafe { coap_io_process(raw_ctx_ptr, timeout) };
357        // Demand the return of the lent handle, ensuring that the mutable reference is no longer
358        // used anywhere.
359        lend_handle.unlend();
360        // Check for errors.
361        if spent_time < 0 {
362            return Err(IoProcessError::Unknown);
363        }
364        // Return with duration of call.
365        Ok(Duration::from_millis(spent_time.unsigned_abs() as u64))
366    }
367
368    /// Return the duration that idle server-side sessions are kept alive if they are not referenced
369    /// or used anywhere else.
370    pub fn session_timeout(&self) -> Duration {
371        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
372        // deleted until the CoapContextInner is dropped.
373        let timeout = unsafe { coap_context_get_session_timeout(self.inner.borrow().raw_context) };
374        Duration::from_secs(timeout as u64)
375    }
376
377    /// Set the duration that idle server-side sessions are kept alive if they are not referenced or
378    /// used anywhere else.
379    ///
380    /// # Panics
381    /// Panics if the provided duration is too large to be provided to libcoap (larger than a
382    /// [libc::c_uint]).
383    pub fn set_session_timeout(&self, timeout: Duration) {
384        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
385        // deleted until the CoapContextInner is dropped.
386        unsafe {
387            coap_context_set_session_timeout(
388                self.inner.borrow_mut().raw_context,
389                timeout
390                    .as_secs()
391                    .try_into()
392                    .expect("provided session timeout is too large for libcoap (> u32::MAX)"),
393            )
394        }
395    }
396
397    /// Returns the maximum number of server-side sessions that can concurrently be in a handshake
398    /// state.
399    ///
400    /// If this number is exceeded, no new handshakes will be accepted.
401    pub fn max_handshake_sessions(&self) -> c_uint {
402        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
403        // deleted until the CoapContextInner is dropped.
404        unsafe { coap_context_get_max_handshake_sessions(self.inner.borrow().raw_context) }
405    }
406
407    /// Sets the maximum number of server-side sessions that can concurrently be in a handshake
408    /// state.
409    ///
410    /// If this number is exceeded, no new handshakes will be accepted.
411
412    pub fn set_max_handshake_sessions(&self, max_handshake_sessions: c_uint) {
413        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
414        // deleted until the CoapContextInner is dropped.
415        unsafe { coap_context_set_max_handshake_sessions(self.inner.borrow().raw_context, max_handshake_sessions) };
416    }
417
418    /// Returns the maximum number of idle server-side sessions for this context.
419    ///
420    /// If this number is exceeded, the oldest unreferenced session will be freed.
421    pub fn max_idle_sessions(&self) -> c_uint {
422        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
423        // deleted until the CoapContextInner is dropped.
424        unsafe { coap_context_get_max_idle_sessions(self.inner.borrow().raw_context) }
425    }
426
427    /// Sets the maximum number of idle server-side sessions for this context.
428    ///
429    /// If this number is exceeded, the oldest unreferenced session will be freed.
430    pub fn set_max_idle_sessions(&self, max_idle_sessions: c_uint) {
431        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
432        // deleted until the CoapContextInner is dropped.
433        unsafe { coap_context_set_max_idle_sessions(self.inner.borrow().raw_context, max_idle_sessions) };
434    }
435
436    /// Returns the maximum size for Capabilities and Settings Messages
437    ///
438    /// CSMs are used in CoAP over TCP as specified in
439    /// [RFC 8323, Section 5.3](https://datatracker.ietf.org/doc/html/rfc8323#section-5.3).
440    pub fn csm_max_message_size(&self) -> u32 {
441        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
442        // deleted until the CoapContextInner is dropped.
443        unsafe { coap_context_get_csm_max_message_size(self.inner.borrow().raw_context) }
444    }
445
446    /// Sets the maximum size for Capabilities and Settings Messages
447    ///
448    /// CSMs are used in CoAP over TCP as specified in
449    /// [RFC 8323, Section 5.3](https://datatracker.ietf.org/doc/html/rfc8323#section-5.3).
450    pub fn set_csm_max_message_size(&self, csm_max_message_size: u32) {
451        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
452        // deleted until the CoapContextInner is dropped.
453        unsafe { coap_context_set_csm_max_message_size(self.inner.borrow().raw_context, csm_max_message_size) };
454    }
455
456    /// Returns the timeout for Capabilities and Settings Messages
457    ///
458    /// CSMs are used in CoAP over TCP as specified in
459    /// [RFC 8323, Section 5.3](https://datatracker.ietf.org/doc/html/rfc8323#section-5.3).
460    pub fn csm_timeout(&self) -> Duration {
461        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
462        // deleted until the CoapContextInner is dropped.
463        let timeout = unsafe { coap_context_get_csm_timeout(self.inner.borrow().raw_context) };
464        Duration::from_secs(timeout as u64)
465    }
466
467    /// Sets the timeout for Capabilities and Settings Messages
468    ///
469    /// CSMs are used in CoAP over TCP as specified in
470    /// [RFC 8323, Section 5.3](https://datatracker.ietf.org/doc/html/rfc8323#section-5.3).
471    ///
472    /// # Panics
473    /// Panics if the provided timeout is too large for libcoap (> [u32::MAX]).
474    pub fn set_csm_timeout(&self, csm_timeout: Duration) {
475        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
476        // deleted until the CoapContextInner is dropped.
477        unsafe {
478            coap_context_set_csm_timeout(
479                self.inner.borrow().raw_context,
480                csm_timeout
481                    .as_secs()
482                    .try_into()
483                    .expect("provided session timeout is too large for libcoap (> u32::MAX)"),
484            )
485        };
486    }
487
488    /// Sets the number of seconds to wait before sending a CoAP keepalive message for idle
489    /// sessions.
490    ///
491    /// If the provided value is None, CoAP-level keepalive messages will be disabled.
492    ///
493    /// # Panics
494    /// Panics if the provided duration is too large to be provided to libcoap (larger than a
495    /// [libc::c_uint]).
496    pub fn set_keepalive(&self, timeout: Option<Duration>) {
497        // SAFETY: Properly initialized CoapContext always has a valid raw_context that is not
498        // deleted until the CoapContextInner is dropped.
499        unsafe {
500            coap_context_set_keepalive(
501                self.inner.borrow().raw_context,
502                timeout.map_or(0, |v| {
503                    v.as_secs()
504                        .try_into()
505                        .expect("provided keepalive time is too large for libcoap (> c_uint)")
506                }),
507            )
508        };
509    }
510
511    /// Provide a raw key for a given identity using the CoapContext's set server crypto provider.
512    ///
513    /// # Safety
514    /// Returned pointer should only be used if the context is borrowed.
515    /// Calling this function may override previous returned values of this function.
516    #[cfg(feature = "dtls")]
517    pub(crate) unsafe fn provide_raw_key_for_identity(
518        &self,
519        identity: &CoapCryptoPskIdentity,
520        session: &CoapServerSession,
521    ) -> Option<*const coap_bin_const_t> {
522        let inner_ref = &mut *self.inner.borrow_mut();
523        match inner_ref
524            .crypto_provider
525            .as_mut()
526            .map(|v| v.provide_key_for_identity(identity))
527        {
528            Some(CoapCryptoProviderResponse::UseNew(new_data)) => {
529                inner_ref.crypto_current_data = Some(CoapCryptoPskInfo {
530                    identity: Box::from(identity),
531                    key: new_data,
532                });
533                let curr_data = inner_ref.crypto_current_data.as_ref().unwrap();
534                curr_data.apply_to_spsk_info(&mut inner_ref.crypto_last_info_ref);
535                Some(&inner_ref.crypto_last_info_ref.key as *const coap_bin_const_t)
536            },
537            Some(CoapCryptoProviderResponse::UseCurrent) => {
538                if let Some(key) = session.psk_key() {
539                    inner_ref.crypto_current_data = Some(CoapCryptoPskInfo {
540                        identity: Box::new([]),
541                        key,
542                    });
543                    inner_ref
544                        .crypto_current_data
545                        .as_ref()
546                        .unwrap()
547                        .apply_to_spsk_info(&mut inner_ref.crypto_last_info_ref);
548                    Some(&inner_ref.crypto_last_info_ref.key)
549                } else if inner_ref.crypto_default_info.is_some() {
550                    inner_ref
551                        .crypto_default_info
552                        .as_ref()
553                        .unwrap()
554                        .apply_to_spsk_info(&mut inner_ref.crypto_last_info_ref);
555                    Some(&inner_ref.crypto_last_info_ref.key)
556                } else {
557                    None
558                }
559            },
560            None | Some(CoapCryptoProviderResponse::Unacceptable) => None,
561        }
562    }
563
564    /// Provide a hint for a given SNI name using the CoapContext's set server crypto provider.
565    ///
566    /// # Safety
567    /// Returned pointer should only be used if the context is borrowed.
568    /// Calling this function may override previous returned values of this function.
569    #[cfg(all(feature = "dtls"))]
570    pub(crate) unsafe fn provide_raw_hint_for_sni(&self, sni: &str) -> Option<*const coap_dtls_spsk_info_t> {
571        let inner_ref = &mut *self.inner.borrow_mut();
572        match inner_ref.crypto_provider.as_mut().map(|v| v.provide_hint_for_sni(sni)) {
573            Some(CoapCryptoProviderResponse::UseNew(new_info)) => {
574                inner_ref.crypto_sni_info_container.push(new_info);
575                inner_ref
576                    .crypto_sni_info_container
577                    .last()
578                    .unwrap()
579                    .apply_to_spsk_info(&mut inner_ref.crypto_last_info_ref);
580                Some(&inner_ref.crypto_last_info_ref as *const coap_dtls_spsk_info_t)
581            },
582            Some(CoapCryptoProviderResponse::UseCurrent) => {
583                if inner_ref.crypto_default_info.is_some() {
584                    inner_ref
585                        .crypto_default_info
586                        .as_ref()
587                        .unwrap()
588                        .apply_to_spsk_info(&mut inner_ref.crypto_last_info_ref);
589                    Some(&inner_ref.crypto_last_info_ref as *const coap_dtls_spsk_info_t)
590                } else {
591                    None
592                }
593            },
594            None | Some(CoapCryptoProviderResponse::Unacceptable) => None,
595        }
596    }
597
598    /// Returns a reference to the raw context contained in this struct.
599    ///
600    /// # Safety
601    /// In general, you should not do anything that would interfere with the safe functions of this
602    /// struct.
603    /// Most notably, this includes the following:
604    /// - Associating raw resources with the context and not removing them before the context is
605    ///   dropped (may cause segfaults on drop).
606    /// - Associating raw sessions that have a reference count != 0 when the CoapContext is dropped
607    ///   (will cause an abort on drop)
608    /// - Calling `coap_free_context()` on this context (for obvious reasons, this will probably
609    ///   cause a segfault if you don't immediately [std::mem::forget()] the CoapContext and never
610    ///   use anything related to the context again, but why would you do that?)  
611    // Kept here for consistency, even though it is unused.
612    #[allow(unused)]
613    pub(crate) unsafe fn as_raw_context(&self) -> &coap_context_t {
614        // SAFETY: raw_context is checked to be a valid pointer on struct instantiation, cannot be
615        // freed by anything outside of here (assuming the contract of this function is kept), and
616        // the default (elided) lifetimes are correct (the pointer is valid as long as the endpoint
617        // is).
618        &*self.inner.borrow().raw_context
619    }
620
621    /// Returns a mutable reference to the raw context contained in this struct.
622    ///
623    /// # Safety
624    /// In general, you should not do anything that would interfere with the safe functions of this
625    /// struct.
626    /// Most notably, this includes the following:
627    /// - Associating raw resources with the context and not removing them before the context is
628    ///   dropped (may cause segfaults on drop).
629    /// - Associating raw sessions that have a reference count != 0 when the CoapContext is dropped
630    ///   (will cause an abort on drop)
631    /// - Calling `coap_free_context()` on this context (for obvious reasons, this will probably
632    ///   cause a segfault if you don't immediately [std::mem::forget()] the CoapContext and never
633    ///   use anything related to the context again, but why would you do that?)  
634    pub(crate) unsafe fn as_mut_raw_context(&mut self) -> &mut coap_context_t {
635        // SAFETY: raw_context is checked to be a valid pointer on struct instantiation, cannot be
636        // freed by anything outside of here (assuming the contract of this function is kept), and
637        // the default (elided) lifetimes are correct (the pointer is valid as long as the endpoint
638        // is).
639        &mut *self.inner.borrow_mut().raw_context
640    }
641
642    // TODO coap_session_get_by_peer
643}
644
645impl Drop for CoapContextInner<'_> {
646    fn drop(&mut self) {
647        // Disable event handler before dropping, as we would otherwise need to lend our reference
648        // and because calling event handlers is probably undesired when we are already dropping
649        // the context.
650        // SAFETY: Validity of our raw context is always given for the lifetime of CoapContextInner
651        // unless coap_free_context() is called during a violation of the [as_mut_raw_context()] and
652        // [as_mut_context()] contracts (we check validity of the pointer on construction).
653        // Passing a NULL handler/None to coap_register_event_handler() is allowed as per the
654        // documentation.
655        unsafe {
656            coap_register_event_handler(self.raw_context, None);
657        }
658        for session in std::mem::take(&mut self.server_sessions).into_iter() {
659            session.drop_exclusively();
660        }
661        // Clear endpoints because coap_free_context() would free their underlying raw structs.
662        self.endpoints.clear();
663        // Extract reference to CoapContextInner from raw context and drop it.
664        // SAFETY: Value is set upon construction of the inner context and never deleted.
665        unsafe {
666            std::mem::drop(CoapLendableFfiWeakCell::<CoapContextInner>::from_raw_box(
667                coap_get_app_data(self.raw_context) as *mut CoapLendableFfiWeakCell<CoapContextInner>,
668            ))
669        }
670        // Attempt to regain sole ownership over all resources.
671        // As long as [CoapResource::into_inner] isn't used and we haven't given out owned
672        // CoapResource instances whose raw resource is attached to the raw context, this should
673        // never fail.
674        std::mem::take(&mut self.resources)
675            .into_iter()
676            .for_each(UntypedCoapResource::drop_inner_exclusive);
677        // SAFETY: We have already dropped all endpoints and contexts which could be freed alongside
678        // the actual context, and our raw context reference is valid (as long as the contracts of
679        // [as_mut_raw_context()] and [as_mut_context()] are fulfilled).
680        unsafe {
681            coap_free_context(self.raw_context);
682        }
683    }
684}