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}