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_mcast_set_hops, coap_mid_t, coap_new_message_id, coap_pdu_get_token,
23 coap_pdu_t, coap_response_t, coap_response_t_COAP_RESPONSE_FAIL, coap_response_t_COAP_RESPONSE_OK, coap_send,
24 coap_session_get_ack_random_factor, coap_session_get_ack_timeout, coap_session_get_addr_local,
25 coap_session_get_addr_remote, coap_session_get_ifindex, coap_session_get_max_retransmit, coap_session_get_proto,
26 coap_session_get_state, coap_session_get_type, coap_session_init_token, coap_session_max_pdu_size,
27 coap_session_new_token, coap_session_send_ping, coap_session_set_ack_random_factor, coap_session_set_ack_timeout,
28 coap_session_set_max_retransmit, coap_session_set_mtu, coap_session_state_t,
29 coap_session_state_t_COAP_SESSION_STATE_CONNECTING, coap_session_state_t_COAP_SESSION_STATE_CSM,
30 coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED, coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE,
31 coap_session_state_t_COAP_SESSION_STATE_NONE, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_CLIENT,
32 coap_session_type_t_COAP_SESSION_TYPE_HELLO, coap_session_type_t_COAP_SESSION_TYPE_NONE,
33 coap_session_type_t_COAP_SESSION_TYPE_SERVER,
34};
35#[cfg(feature = "dtls-psk")]
36use libcoap_sys::{coap_session_get_psk_hint, coap_session_get_psk_identity, coap_session_get_psk_key};
37
38use self::sealed::{CoapSessionCommonInternal, CoapSessionInnerProvider};
39pub use self::{client::CoapClientSession, server::CoapServerSession};
40use crate::{
41 error::{MessageConversionError, MulticastHopLimitError, SessionGetAppDataError},
42 message::{request::CoapRequest, response::CoapResponse, CoapMessage, CoapMessageCommon},
43 protocol::CoapToken,
44 types::{CoapAddress, CoapMessageId, CoapProtocol, IfIndex, MaxRetransmit},
45};
46
47pub mod client;
48
49pub mod server;
50
51#[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 set_mcast_hops_limit(&self, hops: usize) -> Result<(), MulticastHopLimitError> {
301 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 fn next_message_id(&self) -> CoapMessageId {
314 unsafe { coap_new_message_id(self.inner_mut().raw_session) as CoapMessageId }
316 }
317
318 fn new_token(&mut self, token: &mut [u8; 8]) -> usize {
320 let mut length = 8;
321 unsafe { coap_session_new_token(self.inner_mut().raw_session, &mut length, token.as_mut_ptr()) }
323 length
324 }
325
326 fn send_ping(&mut self) -> CoapMessageId {
328 unsafe { coap_session_send_ping(self.inner_mut().raw_session) }
330 }
331
332 fn send<P: Into<CoapMessage>>(&self, pdu: P) -> Result<CoapMessageId, MessageConversionError> {
337 let raw_pdu = pdu.into().into_raw_pdu(self)?;
338 let mid = unsafe { coap_send(self.inner_mut().raw_session, raw_pdu) };
341 Ok(mid)
342 }
343
344 fn send_request(&self, mut req: CoapRequest) -> Result<CoapRequestHandle, MessageConversionError> {
351 if req.token().is_none() {
352 let mut token_len = libcoap_sys::COAP_TOKEN_DEFAULT_MAX as usize;
353 let mut token_tmp: Vec<u8> = vec![0; token_len];
354 unsafe {
356 coap_session_new_token(self.inner_mut().raw_session, &mut token_len, token_tmp.as_mut_ptr());
357 }
358 req.set_token(Some(Vec::from(&token_tmp[0..token_len])))
359 }
360 let token: Box<[u8]> = Box::from(req.token().unwrap());
361 if req.mid().is_none() {
362 req.set_mid(Some(self.next_message_id()))
363 }
364 self.inner_mut()
365 .received_responses
366 .insert(token.clone(), VecDeque::new());
367 self.send(req.into_message()).map(|v| CoapRequestHandle::new(v, token))
368 }
369
370 fn poll_handle(&self, handle: &CoapRequestHandle) -> std::collections::vec_deque::IntoIter<CoapResponse> {
379 self.inner_mut()
380 .received_responses
381 .insert(handle.token.clone(), VecDeque::new())
382 .expect("Attempted to poll handle that does not refer to a valid token")
383 .into_iter()
384 }
385
386 fn is_waiting_for_token(&self, token: &CoapToken) -> bool {
388 self.inner_ref().received_responses.contains_key(token)
389 }
390
391 fn remove_handle(&self, handle: CoapRequestHandle) {
396 self.inner_mut().received_responses.remove(&handle.token);
397 }
398
399 unsafe fn raw_session_mut(&self) -> *mut coap_session_t {
405 self.inner_mut().raw_session
406 }
407
408 unsafe fn raw_session(&self) -> *const coap_session_t {
414 self.inner_ref().raw_session
415 }
416}
417
418impl<'a, T: CoapSessionCommonInternal<'a>> CoapSessionCommon<'a> for T {}
419
420#[derive(Debug)]
421pub enum CoapSession<'a> {
427 Client(CoapClientSession<'a>),
428
429 Server(CoapServerSession<'a>),
430}
431
432impl<'a> CoapSessionInnerProvider<'a> for CoapSession<'a> {
433 fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>> {
434 match self {
435 CoapSession::Client(sess) => sess.inner_ref(),
436
437 CoapSession::Server(sess) => sess.inner_ref(),
438 }
439 }
440
441 fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>> {
442 match self {
443 CoapSession::Client(sess) => sess.inner_mut(),
444
445 CoapSession::Server(sess) => sess.inner_mut(),
446 }
447 }
448}
449
450impl<'a> CoapSession<'a> {
451 pub(crate) unsafe fn from_raw(raw_session: *mut coap_session_t) -> CoapSession<'a> {
470 assert!(!raw_session.is_null(), "provided raw session was null");
471 let raw_session_type = coap_session_get_type(raw_session);
472
473 #[allow(non_upper_case_globals)]
476 match raw_session_type {
477 coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
478
479 coap_session_type_t_COAP_SESSION_TYPE_CLIENT => CoapClientSession::from_raw(raw_session).into(),
480
481 coap_session_type_t_COAP_SESSION_TYPE_SERVER | coap_session_type_t_COAP_SESSION_TYPE_HELLO => {
482 CoapServerSession::from_raw(raw_session).into()
483 },
484 _ => unreachable!("unknown session type"),
485 }
486 }
487}
488
489impl<'a> From<CoapClientSession<'a>> for CoapSession<'a> {
490 fn from(session: CoapClientSession<'a>) -> Self {
491 CoapSession::Client(session)
492 }
493}
494
495impl<'a> From<CoapServerSession<'a>> for CoapSession<'a> {
496 fn from(session: CoapServerSession<'a>) -> Self {
497 CoapSession::Server(session)
498 }
499}
500
501impl PartialEq for CoapSession<'_> {
502 fn eq(&self, other: &Self) -> bool {
503 match self {
504 CoapSession::Client(cli_sess) => cli_sess.eq(other),
505
506 CoapSession::Server(srv_sess) => srv_sess.eq(other),
507 }
508 }
509}
510
511impl Eq for CoapSession<'_> {}
512
513#[derive(Debug)]
518#[doc(hidden)]
519pub struct CoapSessionInner<'a> {
520 raw_session: *mut coap_session_t,
521 app_data: Option<Rc<dyn Any>>,
522 received_responses: HashMap<CoapToken, VecDeque<CoapResponse>>,
523 _context_lifetime_marker: PhantomData<&'a coap_context_t>,
524}
525
526impl CoapSessionInner<'_> {
527 pub(crate) unsafe fn new<'a>(raw_session: *mut coap_session_t) -> CoapSessionInner<'a> {
542 CoapSessionInner {
543 raw_session,
544 app_data: None,
545 received_responses: HashMap::new(),
546 _context_lifetime_marker: Default::default(),
547 }
548 }
549}
550
551#[derive(Debug, Clone, PartialEq, Eq, Hash)]
556pub struct CoapRequestHandle {
557 _mid: CoapMessageId,
558 token: CoapToken,
559}
560
561impl CoapRequestHandle {
562 fn new<T: Into<Box<[u8]>>>(mid: CoapMessageId, token: T) -> CoapRequestHandle {
563 CoapRequestHandle {
564 _mid: mid,
565 token: token.into(),
566 }
567 }
568}
569
570pub(crate) unsafe extern "C" fn session_response_handler(
572 session: *mut coap_session_t,
573 _sent: *const coap_pdu_t,
574 received: *const coap_pdu_t,
575 _id: coap_mid_t,
576) -> coap_response_t {
577 let mut session = CoapSession::from_raw(session);
578 let client = session.borrow_mut();
579 let raw_token = coap_pdu_get_token(received);
581 let token: CoapToken = CoapToken::from(std::slice::from_raw_parts(raw_token.s, raw_token.length));
582 if !client.is_waiting_for_token(&token) {
583 return coap_response_t_COAP_RESPONSE_FAIL;
584 }
585 if let Ok(message) = CoapMessage::from_raw_pdu(received).and_then(CoapResponse::from_message) {
586 client.add_response(message);
587 coap_response_t_COAP_RESPONSE_OK
588 } else {
589 coap_response_t_COAP_RESPONSE_FAIL
590 }
591}