1
// SPDX-License-Identifier: BSD-2-Clause
2
/*
3
 * Copyright © The libcoap-rs Contributors, all rights reserved.
4
 * This file is part of the libcoap-rs project, see the README file for
5
 * general information on this project and the NOTICE.md and LICENSE files
6
 * for information regarding copyright ownership and terms of use.
7
 *
8
 * session/server.rs - Types relating to client-side CoAP sessions.
9
 */
10

            
11
use std::cell::{Ref, RefMut};
12

            
13
use libcoap_sys::{
14
    coap_session_get_app_data, coap_session_get_type, coap_session_reference, coap_session_release,
15
    coap_session_set_app_data, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_CLIENT,
16
    coap_session_type_t_COAP_SESSION_TYPE_HELLO, coap_session_type_t_COAP_SESSION_TYPE_NONE,
17
    coap_session_type_t_COAP_SESSION_TYPE_SERVER,
18
};
19

            
20
use super::{CoapSessionCommon, CoapSessionInner, CoapSessionInnerProvider};
21
use crate::mem::{CoapFfiRcCell, DropInnerExclusively};
22

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

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

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

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

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

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

            
169
impl<'a> Drop for CoapServerSession<'a> {
170
629
    fn drop(&mut self) {
171
629
        let raw_session = self.inner.borrow_mut().inner.raw_session;
172
629
        // Decrease libcoap-internal reference counter for raw session so that we don't leak memory
173
629
        // if we previously incremented the reference count.
174
629
        if self.ref_counted {
175
            // SAFETY: raw_session is always valid for the lifetime of this object.
176
629
            unsafe {
177
629
                coap_session_release(raw_session);
178
629
            }
179
        }
180
629
    }
181
}
182

            
183
impl<'a> CoapSessionInnerProvider<'a> for CoapServerSession<'a> {
184
466
    fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>> {
185
466
        Ref::map(self.inner.borrow(), |v| &v.inner)
186
466
    }
187

            
188
233
    fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>> {
189
233
        RefMut::map(self.inner.borrow_mut(), |v| &mut v.inner)
190
233
    }
191
}
192

            
193
impl<'a, T: CoapSessionCommon<'a>> PartialEq<T> for CoapServerSession<'_> {
194
    fn eq(&self, other: &T) -> bool {
195
        // SAFETY: Pointers are only compared, never accessed.
196
        self.if_index() == other.if_index()
197
            && unsafe { self.raw_session() == other.raw_session() }
198
            && self.addr_local() == other.addr_local()
199
            && self.addr_remote() == other.addr_remote()
200
    }
201
}
202

            
203
impl Eq for CoapServerSession<'_> {}
204

            
205
impl Drop for CoapServerSessionInner<'_> {
206
268
    fn drop(&mut self) {
207
268
        unsafe {
208
268
            let app_data = coap_session_get_app_data(self.inner.raw_session);
209
268
            assert!(!app_data.is_null());
210
268
            std::mem::drop(CoapFfiRcCell::<CoapServerSessionInner>::raw_ptr_to_weak(app_data));
211
268
        }
212
268
    }
213
}