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}