1use std::{
12 any::Any,
13 borrow::BorrowMut,
14 cell::{Ref, RefMut},
15 collections::{HashMap, VecDeque},
16 marker::PhantomData,
17 net::{SocketAddr, ToSocketAddrs},
18 rc::Rc,
19};
20
21use libcoap_sys::{
22 coap_context_t, coap_fixed_point_t, coap_mid_t, coap_new_message_id, coap_pdu_get_token, coap_pdu_t,
23 coap_response_t, coap_response_t_COAP_RESPONSE_FAIL, coap_response_t_COAP_RESPONSE_OK, coap_send,
24 coap_session_get_ack_random_factor, coap_session_get_ack_timeout, coap_session_get_addr_local,
25 coap_session_get_addr_remote, coap_session_get_ifindex, coap_session_get_max_retransmit, coap_session_get_proto,
26 coap_session_get_state, coap_session_get_type, coap_session_init_token, coap_session_max_pdu_size,
27 coap_session_new_token, coap_session_send_ping, coap_session_set_ack_random_factor, coap_session_set_ack_timeout,
28 coap_session_set_max_retransmit, coap_session_set_mtu, coap_session_state_t,
29 coap_session_state_t_COAP_SESSION_STATE_CONNECTING, coap_session_state_t_COAP_SESSION_STATE_CSM,
30 coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED, coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE,
31 coap_session_state_t_COAP_SESSION_STATE_NONE, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_CLIENT,
32 coap_session_type_t_COAP_SESSION_TYPE_HELLO, coap_session_type_t_COAP_SESSION_TYPE_NONE,
33 coap_session_type_t_COAP_SESSION_TYPE_SERVER,
34};
35#[cfg(feature = "dtls-psk")]
36use libcoap_sys::{coap_session_get_psk_hint, coap_session_get_psk_identity, coap_session_get_psk_key};
37
38use self::sealed::{CoapSessionCommonInternal, CoapSessionInnerProvider};
39pub use self::{client::CoapClientSession, server::CoapServerSession};
40use crate::{
41 error::{MessageConversionError, SessionGetAppDataError},
42 message::{request::CoapRequest, response::CoapResponse, CoapMessage, CoapMessageCommon},
43 protocol::CoapToken,
44 types::{CoapAddress, CoapMessageId, CoapProtocol, IfIndex, MaxRetransmit},
45};
46
47pub mod client;
48
49pub mod server;
50
51#[repr(u32)]
53pub enum CoapSessionState {
54 None = coap_session_state_t_COAP_SESSION_STATE_NONE as u32,
55 Connecting = coap_session_state_t_COAP_SESSION_STATE_CONNECTING as u32,
56 Handshake = coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE as u32,
57 Csm = coap_session_state_t_COAP_SESSION_STATE_CSM as u32,
58 Established = coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED as u32,
59}
60
61impl From<coap_session_state_t> for CoapSessionState {
62 fn from(raw_state: coap_session_state_t) -> Self {
63 #[allow(non_upper_case_globals)]
66 match raw_state {
67 coap_session_state_t_COAP_SESSION_STATE_NONE => CoapSessionState::None,
68 coap_session_state_t_COAP_SESSION_STATE_CONNECTING => CoapSessionState::Connecting,
69 coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE => CoapSessionState::Handshake,
70 coap_session_state_t_COAP_SESSION_STATE_CSM => CoapSessionState::Csm,
71 coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED => CoapSessionState::Established,
72 _ => unreachable!("unknown session state added"),
73 }
74 }
75}
76
77mod sealed {
78 use super::*;
79
80 pub trait CoapSessionInnerProvider<'a> {
83 fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>>;
85
86 fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>>;
88 }
89
90 pub trait CoapSessionCommonInternal<'a>: CoapSessionInnerProvider<'a> {
95 fn add_response(&self, pdu: CoapResponse) {
96 let token = pdu.token();
97 if let Some(token) = token {
98 if self.inner_ref().received_responses.contains_key(token) {
99 self.inner_mut()
100 .received_responses
101 .get_mut(token)
102 .unwrap()
103 .push_back(pdu);
104 }
105 }
106 }
107 }
108
109 impl<'a, T: CoapSessionInnerProvider<'a>> CoapSessionCommonInternal<'a> for T {}
110}
111
112pub trait CoapSessionCommon<'a>: CoapSessionCommonInternal<'a> {
114 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 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 fn clear_app_data(&self) {
132 let mut inner = self.inner_mut();
133 inner.app_data = None;
134 }
135
136 fn ack_random_factor(&self) -> (u16, u16) {
141 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 fn set_ack_random_factor(&self, integer_part: u16, fractional_part: u16) {
148 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 fn ack_timeout(&self) -> (u16, u16) {
165 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 fn set_ack_timeout(&self, integer_part: u16, fractional_part: u16) {
172 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 fn addr_local(&self) -> SocketAddr {
186 CoapAddress::from(unsafe {
187 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 fn addr_remote(&self) -> SocketAddr {
201 CoapAddress::from(unsafe {
202 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 fn if_index(&self) -> IfIndex {
216 unsafe { coap_session_get_ifindex(self.inner_ref().raw_session) }
218 }
219
220 fn max_retransmit(&self) -> MaxRetransmit {
222 unsafe { coap_session_get_max_retransmit(self.inner_ref().raw_session) }
224 }
225
226 fn set_max_retransmit(&mut self, value: MaxRetransmit) {
228 unsafe { coap_session_set_max_retransmit(self.inner_ref().raw_session, value) }
230 }
231
232 fn proto(&self) -> CoapProtocol {
234 unsafe { coap_session_get_proto(self.inner_ref().raw_session) }.into()
236 }
237
238 #[cfg(feature = "dtls-psk")]
240 fn psk_hint(&self) -> Option<Box<[u8]>> {
241 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 #[cfg(feature = "dtls-psk")]
251 fn psk_identity(&self) -> Option<Box<[u8]>> {
252 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 #[cfg(feature = "dtls-psk")]
262 fn psk_key(&self) -> Option<Box<[u8]>> {
263 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 #[must_use = "getting the current session state without using it is a no-op"]
273 fn state(&self) -> CoapSessionState {
274 unsafe { coap_session_get_state(self.inner_ref().raw_session).into() }
276 }
277
278 fn init_token(&self, token: &[u8; 8]) {
283 unsafe { coap_session_init_token(self.inner_mut().raw_session, token.len(), token.as_ptr()) }
285 }
286
287 fn max_pdu_size(&self) -> usize {
289 unsafe { coap_session_max_pdu_size(self.inner_ref().raw_session) }
291 }
292
293 fn set_mtu(&self, mtu: u32) {
295 unsafe { coap_session_set_mtu(self.inner_mut().raw_session, mtu) }
297 }
298
299 fn next_message_id(&self) -> CoapMessageId {
301 unsafe { coap_new_message_id(self.inner_mut().raw_session) as CoapMessageId }
303 }
304
305 fn new_token(&mut self, token: &mut [u8; 8]) -> usize {
307 let mut length = 8;
308 unsafe { coap_session_new_token(self.inner_mut().raw_session, &mut length, token.as_mut_ptr()) }
310 length
311 }
312
313 fn send_ping(&mut self) -> CoapMessageId {
315 unsafe { coap_session_send_ping(self.inner_mut().raw_session) }
317 }
318
319 fn send<P: Into<CoapMessage>>(&self, pdu: P) -> Result<CoapMessageId, MessageConversionError> {
324 let raw_pdu = pdu.into().into_raw_pdu(self)?;
325 let mid = unsafe { coap_send(self.inner_mut().raw_session, raw_pdu) };
328 Ok(mid)
329 }
330
331 fn send_request(&self, mut req: CoapRequest) -> Result<CoapRequestHandle, MessageConversionError> {
338 if req.token().is_none() {
339 let mut token_len = libcoap_sys::COAP_TOKEN_DEFAULT_MAX as usize;
340 let mut token_tmp: Vec<u8> = vec![0; token_len];
341 unsafe {
343 coap_session_new_token(self.inner_mut().raw_session, &mut token_len, token_tmp.as_mut_ptr());
344 }
345 req.set_token(Some(Vec::from(&token_tmp[0..token_len])))
346 }
347 let token: Box<[u8]> = Box::from(req.token().unwrap());
348 if req.mid().is_none() {
349 req.set_mid(Some(self.next_message_id()))
350 }
351 self.inner_mut()
352 .received_responses
353 .insert(token.clone(), VecDeque::new());
354 self.send(req.into_message()).map(|v| CoapRequestHandle::new(v, token))
355 }
356
357 fn poll_handle(&self, handle: &CoapRequestHandle) -> std::collections::vec_deque::IntoIter<CoapResponse> {
366 self.inner_mut()
367 .received_responses
368 .insert(handle.token.clone(), VecDeque::new())
369 .expect("Attempted to poll handle that does not refer to a valid token")
370 .into_iter()
371 }
372
373 fn is_waiting_for_token(&self, token: &CoapToken) -> bool {
375 self.inner_ref().received_responses.contains_key(token)
376 }
377
378 fn remove_handle(&self, handle: CoapRequestHandle) {
383 self.inner_mut().received_responses.remove(&handle.token);
384 }
385
386 unsafe fn raw_session_mut(&self) -> *mut coap_session_t {
392 self.inner_mut().raw_session
393 }
394
395 unsafe fn raw_session(&self) -> *const coap_session_t {
401 self.inner_ref().raw_session
402 }
403}
404
405impl<'a, T: CoapSessionCommonInternal<'a>> CoapSessionCommon<'a> for T {}
406
407#[derive(Debug)]
408pub enum CoapSession<'a> {
414 Client(CoapClientSession<'a>),
415
416 Server(CoapServerSession<'a>),
417}
418
419impl<'a> CoapSessionInnerProvider<'a> for CoapSession<'a> {
420 fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>> {
421 match self {
422 CoapSession::Client(sess) => sess.inner_ref(),
423
424 CoapSession::Server(sess) => sess.inner_ref(),
425 }
426 }
427
428 fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>> {
429 match self {
430 CoapSession::Client(sess) => sess.inner_mut(),
431
432 CoapSession::Server(sess) => sess.inner_mut(),
433 }
434 }
435}
436
437impl<'a> CoapSession<'a> {
438 pub(crate) unsafe fn from_raw(raw_session: *mut coap_session_t) -> CoapSession<'a> {
457 assert!(!raw_session.is_null(), "provided raw session was null");
458 let raw_session_type = coap_session_get_type(raw_session);
459
460 #[allow(non_upper_case_globals)]
463 match raw_session_type {
464 coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
465
466 coap_session_type_t_COAP_SESSION_TYPE_CLIENT => CoapClientSession::from_raw(raw_session).into(),
467
468 coap_session_type_t_COAP_SESSION_TYPE_SERVER | coap_session_type_t_COAP_SESSION_TYPE_HELLO => {
469 CoapServerSession::from_raw(raw_session).into()
470 },
471 _ => unreachable!("unknown session type"),
472 }
473 }
474}
475
476impl<'a> From<CoapClientSession<'a>> for CoapSession<'a> {
477 fn from(session: CoapClientSession<'a>) -> Self {
478 CoapSession::Client(session)
479 }
480}
481
482impl<'a> From<CoapServerSession<'a>> for CoapSession<'a> {
483 fn from(session: CoapServerSession<'a>) -> Self {
484 CoapSession::Server(session)
485 }
486}
487
488impl PartialEq for CoapSession<'_> {
489 fn eq(&self, other: &Self) -> bool {
490 match self {
491 CoapSession::Client(cli_sess) => cli_sess.eq(other),
492
493 CoapSession::Server(srv_sess) => srv_sess.eq(other),
494 }
495 }
496}
497
498impl Eq for CoapSession<'_> {}
499
500#[derive(Debug)]
505#[doc(hidden)]
506pub struct CoapSessionInner<'a> {
507 raw_session: *mut coap_session_t,
508 app_data: Option<Rc<dyn Any>>,
509 received_responses: HashMap<CoapToken, VecDeque<CoapResponse>>,
510 _context_lifetime_marker: PhantomData<&'a coap_context_t>,
511}
512
513impl CoapSessionInner<'_> {
514 pub(crate) unsafe fn new<'a>(raw_session: *mut coap_session_t) -> CoapSessionInner<'a> {
529 CoapSessionInner {
530 raw_session,
531 app_data: None,
532 received_responses: HashMap::new(),
533 _context_lifetime_marker: Default::default(),
534 }
535 }
536}
537
538#[derive(Debug, Clone, PartialEq, Eq, Hash)]
543pub struct CoapRequestHandle {
544 _mid: CoapMessageId,
545 token: CoapToken,
546}
547
548impl CoapRequestHandle {
549 fn new<T: Into<Box<[u8]>>>(mid: CoapMessageId, token: T) -> CoapRequestHandle {
550 CoapRequestHandle {
551 _mid: mid,
552 token: token.into(),
553 }
554 }
555}
556
557pub(crate) unsafe extern "C" fn session_response_handler(
559 session: *mut coap_session_t,
560 _sent: *const coap_pdu_t,
561 received: *const coap_pdu_t,
562 _id: coap_mid_t,
563) -> coap_response_t {
564 let mut session = CoapSession::from_raw(session);
565 let client = session.borrow_mut();
566 let raw_token = coap_pdu_get_token(received);
568 let token: CoapToken = CoapToken::from(std::slice::from_raw_parts(raw_token.s, raw_token.length));
569 if !client.is_waiting_for_token(&token) {
570 return coap_response_t_COAP_RESPONSE_FAIL;
571 }
572 if let Ok(message) = CoapMessage::from_raw_pdu(received).and_then(CoapResponse::from_message) {
573 client.add_response(message);
574 coap_response_t_COAP_RESPONSE_OK
575 } else {
576 coap_response_t_COAP_RESPONSE_FAIL
577 }
578}