1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * session/server.rs - Types relating to client-side CoAP sessions.
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
use std::cell::{Ref, RefMut};
11

            
12
use libcoap_sys::{
13
    coap_session_get_app_data, coap_session_get_type, coap_session_reference, coap_session_release,
14
    coap_session_set_app_data, coap_session_t, coap_session_type_t,
15
};
16

            
17
use super::{CoapSessionCommon, CoapSessionInner, CoapSessionInnerProvider};
18
use crate::mem::{CoapFfiRcCell, DropInnerExclusively};
19

            
20
impl DropInnerExclusively for CoapServerSession<'_> {
21
233
    fn drop_exclusively(self) {
22
233
        let sess_ref = self.inner.clone();
23
233
        std::mem::drop(self);
24
233
        sess_ref.drop_exclusively();
25
233
    }
26
}
27

            
28
/// Representation of a server-side CoAP session.
29
#[derive(Debug, Clone)]
30
pub struct CoapServerSession<'a> {
31
    /// Inner part of this server-side session
32
    /// A weak version of this reference is stored inside of the user/app data pointer in the
33
    /// raw session struct so that it can be passed through the FFI barrier.
34
    inner: CoapFfiRcCell<CoapServerSessionInner<'a>>,
35
    ref_counted: bool,
36
}
37

            
38
#[derive(Debug)]
39
/// Inner part of a server-side CoAP session.
40
struct CoapServerSessionInner<'a> {
41
    inner: CoapSessionInner<'a>,
42
}
43

            
44
impl CoapServerSession<'_> {
45
    /// Creates a CoapServerSession from a raw session.
46
    ///
47
    /// This function will increment the libcoap-internal reference counter for the session by one.
48
    /// Dropping the CoapServerSession will then decrement it again.
49
    ///
50
    /// # Panics
51
    /// Panics if the given pointer is a null pointer or the raw session is not a server-side
52
    /// session.
53
    ///
54
    /// # Safety
55
    /// The provided pointer must be valid for the entire (here arbitrarily chosen) lifetime of the
56
    /// CoapServerSession<'a>, most notably the program will abort if the [CoapContext] is dropped
57
    /// before this session is.
58
    /// The existing value in the `app_data` field of the raw session will be overridden.
59
    /// Make sure that this is actually okay to do so — most importantly, no other [CoapSession] may
60
    /// already be stored there.
61
    ///
62
    /// If you wish to restore an existing [CoapSession] from its raw counterpart, use
63
    /// [from_raw()](CoapServerSession::from_raw) instead.
64
268
    pub(crate) unsafe fn initialize_raw<'a>(raw_session: *mut coap_session_t) -> CoapServerSession<'a> {
65
268
        assert!(!raw_session.is_null(), "provided raw session was null");
66
268
        let raw_session_type = coap_session_get_type(raw_session);
67
268
        let inner = CoapSessionInner::new(raw_session);
68
268
        let session_inner = match raw_session_type {
69
            coap_session_type_t::COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
70
            coap_session_type_t::COAP_SESSION_TYPE_CLIENT => {
71
                panic!("attempted to create server session from raw client session")
72
            },
73
105
            coap_session_type_t::COAP_SESSION_TYPE_SERVER => CoapServerSessionInner { inner },
74
163
            coap_session_type_t::COAP_SESSION_TYPE_HELLO => CoapServerSessionInner { inner },
75
            _ => unreachable!("unknown session type"),
76
        };
77
268
        let session_ref = CoapFfiRcCell::new(session_inner);
78
268
        coap_session_set_app_data(raw_session, session_ref.create_raw_weak());
79
268
        // Increase libcoap-internal reference counter for raw session so that it doesn't get freed
80
268
        // as long as this CoapServerSession instance exists.
81
268
        coap_session_reference(raw_session);
82
268
        CoapServerSession {
83
268
            inner: session_ref,
84
268
            ref_counted: true,
85
268
        }
86
268
    }
87

            
88
    /// Restores a [CoapServerSession] from its raw counterpart.
89
    ///
90
    /// Make sure that this struct cannot outlive the [CoapContext] its session originates from, as
91
    /// the lifetime cannot be inferred by the compiler and dropping the context will panic/abort if
92
    /// the inner session is still referenced anywhere else.
93
    ///
94
    /// This function will increment the libcoap-internal reference counter for the session by one.
95
    /// Dropping the CoapServerSession will then decrement it again.
96
    ///
97
    /// # Panics
98
    /// Panics if the provided raw session pointer or its app_data field is null or the raw session
99
    /// is not a server-side session.
100
    ///
101
    /// # Safety
102
    /// The provided pointer must be valid for the entire lifetime of this struct.
103
    /// The provided session's app data must be a valid argument to
104
    /// [`CoapFfiRcCell<CoapServerSessionInner>::clone_raw_rc`](CoapFfiRcCell::clone_raw_rc).
105
361
    pub(crate) unsafe fn from_raw<'a>(raw_session: *mut coap_session_t) -> CoapServerSession<'a> {
106
361
        let mut session = Self::from_raw_without_refcount(raw_session);
107
361
        coap_session_reference(raw_session);
108
361
        session.ref_counted = true;
109
361
        session
110
361
    }
111

            
112
    /// Restores a [CoapServerSession] from its raw counterpart without increasing its reference
113
    /// counter (useful if acquiring libcoap's global lock is undesired).
114
    ///
115
    /// Make sure that this struct cannot outlive the [CoapContext] its session originates from, as
116
    /// the lifetime cannot be inferred by the compiler and dropping the context will panic/abort if
117
    /// the inner session is still referenced anywhere else.
118
    ///
119
    /// In addition to the above, you must also ensure that the session will not be cleaned up by
120
    /// libcoap in the meantime, as the reference counter is not increased while constructing the
121
    /// instance.
122
    ///
123
    /// This function will increment the libcoap-internal reference counter for the session by one.
124
    /// Dropping the CoapServerSession will then decrement it again.
125
    ///
126
    /// # Panics
127
    ///
128
    /// Panics if the provided raw session pointer or its app_data field is null or the raw session
129
    /// is not a server-side session.
130
    ///
131
    /// # Safety
132
    /// The provided pointer must be valid for the entire lifetime of this struct.
133
    ///
134
    /// This also implies that libcoap *must not* clean up this session during the lifetime of this
135
    /// struct, which could happen at any time if the libcoap context is not locked.
136
    ///
137
    /// The provided session's app data must be a valid argument to
138
    /// [`CoapFfiRcCell<CoapServerSessionInner>::clone_raw_rc`](CoapFfiRcCell::clone_raw_rc).
139
361
    pub(crate) unsafe fn from_raw_without_refcount<'a>(raw_session: *mut coap_session_t) -> CoapServerSession<'a> {
140
361
        assert!(!raw_session.is_null(), "provided raw session was null");
141
361
        let raw_session_type = coap_session_get_type(raw_session);
142
361
        match raw_session_type {
143
            coap_session_type_t::COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
144
            coap_session_type_t::COAP_SESSION_TYPE_SERVER | coap_session_type_t::COAP_SESSION_TYPE_HELLO => {
145
361
                let raw_app_data_ptr = coap_session_get_app_data(raw_session);
146
361
                assert!(!raw_app_data_ptr.is_null(), "provided raw session has no app data");
147
361
                CoapServerSession {
148
361
                    inner: CoapFfiRcCell::clone_raw_rc(raw_app_data_ptr),
149
361
                    ref_counted: false,
150
361
                }
151
            },
152
            coap_session_type_t::COAP_SESSION_TYPE_CLIENT => {
153
                panic!("attempted to create CoapServerSession from raw client session")
154
            },
155
            _ => unreachable!("unknown session type"),
156
        }
157
361
    }
158
}
159

            
160
impl<'a> Drop for CoapServerSession<'a> {
161
629
    fn drop(&mut self) {
162
629
        let raw_session = self.inner.borrow_mut().inner.raw_session;
163
629
        // Decrease libcoap-internal reference counter for raw session so that we don't leak memory
164
629
        // if we previously incremented the reference count.
165
629
        if self.ref_counted {
166
            // SAFETY: raw_session is always valid for the lifetime of this object.
167
629
            unsafe {
168
629
                coap_session_release(raw_session);
169
629
            }
170
        }
171
629
    }
172
}
173

            
174
impl<'a> CoapSessionInnerProvider<'a> for CoapServerSession<'a> {
175
466
    fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>> {
176
466
        Ref::map(self.inner.borrow(), |v| &v.inner)
177
466
    }
178

            
179
233
    fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>> {
180
233
        RefMut::map(self.inner.borrow_mut(), |v| &mut v.inner)
181
233
    }
182
}
183

            
184
impl<'a, T: CoapSessionCommon<'a>> PartialEq<T> for CoapServerSession<'_> {
185
    fn eq(&self, other: &T) -> bool {
186
        // SAFETY: Pointers are only compared, never accessed.
187
        self.if_index() == other.if_index()
188
            && unsafe { self.raw_session() == other.raw_session() }
189
            && self.addr_local() == other.addr_local()
190
            && self.addr_remote() == other.addr_remote()
191
    }
192
}
193

            
194
impl Eq for CoapServerSession<'_> {}
195

            
196
impl Drop for CoapServerSessionInner<'_> {
197
268
    fn drop(&mut self) {
198
268
        unsafe {
199
268
            let app_data = coap_session_get_app_data(self.inner.raw_session);
200
268
            assert!(!app_data.is_null());
201
268
            std::mem::drop(CoapFfiRcCell::<CoapServerSessionInner>::raw_ptr_to_weak(app_data));
202
268
        }
203
268
    }
204
}