1use std::borrow::BorrowMut;
11use std::cell::{Ref, RefMut};
12use std::{
13 any::Any,
14 collections::{HashMap, VecDeque},
15 marker::PhantomData,
16 net::{SocketAddr, ToSocketAddrs},
17 rc::Rc,
18};
19
20use rand::Rng;
21
22use libcoap_sys::{
23 coap_context_t, coap_fixed_point_t, coap_mid_t, coap_new_message_id, coap_pdu_get_token, coap_pdu_t,
24 coap_response_t, coap_send, coap_session_get_ack_random_factor, coap_session_get_ack_timeout,
25 coap_session_get_addr_local, coap_session_get_addr_remote, coap_session_get_ifindex,
26 coap_session_get_max_retransmit, coap_session_get_proto, coap_session_get_psk_hint, coap_session_get_psk_identity,
27 coap_session_get_psk_key, coap_session_get_state, coap_session_get_type, coap_session_init_token,
28 coap_session_max_pdu_size, coap_session_new_token, coap_session_send_ping, coap_session_set_ack_random_factor,
29 coap_session_set_ack_timeout, coap_session_set_max_retransmit, coap_session_set_mtu, coap_session_state_t,
30 coap_session_t, coap_session_type_t,
31};
32
33#[cfg(feature = "dtls")]
34use crate::crypto::{CoapCryptoPskData, CoapCryptoPskIdentity};
35
36use crate::message::request::CoapRequest;
37use crate::message::response::CoapResponse;
38use crate::{
39 error::{MessageConversionError, SessionGetAppDataError},
40 message::{CoapMessage, CoapMessageCommon},
41 protocol::CoapToken,
42 types::{CoapAddress, CoapMessageId, CoapProtocol, IfIndex, MaxRetransmit},
43};
44
45pub use self::client::CoapClientSession;
46
47pub(self) use self::sealed::{CoapSessionCommonInternal, CoapSessionInnerProvider};
48
49pub use self::server::CoapServerSession;
50
51pub mod client;
52
53pub mod server;
54
55#[repr(u32)]
57pub enum CoapSessionState {
58 None = coap_session_state_t::COAP_SESSION_STATE_NONE as u32,
59 Connecting = coap_session_state_t::COAP_SESSION_STATE_CONNECTING as u32,
60 Handshake = coap_session_state_t::COAP_SESSION_STATE_HANDSHAKE as u32,
61 Csm = coap_session_state_t::COAP_SESSION_STATE_CSM as u32,
62 Established = coap_session_state_t::COAP_SESSION_STATE_ESTABLISHED as u32,
63}
64
65impl From<coap_session_state_t> for CoapSessionState {
66 fn from(raw_state: coap_session_state_t) -> Self {
67 match raw_state {
68 coap_session_state_t::COAP_SESSION_STATE_NONE => CoapSessionState::None,
69 coap_session_state_t::COAP_SESSION_STATE_CONNECTING => CoapSessionState::Connecting,
70 coap_session_state_t::COAP_SESSION_STATE_HANDSHAKE => CoapSessionState::Handshake,
71 coap_session_state_t::COAP_SESSION_STATE_CSM => CoapSessionState::Csm,
72 coap_session_state_t::COAP_SESSION_STATE_ESTABLISHED => CoapSessionState::Established,
73 _ => unreachable!("unknown session state added"),
74 }
75 }
76}
77
78mod sealed {
79 use super::*;
80
81 pub trait CoapSessionInnerProvider<'a> {
84 fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>>;
86
87 fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>>;
89 }
90
91 pub trait CoapSessionCommonInternal<'a>: CoapSessionInnerProvider<'a> {
96 fn add_response(&self, pdu: CoapResponse) {
97 let token = pdu.token();
98 if let Some(token) = token {
99 if self.inner_ref().received_responses.contains_key(token) {
100 self.inner_mut()
101 .received_responses
102 .get_mut(token)
103 .unwrap()
104 .push_back(pdu);
105 }
106 }
107 }
108 }
109
110 impl<'a, T: CoapSessionInnerProvider<'a>> CoapSessionCommonInternal<'a> for T {}
111}
112
113pub trait CoapSessionCommon<'a>: CoapSessionCommonInternal<'a> {
115 fn app_data<T: Any>(&self) -> Result<Option<Rc<T>>, SessionGetAppDataError> {
117 self.inner_ref()
118 .app_data
119 .as_ref()
120 .map(|v| v.clone().downcast().map_err(|_v| SessionGetAppDataError::WrongType))
121 .transpose()
122 }
123
124 fn set_app_data<T: 'static + Any>(&self, value: Option<T>) {
126 let mut inner = self.inner_mut();
127 let new_box: Option<Rc<dyn Any>> = value.map(|v| Rc::new(v) as Rc<dyn Any>);
128 inner.app_data = new_box;
129 }
130
131 fn clear_app_data(&self) {
133 let mut inner = self.inner_mut();
134 inner.app_data = None;
135 }
136
137 fn ack_random_factor(&self) -> (u16, u16) {
142 let random_factor = unsafe { coap_session_get_ack_random_factor(self.inner_ref().raw_session) };
144 (random_factor.integer_part, random_factor.fractional_part)
145 }
146
147 fn set_ack_random_factor(&self, integer_part: u16, fractional_part: u16) {
149 unsafe {
151 coap_session_set_ack_random_factor(
152 self.inner_mut().raw_session,
153 coap_fixed_point_t {
154 integer_part,
155 fractional_part,
156 },
157 )
158 };
159 }
160
161 fn ack_timeout(&self) -> (u16, u16) {
166 let random_factor = unsafe { coap_session_get_ack_timeout(self.inner_ref().raw_session) };
168 (random_factor.integer_part, random_factor.fractional_part)
169 }
170
171 fn set_ack_timeout(&self, integer_part: u16, fractional_part: u16) {
173 unsafe {
175 coap_session_set_ack_timeout(
176 self.inner_ref().raw_session,
177 coap_fixed_point_t {
178 integer_part,
179 fractional_part,
180 },
181 )
182 };
183 }
184
185 fn addr_local(&self) -> SocketAddr {
187 CoapAddress::from(unsafe {
188 coap_session_get_addr_local(self.inner_ref().raw_session)
191 .as_ref()
192 .unwrap()
193 })
194 .to_socket_addrs()
195 .unwrap()
196 .next()
197 .unwrap()
198 }
199
200 fn addr_remote(&self) -> SocketAddr {
202 CoapAddress::from(unsafe {
203 coap_session_get_addr_remote(self.inner_ref().raw_session)
206 .as_ref()
207 .unwrap()
208 })
209 .to_socket_addrs()
210 .unwrap()
211 .next()
212 .unwrap()
213 }
214
215 fn if_index(&self) -> IfIndex {
217 unsafe { coap_session_get_ifindex(self.inner_ref().raw_session) }
219 }
220
221 fn max_retransmit(&self) -> MaxRetransmit {
223 unsafe { coap_session_get_max_retransmit(self.inner_ref().raw_session) }
225 }
226
227 fn set_max_retransmit(&mut self, value: MaxRetransmit) {
229 unsafe { coap_session_set_max_retransmit(self.inner_ref().raw_session, value) }
231 }
232
233 fn proto(&self) -> CoapProtocol {
235 unsafe { coap_session_get_proto(self.inner_ref().raw_session) }.into()
237 }
238
239 #[cfg(feature = "dtls")]
241 fn psk_hint(&self) -> Option<Box<CoapCryptoPskIdentity>> {
242 unsafe {
244 coap_session_get_psk_hint(self.inner_ref().raw_session)
245 .as_ref()
246 .map(|raw_hint| Box::from(std::slice::from_raw_parts(raw_hint.s, raw_hint.length)))
247 }
248 }
249
250 #[cfg(feature = "dtls")]
252 fn psk_identity(&self) -> Option<Box<CoapCryptoPskIdentity>> {
253 unsafe {
255 coap_session_get_psk_identity(self.inner_ref().raw_session)
256 .as_ref()
257 .map(|raw_hint| Box::from(std::slice::from_raw_parts(raw_hint.s, raw_hint.length)))
258 }
259 }
260
261 #[cfg(feature = "dtls")]
263 fn psk_key(&self) -> Option<Box<CoapCryptoPskData>> {
264 unsafe {
266 coap_session_get_psk_key(self.inner_ref().raw_session)
267 .as_ref()
268 .map(|raw_hint| Box::from(std::slice::from_raw_parts(raw_hint.s, raw_hint.length)))
269 }
270 }
271
272 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]) {
284 unsafe { coap_session_init_token(self.inner_mut().raw_session, token.len(), token.as_ptr()) }
286 }
287
288 fn max_pdu_size(&self) -> usize {
290 unsafe { coap_session_max_pdu_size(self.inner_ref().raw_session) }
292 }
293
294 fn set_mtu(&self, mtu: u32) {
296 unsafe { coap_session_set_mtu(self.inner_mut().raw_session, mtu) }
298 }
299
300 fn next_message_id(&self) -> CoapMessageId {
302 unsafe { coap_new_message_id(self.inner_mut().raw_session) as CoapMessageId }
304 }
305
306 fn new_token(&mut self, token: &mut [u8; 8]) -> usize {
308 let mut length = 8;
309 unsafe { coap_session_new_token(self.inner_mut().raw_session, &mut length, token.as_mut_ptr()) }
311 length
312 }
313
314 fn send_ping(&mut self) -> CoapMessageId {
316 unsafe { coap_session_send_ping(self.inner_mut().raw_session) }
318 }
319
320 fn send<P: Into<CoapMessage>>(&self, pdu: P) -> Result<CoapMessageId, MessageConversionError> {
325 let raw_pdu = pdu.into().into_raw_pdu(self)?;
326 let mid = unsafe { coap_send(self.inner_mut().raw_session, raw_pdu) };
329 Ok(mid)
330 }
331
332 fn send_request(&self, mut req: CoapRequest) -> Result<CoapRequestHandle, MessageConversionError> {
339 if req.token().is_none() {
340 let mut token_tmp: Vec<u8> = vec![0; 8];
341 rand::thread_rng().fill(&mut token_tmp[0..8]);
342 req.set_token(Some(token_tmp))
343 }
344 let token: Box<[u8]> = Box::from(req.token().unwrap());
345 if req.mid().is_none() {
346 req.set_mid(Some(self.next_message_id()))
347 }
348 self.inner_mut()
349 .received_responses
350 .insert(token.clone(), VecDeque::new());
351 self.send(req.into_message()).map(|v| CoapRequestHandle::new(v, token))
352 }
353
354 fn poll_handle(&self, handle: &CoapRequestHandle) -> std::collections::vec_deque::IntoIter<CoapResponse> {
362 self.inner_mut()
363 .received_responses
364 .insert(handle.token.clone(), VecDeque::new())
365 .expect("Attempted to poll handle that does not refer to a valid token")
366 .into_iter()
367 }
368
369 fn is_waiting_for_token(&self, token: &CoapToken) -> bool {
371 self.inner_ref().received_responses.contains_key(token)
372 }
373
374 fn remove_handle(&self, handle: CoapRequestHandle) {
379 self.inner_mut().received_responses.remove(&handle.token);
380 }
381
382 unsafe fn raw_session_mut(&self) -> *mut coap_session_t {
388 self.inner_mut().raw_session
389 }
390
391 unsafe fn raw_session(&self) -> *const coap_session_t {
397 self.inner_ref().raw_session
398 }
399}
400
401impl<'a, T: CoapSessionCommonInternal<'a>> CoapSessionCommon<'a> for T {}
402
403#[derive(Debug)]
404pub enum CoapSession<'a> {
410 Client(CoapClientSession<'a>),
411
412 Server(CoapServerSession<'a>),
413}
414
415impl<'a> CoapSessionInnerProvider<'a> for CoapSession<'a> {
416 fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>> {
417 match self {
418 CoapSession::Client(sess) => sess.inner_ref(),
419
420 CoapSession::Server(sess) => sess.inner_ref(),
421 }
422 }
423
424 fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>> {
425 match self {
426 CoapSession::Client(sess) => sess.inner_mut(),
427
428 CoapSession::Server(sess) => sess.inner_mut(),
429 }
430 }
431}
432
433impl<'a> CoapSession<'a> {
434 pub(crate) unsafe fn from_raw(raw_session: *mut coap_session_t) -> CoapSession<'a> {
449 assert!(!raw_session.is_null(), "provided raw session was null");
450 let raw_session_type = coap_session_get_type(raw_session);
451 match raw_session_type {
452 coap_session_type_t::COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
453
454 coap_session_type_t::COAP_SESSION_TYPE_CLIENT => CoapClientSession::from_raw(raw_session).into(),
455
456 coap_session_type_t::COAP_SESSION_TYPE_SERVER | coap_session_type_t::COAP_SESSION_TYPE_HELLO => {
457 CoapServerSession::from_raw(raw_session).into()
458 },
459 _ => unreachable!("unknown session type"),
460 }
461 }
462}
463
464impl<'a> From<CoapClientSession<'a>> for CoapSession<'a> {
465 fn from(session: CoapClientSession<'a>) -> Self {
466 CoapSession::Client(session)
467 }
468}
469
470impl<'a> From<CoapServerSession<'a>> for CoapSession<'a> {
471 fn from(session: CoapServerSession<'a>) -> Self {
472 CoapSession::Server(session)
473 }
474}
475
476impl PartialEq for CoapSession<'_> {
477 fn eq(&self, other: &Self) -> bool {
478 match self {
479 CoapSession::Client(cli_sess) => cli_sess.eq(other),
480
481 CoapSession::Server(srv_sess) => srv_sess.eq(other),
482 }
483 }
484}
485
486impl Eq for CoapSession<'_> {}
487
488#[derive(Debug)]
493#[doc(hidden)]
494pub struct CoapSessionInner<'a> {
495 raw_session: *mut coap_session_t,
496 app_data: Option<Rc<dyn Any>>,
497 received_responses: HashMap<CoapToken, VecDeque<CoapResponse>>,
498 _context_lifetime_marker: PhantomData<&'a coap_context_t>,
499}
500
501impl CoapSessionInner<'_> {
502 pub(crate) unsafe fn new<'a>(raw_session: *mut coap_session_t) -> CoapSessionInner<'a> {
517 CoapSessionInner {
518 raw_session,
519 app_data: None,
520 received_responses: HashMap::new(),
521 _context_lifetime_marker: Default::default(),
522 }
523 }
524}
525
526#[derive(Debug, Clone, PartialEq, Eq, Hash)]
531pub struct CoapRequestHandle {
532 _mid: CoapMessageId,
533 token: CoapToken,
534}
535
536impl CoapRequestHandle {
537 fn new<T: Into<Box<[u8]>>>(mid: CoapMessageId, token: T) -> CoapRequestHandle {
538 CoapRequestHandle {
539 _mid: mid,
540 token: token.into(),
541 }
542 }
543}
544
545#[allow(improper_ctypes_definitions)]
547pub(crate) unsafe extern "C" fn session_response_handler(
548 session: *mut coap_session_t,
549 _sent: *const coap_pdu_t,
550 received: *const coap_pdu_t,
551 _id: coap_mid_t,
552) -> coap_response_t {
553 let mut session = CoapSession::from_raw(session);
554 let client = session.borrow_mut();
555 let raw_token = coap_pdu_get_token(received);
557 let token: CoapToken = CoapToken::from(std::slice::from_raw_parts(raw_token.s, raw_token.length));
558 if !client.is_waiting_for_token(&token) {
559 return coap_response_t::COAP_RESPONSE_FAIL;
560 }
561 if let Ok(message) = CoapMessage::from_raw_pdu(received).and_then(CoapResponse::from_message) {
562 client.add_response(message);
563 coap_response_t::COAP_RESPONSE_OK
564 } else {
565 coap_response_t::COAP_RESPONSE_FAIL
566 }
567}