libcoap_sys/
lib.rs

1// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * lib.rs - Main library entry point for raw libcoap bindings.
4 * This file is part of the libcoap-sys 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//! Auto-generated unsafe bindings to [libcoap](https://github.com/obgm/libcoap), generated using
11//! [bindgen](https://crates.io/crates/bindgen).
12//!
13//! This crate allows direct (but unsafe) usage of the libcoap C library from Rust. The declarations
14//! made in this library are generated automatically using bindgen, for further documentation on how
15//! to use them, refer to the [libcoap documentation](https://libcoap.net/documentation.html).
16//!
17//! In most cases you probably want to use the safe wrapper provided by the libcoap crate (or
18//! another coap library written in pure rust such as [coap-rs](https://github.com/covertness/coap-rs))
19//! instead.
20//!
21//! Cargo Features
22//! --------------
23//! We currently define a number of features that affect the functionality provided by this wrapper
24//! and required by the linked libcoap library.
25//!
26//! Features affecting functionality:
27//! - `dtls`: Enable usage of DTLS for transport security. Supports a number of different backends.
28//!
29//!   Note that while not specified here due to limitations in Cargo's syntax, the DTLS feature
30//!   depends on one of the DTLS backends being enabled, and failing to enable a DTLS backend will
31//!   result in a build failure.
32//!   
33//!   If you are developing a library based on libcoap-sys and do not care about the DTLS backend,
34//!   enable the dtls feature and let the user decide on the backend to use, either by
35//!   re-exporting these features (see [the Cargo Book](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features))
36//!   or by assuming that the user will use libcoap-sys as a dependency and enable the
37//!   corresponding backend feature themselves, relying on Cargo's feature unification to enable
38//!   it for your crate as well.
39//!   
40//!   Also note that the backends are **mutually exclusive** due to the C library having these
41//!   backends as mutually exclusive features. If multiple backends are enabled (e.g. because
42//!   multiple dependencies use libcoap-sys and use different backends), we select one based on
43//!   the auto-detection order specified in [the libcoap configure script](https://github.com/obgm/libcoap/blob/develop/configure.ac#L494)
44//!   (gnutls > openssl > mbedtls > tinydtls).
45//!   - `dtls_backend_(openssl|gnutls|mbedtls|tinydtls)`: Enable the corresponding DTLS backend.
46//!      
47//!      Note that enabling the OpenSSL, GnuTLS, TinyDTLS or MbedTLS backend will also require the
48//!      appropriate library to be available (hence the dependency on the corresponding sys-crate).
49//!      The TinyDTLS backend is built using a vendored (and statically linked) version of TinyDTLS
50//!      by default, see the tinydtls-sys crate for more info.
51//!      Choosing a DTLS backend also means that the license terms of these libraries may apply to
52//!      you. See the relevant parts of the [libcoap license file](https://github.com/obgm/libcoap/blob/develop/LICENSE)
53//!      for more information.
54//! - `tcp` (default): Enable CoAP over TCP support
55//! - `async` (default): Enable async functionality.
56//!   
57//!   Note that this async functionality is not translated to Rust's async language functionality,
58//!   but instead adds functionality to the underlying C library to allow for making asynchronous
59//!   requests (i.e. function calls that return before the response has arrived).
60//!
61//!   Integrating libcoap into Rusts async language features is out of scope for this crate, but
62//!   might be implemented later on in the libcoap (safe abstraction) crate.
63//! - `server` (default): Enable code related to server functionality
64//! - `client` (default): Enable code related to client functionality
65//! - `epoll` (default): Allow the underlying C library to perform IO operations using epoll.
66//!
67//! Other features:
68//! - `vendored` (default): Use a vendored version of libcoap instead of the system-provided one.
69//!   Note that `vendored` implies `static`.
70//! - `static` (default): Perform static linking to the libcoap C library.
71//!
72//! ### Note on features affecting functionality
73//! The features that add or remove functionality do not change the generated bindings as libcoap's
74//! headers (unlike the source files themselves) are not affected by the corresponding `#define`s.
75//!
76//! For library users that link to a shared version of libcoap, this means that the feature flags
77//! do not have any effect and the supported features will correspond directly to the features
78//! enabled during the build of the shared libcoap instance (using the configure-script).
79//!
80//! For users of the vendored version of libcoap (see the `vendored` feature), the supported
81//! features of the vendored libcoap will be set to match the cargo features during build.
82
83// Bindgen translates the C headers, clippy's and rustfmt's recommendations are not applicable here.
84#![allow(clippy::all)]
85#![allow(non_camel_case_types)]
86#![allow(deref_nullptr)]
87#![allow(non_snake_case)]
88
89use libc::{epoll_event, fd_set, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t};
90
91use crate::coap_pdu_type_t::COAP_MESSAGE_RST;
92
93#[cfg(feature = "dtls_backend_gnutls")]
94use gnutls_sys as _;
95#[cfg(feature = "dtls_backend_mbedtls")]
96use mbedtls_sys as _;
97#[cfg(feature = "dtls_backend_openssl")]
98use openssl_sys as _;
99#[cfg(feature = "dtls_backend_tinydtls")]
100use tinydtls_sys as _;
101
102#[cfg(target_family = "windows")]
103include!(concat!(env!("OUT_DIR"), "\\bindings.rs"));
104#[cfg(not(target_family = "windows"))]
105include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
106
107#[inline]
108pub unsafe fn coap_send_rst(
109    session: *mut coap_session_t,
110    request: *const coap_pdu_t,
111    _type_: coap_pdu_type_t,
112) -> coap_mid_t {
113    coap_send_message_type(session, request, COAP_MESSAGE_RST)
114}
115
116#[cfg(test)]
117mod tests {
118    use std::{
119        ffi::c_void,
120        net::{SocketAddr, UdpSocket},
121        os::raw::c_int,
122        sync::{Arc, Barrier},
123    };
124
125    use libc::{in6_addr, in_addr, sa_family_t, size_t, AF_INET, AF_INET6};
126
127    use super::*;
128    use crate::{
129        coap_pdu_code_t::{COAP_REQUEST_CODE_GET, COAP_RESPONSE_CODE_CONTENT},
130        coap_proto_t::COAP_PROTO_UDP,
131        coap_request_t::COAP_REQUEST_GET,
132        coap_response_t::COAP_RESPONSE_OK,
133    };
134
135    const COAP_TEST_RESOURCE_URI: &str = "test";
136    const COAP_TEST_RESOURCE_RESPONSE: &str = "Hello World!";
137
138    /// Creates a coap_address_t from a &SocketAddr.
139    fn coap_address_from_socketaddr(addr: &SocketAddr) -> coap_address_t {
140        match addr {
141            SocketAddr::V4(addr) => {
142                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
143                // to use a pointer instead.
144                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
145                unsafe {
146                    let mut coap_addr = coap_address_t {
147                        size: std::mem::size_of::<sockaddr_in>() as socklen_t,
148                        addr: std::mem::zeroed(),
149                    };
150                    *coap_addr.addr.sin.as_mut() = sockaddr_in {
151                        sin_family: AF_INET as sa_family_t,
152                        sin_port: addr.port().to_be(),
153                        sin_addr: in_addr {
154                            s_addr: u32::from_ne_bytes(addr.ip().octets()),
155                        },
156                        sin_zero: Default::default(),
157                    };
158                    coap_addr
159                }
160            },
161            SocketAddr::V6(addr) => {
162                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
163                // to use a pointer instead.
164                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
165                unsafe {
166                    let mut coap_addr = coap_address_t {
167                        size: std::mem::size_of::<sockaddr_in6>() as socklen_t,
168                        addr: std::mem::zeroed(),
169                    };
170                    *coap_addr.addr.sin6.as_mut() = sockaddr_in6 {
171                        sin6_family: AF_INET6 as sa_family_t,
172                        sin6_port: addr.port().to_be(),
173                        sin6_addr: in6_addr {
174                            s6_addr: addr.ip().octets(),
175                        },
176                        sin6_flowinfo: addr.flowinfo(),
177                        sin6_scope_id: addr.scope_id(),
178                    };
179                    coap_addr
180                }
181            },
182        }
183    }
184
185    /// Response handler for the CoAP client/server test (client-side)
186    /// # Safety
187    /// Assumes all pointers to be valid and pointing to data structures containing valid data
188    /// according to the specification of the method handler function provided in libcoap.
189    /// Assumes that the application data in the CoAP context associated with the session is a
190    /// pointer to a boolean specifying whether a successful request was received (will be set to
191    /// true by this function).
192    unsafe extern "C" fn test_resource_handler(
193        _resource: *mut coap_resource_t,
194        session: *mut coap_session_t,
195        _incoming_pdu: *const coap_pdu_t,
196        _query: *const coap_string_t,
197        response_pdu: *mut coap_pdu_t,
198    ) {
199        let mut buf: [u8; 3] = [0; 3];
200        coap_add_option(
201            response_pdu,
202            COAP_OPTION_CONTENT_TYPE as coap_option_num_t,
203            coap_encode_var_safe(buf.as_mut_ptr(), buf.len(), COAP_MEDIATYPE_TEXT_PLAIN) as usize,
204            buf.as_ptr(),
205        );
206        coap_add_data(
207            response_pdu,
208            COAP_TEST_RESOURCE_RESPONSE.len(),
209            COAP_TEST_RESOURCE_RESPONSE.as_ptr(),
210        );
211        coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void);
212        coap_pdu_set_code(response_pdu, COAP_RESPONSE_CODE_CONTENT);
213    }
214
215    /// Response handler for the CoAP client/server test (client-side)
216    /// # Safety
217    /// Assumes all pointers to be valid and pointing to data structures containing valid data
218    /// according to the specification of the response handler function provided in libcoap.
219    /// Assumes that the application data in the CoAP context associated with the session is a
220    /// pointer to a boolean specifying whether a successful response was received (will be set to
221    /// true by this function).
222    unsafe extern "C" fn test_response_handler(
223        session: *mut coap_session_t,
224        _sent: *const coap_pdu_t,
225        received: *const coap_pdu_t,
226        _mid: coap_mid_t,
227    ) -> coap_response_t {
228        assert_eq!(coap_pdu_get_code(received), COAP_RESPONSE_CODE_CONTENT);
229        let mut len: size_t = 0;
230        let mut data: *const u8 = std::ptr::null();
231        assert_ne!(coap_get_data(received, &mut len, &mut data), 0);
232        let data = std::slice::from_raw_parts(data, len);
233
234        assert_eq!(data, COAP_TEST_RESOURCE_RESPONSE.as_bytes());
235        coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void);
236        return COAP_RESPONSE_OK;
237    }
238
239    /// Creates a CoAP server that provides a single resource under COAP_TEST_RESOURCE_URI over the
240    /// supplied socket
241    fn run_coap_test_server(addr: &SocketAddr, barrier: Arc<Barrier>) {
242        // SAFETY: Null pointer is a valid parameter here.
243        let context = unsafe { coap_new_context(std::ptr::null()) };
244        assert!(!context.is_null());
245
246        let address: coap_address_t = coap_address_from_socketaddr(addr);
247
248        // SAFETY: We asserted that context != null, listen_addr is a reference and can therefore not be null.
249        let endpoint = unsafe { coap_new_endpoint(context, &address, coap_proto_t::COAP_PROTO_UDP) };
250        assert!(!endpoint.is_null());
251
252        // SAFETY: Since we use a string constant here, the arguments to the function are all valid.
253        let uri = unsafe { coap_new_str_const(COAP_TEST_RESOURCE_URI.as_ptr(), COAP_TEST_RESOURCE_URI.len()) };
254        assert!(!uri.is_null());
255
256        // SAFETY: We just asserted that uri is valid, COAP_RESOURCE_FLAGS_RELEASE_URI is valid because we will not free the uri ourselves.
257        let test_resource = unsafe { coap_resource_init(uri, COAP_RESOURCE_FLAGS_RELEASE_URI as c_int) };
258        assert!(!test_resource.is_null());
259
260        // SAFETY: We asserted that test_resource and context are valid, other pointers are always valid.
261        // The fact that we cast a constant to a mutable pointer is not problematic, because neither we
262        // nor libcoap ever mutate the pointer. This is necessary, because the underlying libcoap
263        // struct allows for mutable pointers to be set there, so that applications can use this to
264        // modify some application specific state.
265        unsafe {
266            coap_register_request_handler(test_resource, COAP_REQUEST_GET, Some(test_resource_handler));
267            coap_add_resource(context, test_resource);
268            coap_set_app_data(context, (&false) as *const bool as *mut c_void);
269        }
270
271        barrier.wait();
272        loop {
273            let time_spent_millis = unsafe { coap_io_process(context, 10000) };
274            // SAFETY: We are the only ones setting or accessing this value, so we know it is a
275            // const bool pointer (we have also set it to *false before and only ever set it to
276            // *true afterwards).
277            if unsafe { *(coap_get_app_data(context) as *const bool).as_ref().unwrap() } {
278                break;
279            }
280            if time_spent_millis == -1 || time_spent_millis >= 10000 {
281                panic!("Test timeout exceeded");
282            }
283        }
284        // SAFETY: Context is not referenced outside this function, and handlers (which get the
285        // context from libcoap) will not be called after the context is freed.
286        // This also seems to free all resources.
287        unsafe {
288            coap_free_context(context);
289            std::mem::drop(context);
290            std::mem::drop(test_resource);
291        }
292    }
293
294    /// Test case that creates a basic coap server and makes a request to it from a separate context
295    #[test]
296    fn test_coap_client_server_basic() {
297        // This will give us a SocketAddress with a port in the local port range automatically
298        // assigned by the operating system.
299        // Because the UdpSocket goes out of scope, the Port will be free for usage by libcoap.
300        // This seems to be the only portable way to get a port number assigned from the operating
301        // system, which is necessary here because of potential parallelisation (we can't just use
302        // the default CoAP port if multiple tests are run in parallel).
303        // It is assumed here that after unbinding the temporary socket, the OS will not reassign
304        // this port until we bind it again. This should work in most cases (unless we run on a
305        // system with very few free ports), because at least Linux will not reuse port numbers
306        // unless necessary, see https://unix.stackexchange.com/a/132524.
307        let server_address = UdpSocket::bind("localhost:0")
308            .expect("Failed to bind server socket")
309            .local_addr()
310            .expect("Failed to get server socket address");
311
312        let preparation_barrier = Arc::new(Barrier::new(2));
313
314        let server_address_clone = server_address.clone();
315        let preparation_barrier_clone = preparation_barrier.clone();
316        let server_thread_handle =
317            std::thread::spawn(move || run_coap_test_server(&server_address_clone, preparation_barrier_clone));
318
319        // SAFETY: Null pointer is a valid parameter here.
320        let context = unsafe { coap_new_context(std::ptr::null()) };
321        assert!(!context.is_null());
322
323        preparation_barrier.wait();
324
325        let server_address = coap_address_from_socketaddr(&server_address);
326
327        // SAFETY: null pointer is valid argument for local_if, server_address is guaranteed to be
328        // a correct value (conversion cannot fail), validity of context was asserted before.
329        let client_session =
330            unsafe { coap_new_client_session(context, std::ptr::null(), &server_address, COAP_PROTO_UDP) };
331
332        // SAFETY: context and client_session were asserted to be valid.
333        // Casting *const to *mut is fine because we don't mutate the value pointed to and the
334        // pointer is not used by libcoap (the application data pointer is intended to be used by
335        // client applications to store and/or modify their own data, which is why it is a mutable
336        // pointer in the first place).
337        // coap_request_pdu is asserted to be valid before it is used.
338        unsafe {
339            coap_register_response_handler(context, Some(test_response_handler));
340            coap_set_app_data(context, (&false) as *const bool as *mut c_void);
341            let coap_request_pdu =
342                coap_new_pdu(coap_pdu_type_t::COAP_MESSAGE_NON, COAP_REQUEST_CODE_GET, client_session);
343            assert!(!coap_request_pdu.is_null());
344            assert_ne!(
345                coap_add_option(
346                    coap_request_pdu,
347                    COAP_OPTION_URI_PATH as coap_option_num_t,
348                    COAP_TEST_RESOURCE_URI.len(),
349                    COAP_TEST_RESOURCE_URI.as_ptr(),
350                ),
351                0
352            );
353            assert_ne!(coap_send(client_session, coap_request_pdu), COAP_INVALID_MID);
354        }
355
356        loop {
357            // SAFETY: context is asserted to be valid, no known side effects that would violate any guarantees
358            let time_spent_millis = unsafe { coap_io_process(context, 10000) };
359            // SAFETY: We are the only ones setting or accessing this value, so we know it is a
360            // const bool pointer (we have also set it to *false before and only ever set it to
361            // *true afterwards).
362            if unsafe { *(coap_get_app_data(context) as *const bool).as_ref().unwrap() } {
363                break;
364            }
365            if time_spent_millis == -1 || time_spent_millis >= 10000 {
366                panic!("Test timeout exceeded");
367            }
368        }
369        // SAFETY: Context is not references outside this function, and handlers (which get the
370        // context from libcoap) will not be called after the context is freed.
371        // This also seems to free all resources.
372        unsafe {
373            coap_free_context(context);
374            std::mem::drop(context);
375            std::mem::drop(client_session)
376        }
377        server_thread_handle.join().expect("Error waiting for server thread");
378    }
379}