libcoap_rs/session/
server.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
// SPDX-License-Identifier: BSD-2-Clause
/*
 * Copyright © The libcoap-rs Contributors, all rights reserved.
 * This file is part of the libcoap-rs project, see the README file for
 * general information on this project and the NOTICE.md and LICENSE files
 * for information regarding copyright ownership and terms of use.
 *
 * session/server.rs - Types relating to client-side CoAP sessions.
 */

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

use libcoap_sys::{
    coap_session_get_app_data, coap_session_get_type, coap_session_reference, coap_session_release,
    coap_session_set_app_data, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_CLIENT,
    coap_session_type_t_COAP_SESSION_TYPE_HELLO, coap_session_type_t_COAP_SESSION_TYPE_NONE,
    coap_session_type_t_COAP_SESSION_TYPE_SERVER,
};

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

impl DropInnerExclusively for CoapServerSession<'_> {
    fn drop_exclusively(self) {
        let sess_ref = self.inner.clone();
        std::mem::drop(self);
        sess_ref.drop_exclusively();
    }
}

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

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

impl CoapServerSession<'_> {
    /// Creates a CoapServerSession from a raw session.
    ///
    /// This function will increment the libcoap-internal reference counter for the session by one.
    /// Dropping the CoapServerSession will then decrement it again.
    ///
    /// # Panics
    /// Panics if the given pointer is a null pointer or the raw session is not a server-side
    /// session.
    ///
    /// # Safety
    /// The provided pointer must be valid for the entire (here arbitrarily chosen) lifetime of the
    /// CoapServerSession<'a>, most notably the program will abort if the [CoapContext] is dropped
    /// before this session is.
    /// The existing value in the `app_data` field of the raw session will be overridden.
    /// Make sure that this is actually okay to do so — most importantly, no other [CoapSession] may
    /// already be stored there.
    ///
    /// If you wish to restore an existing [CoapSession] from its raw counterpart, use
    /// [from_raw()](CoapServerSession::from_raw) instead.
    pub(crate) unsafe fn initialize_raw<'a>(raw_session: *mut coap_session_t) -> CoapServerSession<'a> {
        assert!(!raw_session.is_null(), "provided raw session was null");
        let raw_session_type = coap_session_get_type(raw_session);
        let inner = CoapSessionInner::new(raw_session);
        // Variant names are named by bindgen, we have no influence on this.
        // Ref: https://github.com/rust-lang/rust/issues/39371
        #[allow(non_upper_case_globals)]
        let session_inner = match raw_session_type {
            coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
            coap_session_type_t_COAP_SESSION_TYPE_CLIENT => {
                panic!("attempted to create server session from raw client session")
            },
            coap_session_type_t_COAP_SESSION_TYPE_SERVER => CoapServerSessionInner { inner },
            coap_session_type_t_COAP_SESSION_TYPE_HELLO => CoapServerSessionInner { inner },
            _ => unreachable!("unknown session type"),
        };
        let session_ref = CoapFfiRcCell::new(session_inner);
        coap_session_set_app_data(raw_session, session_ref.create_raw_weak());
        // Increase libcoap-internal reference counter for raw session so that it doesn't get freed
        // as long as this CoapServerSession instance exists.
        coap_session_reference(raw_session);
        CoapServerSession {
            inner: session_ref,
            ref_counted: true,
        }
    }

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

    /// Restores a [CoapServerSession] from its raw counterpart without increasing its reference
    /// counter (useful if acquiring libcoap's global lock is undesired).
    ///
    /// Make sure that this struct cannot outlive the [CoapContext] its session originates from, as
    /// the lifetime cannot be inferred by the compiler and dropping the context will panic/abort if
    /// the inner session is still referenced anywhere else.
    ///
    /// In addition to the above, you must also ensure that the session will not be cleaned up by
    /// libcoap in the meantime, as the reference counter is not increased while constructing the
    /// instance.
    ///
    /// This function will increment the libcoap-internal reference counter for the session by one.
    /// Dropping the CoapServerSession will then decrement it again.
    ///
    /// # Panics
    ///
    /// Panics if the provided raw session pointer or its app_data field is null or the raw session
    /// is not a server-side session.
    ///
    /// # Safety
    /// The provided pointer must be valid for the entire lifetime of this struct.
    ///
    /// This also implies that libcoap *must not* clean up this session during the lifetime of this
    /// struct, which could happen at any time if the libcoap context is not locked.
    ///
    /// The provided session's app data must be a valid argument to
    /// [`CoapFfiRcCell<CoapServerSessionInner>::clone_raw_rc`](CoapFfiRcCell::clone_raw_rc).
    pub(crate) unsafe fn from_raw_without_refcount<'a>(raw_session: *mut coap_session_t) -> CoapServerSession<'a> {
        assert!(!raw_session.is_null(), "provided raw session was null");
        let raw_session_type = coap_session_get_type(raw_session);
        // Variant names are named by bindgen, we have no influence on this.
        // Ref: https://github.com/rust-lang/rust/issues/39371
        #[allow(non_upper_case_globals)]
        match raw_session_type {
            coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"),
            coap_session_type_t_COAP_SESSION_TYPE_SERVER | coap_session_type_t_COAP_SESSION_TYPE_HELLO => {
                let raw_app_data_ptr = coap_session_get_app_data(raw_session);
                assert!(!raw_app_data_ptr.is_null(), "provided raw session has no app data");
                CoapServerSession {
                    inner: CoapFfiRcCell::clone_raw_rc(raw_app_data_ptr),
                    ref_counted: false,
                }
            },
            coap_session_type_t_COAP_SESSION_TYPE_CLIENT => {
                panic!("attempted to create CoapServerSession from raw client session")
            },
            _ => unreachable!("unknown session type"),
        }
    }
}

impl<'a> Drop for CoapServerSession<'a> {
    fn drop(&mut self) {
        let raw_session = self.inner.borrow_mut().inner.raw_session;
        // Decrease libcoap-internal reference counter for raw session so that we don't leak memory
        // if we previously incremented the reference count.
        if self.ref_counted {
            // SAFETY: raw_session is always valid for the lifetime of this object.
            unsafe {
                coap_session_release(raw_session);
            }
        }
    }
}

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

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

impl<'a, T: CoapSessionCommon<'a>> PartialEq<T> for CoapServerSession<'_> {
    fn eq(&self, other: &T) -> bool {
        // SAFETY: Pointers are only compared, never accessed.
        self.if_index() == other.if_index()
            && unsafe { self.raw_session() == other.raw_session() }
            && self.addr_local() == other.addr_local()
            && self.addr_remote() == other.addr_remote()
    }
}

impl Eq for CoapServerSession<'_> {}

impl Drop for CoapServerSessionInner<'_> {
    fn drop(&mut self) {
        unsafe {
            let app_data = coap_session_get_app_data(self.inner.raw_session);
            assert!(!app_data.is_null());
            std::mem::drop(CoapFfiRcCell::<CoapServerSessionInner>::raw_ptr_to_weak(app_data));
        }
    }
}