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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
// SPDX-License-Identifier: BSD-2-Clause
/*
 * lib.rs - Main library entry point for raw libcoap bindings.
 * This file is part of the libcoap-sys crate, see the README and LICENSE files for
 * more information and terms of use.
 * Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved.
 * See the README as well as the LICENSE file for more information.
 */

//! Auto-generated unsafe bindings to [libcoap](https://github.com/obgm/libcoap), generated using
//! [bindgen](https://crates.io/crates/bindgen).
//!
//! This crate allows direct (but unsafe) usage of the libcoap C library from Rust. The declarations
//! made in this library are generated automatically using bindgen, for further documentation on how
//! to use them, refer to the [libcoap documentation](https://libcoap.net/documentation.html).
//!
//! In most cases you probably want to use the safe wrapper provided by the libcoap crate (or
//! another coap library written in pure rust such as [coap-rs](https://github.com/covertness/coap-rs))
//! instead.
//!
//! Cargo Features
//! --------------
//! We currently define a number of features that affect the functionality provided by this wrapper
//! and required by the linked libcoap library.
//!
//! Features affecting functionality:
//! - `dtls`: Enable usage of DTLS for transport security. Supports a number of different backends.
//!
//!   Note that while not specified here due to limitations in Cargo's syntax, the DTLS feature
//!   depends on one of the DTLS backends being enabled, and failing to enable a DTLS backend will
//!   result in a build failure.
//!   
//!   If you are developing a library based on libcoap-sys and do not care about the DTLS backend,
//!   enable the dtls feature and let the user decide on the backend to use, either by
//!   re-exporting these features (see [the Cargo Book](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features))
//!   or by assuming that the user will use libcoap-sys as a dependency and enable the
//!   corresponding backend feature themselves, relying on Cargo's feature unification to enable
//!   it for your crate as well.
//!   
//!   Also note that the backends are **mutually exclusive** due to the C library having these
//!   backends as mutually exclusive features. If multiple backends are enabled (e.g. because
//!   multiple dependencies use libcoap-sys and use different backends), we select one based on
//!   the auto-detection order specified in [the libcoap configure script](https://github.com/obgm/libcoap/blob/develop/configure.ac#L494)
//!   (gnutls > openssl > mbedtls > tinydtls).
//!   - `dtls_backend_(openssl|gnutls|mbedtls|tinydtls)`: Enable the corresponding DTLS backend.
//!      
//!      Note that enabling the OpenSSL, GnuTLS, TinyDTLS or MbedTLS backend will also require the
//!      appropriate library to be available (hence the dependency on the corresponding sys-crate).
//!      The TinyDTLS backend is built using a vendored (and statically linked) version of TinyDTLS
//!      by default, see the tinydtls-sys crate for more info.
//!      Choosing a DTLS backend also means that the license terms of these libraries may apply to
//!      you. See the relevant parts of the [libcoap license file](https://github.com/obgm/libcoap/blob/develop/LICENSE)
//!      for more information.
//! - `tcp` (default): Enable CoAP over TCP support
//! - `async` (default): Enable async functionality.
//!   
//!   Note that this async functionality is not translated to Rust's async language functionality,
//!   but instead adds functionality to the underlying C library to allow for making asynchronous
//!   requests (i.e. function calls that return before the response has arrived).
//!
//!   Integrating libcoap into Rusts async language features is out of scope for this crate, but
//!   might be implemented later on in the libcoap (safe abstraction) crate.
//! - `server` (default): Enable code related to server functionality
//! - `client` (default): Enable code related to client functionality
//! - `epoll` (default): Allow the underlying C library to perform IO operations using epoll.
//!
//! Other features:
//! - `vendored` (default): Use a vendored version of libcoap instead of the system-provided one.
//!   Note that `vendored` implies `static`.
//! - `static` (default): Perform static linking to the libcoap C library.
//!
//! ### Note on features affecting functionality
//! The features that add or remove functionality do not change the generated bindings as libcoap's
//! headers (unlike the source files themselves) are not affected by the corresponding `#define`s.
//!
//! For library users that link to a shared version of libcoap, this means that the feature flags
//! do not have any effect and the supported features will correspond directly to the features
//! enabled during the build of the shared libcoap instance (using the configure-script).
//!
//! For users of the vendored version of libcoap (see the `vendored` feature), the supported
//! features of the vendored libcoap will be set to match the cargo features during build.

// Bindgen translates the C headers, clippy's and rustfmt's recommendations are not applicable here.
#![allow(clippy::all)]
#![allow(non_camel_case_types)]
#![allow(deref_nullptr)]
#![allow(non_snake_case)]

use libc::{epoll_event, fd_set, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t};

use crate::coap_pdu_type_t::COAP_MESSAGE_RST;

#[cfg(feature = "dtls_backend_gnutls")]
use gnutls_sys as _;
#[cfg(feature = "dtls_backend_mbedtls")]
use mbedtls_sys as _;
#[cfg(feature = "dtls_backend_openssl")]
use openssl_sys as _;
#[cfg(feature = "dtls_backend_tinydtls")]
use tinydtls_sys as _;

#[cfg(target_family = "windows")]
include!(concat!(env!("OUT_DIR"), "\\bindings.rs"));
#[cfg(not(target_family = "windows"))]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

#[inline]
pub unsafe fn coap_send_rst(
    session: *mut coap_session_t,
    request: *const coap_pdu_t,
    _type_: coap_pdu_type_t,
) -> coap_mid_t {
    coap_send_message_type(session, request, COAP_MESSAGE_RST)
}

#[cfg(test)]
mod tests {
    use std::{
        ffi::c_void,
        net::{SocketAddr, UdpSocket},
        os::raw::c_int,
        sync::{Arc, Barrier},
    };

    use libc::{in6_addr, in_addr, sa_family_t, size_t, AF_INET, AF_INET6};

    use super::*;
    use crate::{
        coap_pdu_code_t::{COAP_REQUEST_CODE_GET, COAP_RESPONSE_CODE_CONTENT},
        coap_proto_t::COAP_PROTO_UDP,
        coap_request_t::COAP_REQUEST_GET,
        coap_response_t::COAP_RESPONSE_OK,
    };

    const COAP_TEST_RESOURCE_URI: &str = "test";
    const COAP_TEST_RESOURCE_RESPONSE: &str = "Hello World!";

    /// Creates a coap_address_t from a &SocketAddr.
    fn coap_address_from_socketaddr(addr: &SocketAddr) -> coap_address_t {
        match addr {
            SocketAddr::V4(addr) => {
                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
                // to use a pointer instead.
                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
                unsafe {
                    let mut coap_addr = coap_address_t {
                        size: std::mem::size_of::<sockaddr_in>() as socklen_t,
                        addr: std::mem::zeroed(),
                    };
                    *coap_addr.addr.sin.as_mut() = sockaddr_in {
                        sin_family: AF_INET as sa_family_t,
                        sin_port: addr.port().to_be(),
                        sin_addr: in_addr {
                            s_addr: u32::from_ne_bytes(addr.ip().octets()),
                        },
                        sin_zero: Default::default(),
                    };
                    coap_addr
                }
            },
            SocketAddr::V6(addr) => {
                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
                // to use a pointer instead.
                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
                unsafe {
                    let mut coap_addr = coap_address_t {
                        size: std::mem::size_of::<sockaddr_in6>() as socklen_t,
                        addr: std::mem::zeroed(),
                    };
                    *coap_addr.addr.sin6.as_mut() = sockaddr_in6 {
                        sin6_family: AF_INET6 as sa_family_t,
                        sin6_port: addr.port().to_be(),
                        sin6_addr: in6_addr {
                            s6_addr: addr.ip().octets(),
                        },
                        sin6_flowinfo: addr.flowinfo(),
                        sin6_scope_id: addr.scope_id(),
                    };
                    coap_addr
                }
            },
        }
    }

    /// Response handler for the CoAP client/server test (client-side)
    /// # Safety
    /// Assumes all pointers to be valid and pointing to data structures containing valid data
    /// according to the specification of the method handler function provided in libcoap.
    /// Assumes that the application data in the CoAP context associated with the session is a
    /// pointer to a boolean specifying whether a successful request was received (will be set to
    /// true by this function).
    unsafe extern "C" fn test_resource_handler(
        _resource: *mut coap_resource_t,
        session: *mut coap_session_t,
        _incoming_pdu: *const coap_pdu_t,
        _query: *const coap_string_t,
        response_pdu: *mut coap_pdu_t,
    ) {
        let mut buf: [u8; 3] = [0; 3];
        coap_add_option(
            response_pdu,
            COAP_OPTION_CONTENT_TYPE as coap_option_num_t,
            coap_encode_var_safe(buf.as_mut_ptr(), buf.len(), COAP_MEDIATYPE_TEXT_PLAIN) as usize,
            buf.as_ptr(),
        );
        coap_add_data(
            response_pdu,
            COAP_TEST_RESOURCE_RESPONSE.len(),
            COAP_TEST_RESOURCE_RESPONSE.as_ptr(),
        );
        coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void);
        coap_pdu_set_code(response_pdu, COAP_RESPONSE_CODE_CONTENT);
    }

    /// Response handler for the CoAP client/server test (client-side)
    /// # Safety
    /// Assumes all pointers to be valid and pointing to data structures containing valid data
    /// according to the specification of the response handler function provided in libcoap.
    /// Assumes that the application data in the CoAP context associated with the session is a
    /// pointer to a boolean specifying whether a successful response was received (will be set to
    /// true by this function).
    unsafe extern "C" fn test_response_handler(
        session: *mut coap_session_t,
        _sent: *const coap_pdu_t,
        received: *const coap_pdu_t,
        _mid: coap_mid_t,
    ) -> coap_response_t {
        assert_eq!(coap_pdu_get_code(received), COAP_RESPONSE_CODE_CONTENT);
        let mut len: size_t = 0;
        let mut data: *const u8 = std::ptr::null();
        assert_ne!(coap_get_data(received, &mut len, &mut data), 0);
        let data = std::slice::from_raw_parts(data, len);

        assert_eq!(data, COAP_TEST_RESOURCE_RESPONSE.as_bytes());
        coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void);
        return COAP_RESPONSE_OK;
    }

    /// Creates a CoAP server that provides a single resource under COAP_TEST_RESOURCE_URI over the
    /// supplied socket
    fn run_coap_test_server(addr: &SocketAddr, barrier: Arc<Barrier>) {
        // SAFETY: Null pointer is a valid parameter here.
        let context = unsafe { coap_new_context(std::ptr::null()) };
        assert!(!context.is_null());

        let address: coap_address_t = coap_address_from_socketaddr(addr);

        // SAFETY: We asserted that context != null, listen_addr is a reference and can therefore not be null.
        let endpoint = unsafe { coap_new_endpoint(context, &address, coap_proto_t::COAP_PROTO_UDP) };
        assert!(!endpoint.is_null());

        // SAFETY: Since we use a string constant here, the arguments to the function are all valid.
        let uri = unsafe { coap_new_str_const(COAP_TEST_RESOURCE_URI.as_ptr(), COAP_TEST_RESOURCE_URI.len()) };
        assert!(!uri.is_null());

        // SAFETY: We just asserted that uri is valid, COAP_RESOURCE_FLAGS_RELEASE_URI is valid because we will not free the uri ourselves.
        let test_resource = unsafe { coap_resource_init(uri, COAP_RESOURCE_FLAGS_RELEASE_URI as c_int) };
        assert!(!test_resource.is_null());

        // SAFETY: We asserted that test_resource and context are valid, other pointers are always valid.
        // The fact that we cast a constant to a mutable pointer is not problematic, because neither we
        // nor libcoap ever mutate the pointer. This is necessary, because the underlying libcoap
        // struct allows for mutable pointers to be set there, so that applications can use this to
        // modify some application specific state.
        unsafe {
            coap_register_request_handler(test_resource, COAP_REQUEST_GET, Some(test_resource_handler));
            coap_add_resource(context, test_resource);
            coap_set_app_data(context, (&false) as *const bool as *mut c_void);
        }

        barrier.wait();
        loop {
            let time_spent_millis = unsafe { coap_io_process(context, 10000) };
            // SAFETY: We are the only ones setting or accessing this value, so we know it is a
            // const bool pointer (we have also set it to *false before and only ever set it to
            // *true afterwards).
            if unsafe { *(coap_get_app_data(context) as *const bool).as_ref().unwrap() } {
                break;
            }
            if time_spent_millis == -1 || time_spent_millis >= 10000 {
                panic!("Test timeout exceeded");
            }
        }
        // SAFETY: Context is not referenced outside this function, and handlers (which get the
        // context from libcoap) will not be called after the context is freed.
        // This also seems to free all resources.
        unsafe {
            coap_free_context(context);
            std::mem::drop(context);
            std::mem::drop(test_resource);
        }
    }

    /// Test case that creates a basic coap server and makes a request to it from a separate context
    #[test]
    fn test_coap_client_server_basic() {
        // This will give us a SocketAddress with a port in the local port range automatically
        // assigned by the operating system.
        // Because the UdpSocket goes out of scope, the Port will be free for usage by libcoap.
        // This seems to be the only portable way to get a port number assigned from the operating
        // system, which is necessary here because of potential parallelisation (we can't just use
        // the default CoAP port if multiple tests are run in parallel).
        // It is assumed here that after unbinding the temporary socket, the OS will not reassign
        // this port until we bind it again. This should work in most cases (unless we run on a
        // system with very few free ports), because at least Linux will not reuse port numbers
        // unless necessary, see https://unix.stackexchange.com/a/132524.
        let server_address = UdpSocket::bind("localhost:0")
            .expect("Failed to bind server socket")
            .local_addr()
            .expect("Failed to get server socket address");

        let preparation_barrier = Arc::new(Barrier::new(2));

        let server_address_clone = server_address.clone();
        let preparation_barrier_clone = preparation_barrier.clone();
        let server_thread_handle =
            std::thread::spawn(move || run_coap_test_server(&server_address_clone, preparation_barrier_clone));

        // SAFETY: Null pointer is a valid parameter here.
        let context = unsafe { coap_new_context(std::ptr::null()) };
        assert!(!context.is_null());

        preparation_barrier.wait();

        let server_address = coap_address_from_socketaddr(&server_address);

        // SAFETY: null pointer is valid argument for local_if, server_address is guaranteed to be
        // a correct value (conversion cannot fail), validity of context was asserted before.
        let client_session =
            unsafe { coap_new_client_session(context, std::ptr::null(), &server_address, COAP_PROTO_UDP) };

        // SAFETY: context and client_session were asserted to be valid.
        // Casting *const to *mut is fine because we don't mutate the value pointed to and the
        // pointer is not used by libcoap (the application data pointer is intended to be used by
        // client applications to store and/or modify their own data, which is why it is a mutable
        // pointer in the first place).
        // coap_request_pdu is asserted to be valid before it is used.
        unsafe {
            coap_register_response_handler(context, Some(test_response_handler));
            coap_set_app_data(context, (&false) as *const bool as *mut c_void);
            let coap_request_pdu =
                coap_new_pdu(coap_pdu_type_t::COAP_MESSAGE_NON, COAP_REQUEST_CODE_GET, client_session);
            assert!(!coap_request_pdu.is_null());
            assert_ne!(
                coap_add_option(
                    coap_request_pdu,
                    COAP_OPTION_URI_PATH as coap_option_num_t,
                    COAP_TEST_RESOURCE_URI.len(),
                    COAP_TEST_RESOURCE_URI.as_ptr(),
                ),
                0
            );
            assert_ne!(coap_send(client_session, coap_request_pdu), COAP_INVALID_MID);
        }

        loop {
            // SAFETY: context is asserted to be valid, no known side effects that would violate any guarantees
            let time_spent_millis = unsafe { coap_io_process(context, 10000) };
            // SAFETY: We are the only ones setting or accessing this value, so we know it is a
            // const bool pointer (we have also set it to *false before and only ever set it to
            // *true afterwards).
            if unsafe { *(coap_get_app_data(context) as *const bool).as_ref().unwrap() } {
                break;
            }
            if time_spent_millis == -1 || time_spent_millis >= 10000 {
                panic!("Test timeout exceeded");
            }
        }
        // SAFETY: Context is not references outside this function, and handlers (which get the
        // context from libcoap) will not be called after the context is freed.
        // This also seems to free all resources.
        unsafe {
            coap_free_context(context);
            std::mem::drop(context);
            std::mem::drop(client_session)
        }
        server_thread_handle.join().expect("Error waiting for server thread");
    }
}