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
 * lib.rs - Main library entry point for raw libcoap bindings.
9
 */
10

            
11
//! Auto-generated unsafe bindings to [libcoap](https://github.com/obgm/libcoap), generated using
12
//! [bindgen](https://crates.io/crates/bindgen).
13
//!
14
//! This crate allows direct (but unsafe) usage of the libcoap C library from Rust. The declarations
15
//! made in this library are generated automatically using bindgen, for further documentation on how
16
//! to use them, refer to the [libcoap documentation](https://libcoap.net/documentation.html).
17
//!
18
//! In most cases you probably want to use the safe wrapper provided by the
19
//! [libcoap_rs](https://github.com/namib-project/libcoap-rs/tree/main/libcoap) crate or another
20
//! CoAP library written in pure Rust such as [coap-rs](https://github.com/covertness/coap-rs)
21
//! instead.
22
//!
23
//! The TLDR for building libcoap-sys (and resolving the most common Build Issues)
24
//! ------------------------------------------------------------------------------
25
//! It is strongly recommended that you read the remainder of this page in order to fully understand
26
//! the build process and possible causes of errors, especially if you're cross-compiling or
27
//! building for embedded targets.
28
//!
29
//! However, if you lack the time to do so, the following instructions should work in most cases:
30
//!
31
//! 1. Add a dependency to this crate and add all features you need for your crate to work.
32
//!    Call [`coap_startup_with_feature_checks()`] instead of [`coap_startup()`] during
33
//!    initialization to ensure that all of these features are actually available in the linked
34
//!    version of `libcoap`.
35
//!
36
//! 2. If you require DTLS support and run into `Required feature "dtls-(psk|pki|rpk|...)" is not
37
//!    supported by libcoap` errors, manually select a DTLS library that supports all of your
38
//!    required DTLS features by setting the `LIBCOAP_RS_DTLS_BACKEND` environment variable to your
39
//!    desired choice (the library name in all-lowercase should work).
40
//!
41
//! 3. If you're building a binary crate (or tests, examples, ...) and are getting non-DTLS-related
42
//!    `Required feature "<FEATURE>" is not supported by libcoap` errors, enable the `vendored`
43
//!    feature to build and statically link a version of libcoap that supports exactly the features
44
//!    you requested.
45
//!
46
//! 4. Inspect your dependency tree to determine whether you already have a DTLS library's sys-crate
47
//!    (`openssl-sys`, `tinydtls-sys` or `mbedtls-sys-auto`) in your dependency tree.
48
//!    If this is the case, enable the `dtls-<LIBRARY NAME>-sys` feature for all of them.
49
//!    This may resolve issues related to linking multiple versions of the same library at once, and
50
//!    could also help in reducing binary size.
51
//!    
52
//! If you're still unable to compile `libcoap-sys`, refer to the documentation below.
53
//! If the documentation below does not solve your issue, feel free to open an issue
54
//! [on GitHub](https://github.com/namib-project/libcoap-rs/) and ask for help.
55
//!
56
//! Optional Features
57
//! --------------
58
//! Most features specified in this crate's Cargo.toml directly correspond to a feature that can be
59
//! enabled or disabled in libcoap's configure-script and/or CMake configuration, refer to the
60
//! libcoap documentation for more details on these features.
61
//!
62
//! The `default` feature should match the default features enabled in the configure script of the
63
//! minimum supported version of libcoap.
64
//!
65
//! Depending on the build system and linked version of libcoap, the features actually provided may
66
//! differ from the ones indicated by the crate features.
67
//! If you want to ensure that all features that are enabled for this crate are actually supported
68
//! by the linked version of libcoap, you may call [`coap_startup_with_feature_checks()`].
69
//!
70
//! Aside from the features relating to libcoap functionality, the following features may also be
71
//! enabled for this crate:
72
//! - `vendored`: Build and statically link against a version of libcoap bundled with this crate
73
//!     instead of using a system-provided one[^1].
74
//! - `dtls-<LIBRARY NAME>-sys`: Allows the [vendored](#vendored-build-system) libcoap version to link against the
75
//!     same version of a DTLS library that is used by the corresponding <LIBRARY NAME>-sys
76
//!     crate[^2].
77
//!     Note, however, that this does not imply that this DTLS library *will* be used, refer to
78
//!     the [documentation below](#dtls-library-selection) for more information.
79
//!
80
//!     If a different build system than `vendored` is used, this feature is effectively a no-op.
81
//! - `dtls-<LIBRARY NAME>-sys-vendored` instructs the sys-crate of the DTLS library corresponding
82
//!     to the feature name to use a vendored version of the underlying library (implies
83
//!     `dtls-<LIBRARY NAME>-sys`).
84
//! - `dtls-(cid|psk|pki|pkcs11|rpk)`: Require support for specific DTLS features in `libcoap`.
85
//!     These features can not be enabled explicitly while building `libcoap`, support for them is
86
//!     automatically made available based on the used DTLS library (see
87
//!     [the corresponding section below](#dtls-library-selection)).
88
//!
89
//!     Enabling these features will add appropriate checks during compile- and/or runtime
90
//!     initialization to ensure these features are available in the used DTLS library (or panic
91
//!     otherwise).
92
//!
93
//! [^1]: Note that when building for the ESP-IDF, this feature will be a no-op, as the version
94
//!       provided by the ESP-IDF will always be used.
95
//! [^2]: In the case of `mbedtls`, `mbedtls-sys-auto` is used instead, as `mbedtls-sys` is
96
//!       unmaintained.
97
//!
98
//! Build Process
99
//! -------------
100
//! In general, `libcoap-sys` supports four different build systems, which will be explained in more
101
//! detail in the following sections:
102
//! - `vendored`: Build libcoap from source using a bundled version of the library (requires the
103
//!               `vendored` feature to be enabled.
104
//! - `pkgconfig`: Link against a system-provided version of libcoap, obtaining the library and
105
//!                include paths using the `pkg-config` utility.
106
//! - `manual`: Provide include and library directories+compiler/linker flags via environment
107
//!             variables.
108
//! - `espidf`: Build for the ESP family of microcontrollers using the ESP-IDF framework (used
109
//!             instead of the regular `vendored` build if the build system is set to `vendored` and
110
//!             building for an ESP-IDF Rust target).
111
//!
112
//! The build system that should be used can be specified manually by setting the
113
//! `LIBCOAP_RS_BUILD_SYSTEM` environment variable to the corresponding value.
114
//!
115
//! If you have explicitly specified a build system and building using that system fails, no other
116
//! system will be tried.
117
//!
118
//! If you do not explicitly provide a build system to use, the build script will follow these
119
//! steps to determine a suitable build system:
120
//!
121
//! 1. If the `vendored` crate feature is enabled, or we are building for the ESP-IDF, act as if the
122
//!    build system is set to `vendored`. If a vendored build is attempted and fails, return with an
123
//!    error and do not try anything else.
124
//! 2. Otherwise, try `pkgconfig` first.
125
//! 3. If `pkgconfig` doesn't work, fall back to `manual` (which will fail if the environment
126
//!    variables aren't set).
127
//! 4. If `manual` doesn't work, return an error indicating the issues with all previously attempted
128
//!    build systems.
129
//!
130
//! ## Generic Information (applies to all build systems)
131
//! The following information applies to all build systems (although some specifics may be detailed
132
//! in the respective build system's section).
133
//!
134
//! ### C Standard Library Functions
135
//! Some `libcoap` functions utilize types from the C standard library (e.g., `sockaddr_in` in
136
//! `coap_address_t`).
137
//!
138
//! For most targets, the data types defined in the [`libc`](https://docs.rs/libc/latest/libc/)
139
//! crate will be used to provide those data types.
140
//! However, some targets (especially embedded ones such as `espidf`) will use a different library
141
//! instead, which may cause compilation issues in code that assumes `libc` data types to be
142
//! compatible.
143
//!
144
//! For your convenience, this crate re-exports the used standard library crate as the `c_stdlib`
145
//! module. For best interoperability, you should `use` this module instead of using the actual
146
//! crates directly to import the required data types.
147
//!
148
//! ### DTLS Library Selection
149
//!
150
//! In order to provide DTLS support, `libcoap` must be combined with a DTLS library/backend.
151
//! DTLS libraries are mutually exclusive, and multiple versions of `libcoap` linked against
152
//! different DTLS libraries may be installed in a system simultaneously, so `libcoap-sys` must
153
//! decide on a variant of `libcoap` to link against during build.
154
//!
155
//! While the default mechanism for determining a DTLS library differs between build systems, you
156
//! may select a DTLS library explicitly by setting the `LIBCOAP_RS_DTLS_BACKEND` environment
157
//! variable to any of the supported values (`gnutls`, `openssl`, `mbedtls`, `tinydtls`, or
158
//! `wolfssl`). Refer to the build-system-specific documentation for information about supported
159
//! DTLS libraries and specifics.
160
//!
161
//! Note that some DTLS-related features (such as `dtls-(cid|psk|pki|pkcs11|rpk)`) are dependent on
162
//! the used DTLS backend, refer to [the `coap_encryption(3)` man page](https://libcoap.net/doc/reference/4.3.5/man_coap_encryption.html)
163
//! for information on supported features for each DTLS library.
164
//!
165
//! ### Feature Support and Compile-Time/Initialization Checks
166
//! During compilation, each build system will attempt to ensure that the used version of `libcoap`
167
//! does in fact support all [features](#optional-features) that were enabled in `Cargo.toml`.
168
//! The exact method differs based on each build system, but most will attempt to parse the
169
//! `coap3/coap_defines.h` header file in order to determine missing features (with `espidf` being a
170
//! notable exception).
171
//!
172
//! If a build system detects that a requested feature is missing, an appropriate error message will
173
//! be returned. In most cases, these errors must be resolved by linking to a different version of
174
//! `libcoap`.
175
//!
176
//! Unfortunately, for various reasons, this compile-time feature check may produce false
177
//! positive and/or false negative results (especially when cross compiling[^3])
178
//! is not available for all features (especially ones dependent on the DTLS library) and may not
179
//! even be available at all on some platforms.
180
//!
181
//! Therefore, library users should assume that the compile-time checks may not provide accurate
182
//! results, and should call [`coap_startup_with_feature_checks()`] during initialization to perform
183
//! run-time checks for all requested features. This run-time check will always work and be
184
//! accurate.
185
//!
186
//! Lastly, if you encounter a false positive error (i.e., a compile time error that indicates that
187
//! some feature is missing, even though you are 100% certain that it is available), you may bypass
188
//! the compile-time checks by setting `LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS` to any non-zero
189
//! value.
190
//! Note, however, that this might lead to cryptic errors if your assumption was wrong and the
191
//! feature is not available after all.
192
//!
193
//! In most cases, a false positive might be caused by the include paths/header files used for
194
//! binding generation refering to a different version of `libcoap` than the one that is linked
195
//! against, which could also cause difficult-to-debug issues and indicates a more severe problem
196
//! with the build process.
197
//!
198
//! [^3]: For this reason, using this method while cross-compiling [is noted to be unsafe in `libcoap`'s documentation](https://libcoap.net/doc/reference/4.3.5/man_coap_supported.html).
199
//!
200
//! ## Vendored Build System
201
//!
202
//! The vendored build system uses a bundled version of libcoap (usually the latest stable version
203
//! at the time of release) to build and statically link against.
204
//! Under the hood, it uses the [`autotools`](https://docs.rs/autotools/latest/autotools/) crate to
205
//! configure and run the build process, and you may therefore customize the build's compiler and
206
//! linker flags by setting the environment variables used in `libcoap`'s `configure` script.
207
//!
208
//! This build system will enable only those features in `libcoap` that are requested as
209
//! `Cargo.toml` features, and will explicitly disable all other ones.
210
//!
211
//! If a DTLS library is explicitly selected by the user, it will instruct libcoap to link against
212
//! that library by setting the corresponding `--with-<LIBRARY NAME>` configure flag.
213
//!
214
//! If you enable the `dtls-<LIBRARY NAME>-sys` features and do not set the `<LIBRARY NAME>_CFLAGS`
215
//! or `<LIBRARY NAME>_LIBS` environment variables, this build system will set these environment
216
//! variables to ensure that if this DTLS library is the one that libcoap uses, we link against
217
//! exactly the same version as used in the `<LIBRARY NAME>-sys` crate.
218
//! This is especially relevant if those crates also provide a `vendored` version in order to avoid
219
//! multiple versions of the same library being in use.
220
//! If a different DTLS library is used, this feature should have no effect (it will set the
221
//! environment variable, but `libcoap` will ignore it).
222
//!
223
//! If you do not specify a DTLS library, this build system will follow the same default order that
224
//! libcoap does (gnutls > openssl > wolfssl > mbedtls > tinydtls), unless you enabled one of the
225
//! `dtls-<LIBRARY NAME>-sys` features, in which case those will have priority.
226
//! If multiple of these features are enabled, they are prioritized in the same order as used by
227
//! `libcoap` (openssl > mbedtls > tinydtls).
228
//!
229
//! ## `pkg-config` Build System
230
//!
231
//! The `pkg-config` build system utilizes the `pkg-config` utility available on most Unix-like
232
//! systems to link against a system-provided version of `libcoap`.
233
//! To do so, it uses the [`pkg_config`](https://docs.rs/pkg-config/latest/pkg_config/) crate, and
234
//! you may therefore customize the build process by setting the environment variables described in
235
//! that library's documentation (which may be of special relevance if you try to cross compile).
236
//!
237
//! By default, it will probe `pkg-config` for a library with the name `libcoap-3`, which will
238
//! usually symlink to the DTLS library variant of libcoap that was installed most recently.
239
//! If you have explicitly requested use of a specific DTLS library, this build system will attempt
240
//! to find the `libcoap-3-<LIBRARY NAME>` library instead.
241
//!
242
//! However, library selection does not take into account any other requested features (i.e., it
243
//! will not check for feature support before generating the bindings), but will use header-based
244
//! compile-time feature checks (see the general section) to ensure support for all required
245
//! features after binding generation.
246
//!
247
//! The `dtls-<LIBRARY NAME>-sys` features have no effect on this build system, but note that static
248
//! linking against a system-provided version of `libcoap` may cause issues if it causes multiple
249
//! versions of the same DTLS library to be statically linked into the same Rust binary.
250
//!
251
//! ## `manual` Build System
252
//!
253
//! This build system is intended as a fallback solution if all other options fail. It will attempt
254
//! to generate bindings and link against the version of `libcoap` that is described by the
255
//! following environment variables:
256
//!
257
//! - `LIBCOAP_RS_INCLUDE_DIRS`: Paths that should be added to `clang`'s include path to search for
258
//!                              header files (e.g., `/usr/local/include`, _not_
259
//!                              `/usr/local/include/coap3`). Multiple values are separated by
260
//!                              colons (`:`).
261
//! - `LIBCOAP_RS_LIB_DIRS`:     Paths that should be added to `rustc`'s library path to search for
262
//!                              object files to link against. Multiple values are separated by
263
//!                              colons (`:`).
264
//! - `LIBCOAP_RS_STATIC`:       Set to any non-zero and non-empty value to instruct `rustc` to use
265
//!                              static linking instead of dynamic linking for `libcoap`.
266
//! - `LIBCOAP_RS_ADDITIONAL_LIBRARIES`: Additional libraries (such as DTLS libraries) that should
267
//!                              be linked against, separated by colons (`:`).
268
//!                              Note that these will be added after `libcoap`, and that the order
269
//!                              in which they are specified matters for most linkers.
270
//!                              You may also request static linking by prepending `static=` to the
271
//!                              library name.
272
//!
273
//! ## `espidf` Build System
274
//!
275
//! This build system will be used instead of the regular `vendored` build if you are building for
276
//! targets that are based on the `ESP-IDF`.
277
//!
278
//! If `libcoap-sys` is a direct dependency, it will automatically enable the
279
//! [`espressif/coap`](https://components.espressif.com/components/espressif/coap) component in
280
//! order to instruct `esp-idf-sys` to compile and link `libcoap` and generate bindings for it.
281
//!
282
//! If you encounter errors that indicate that the `espressif/coap` component may not be enabled in
283
//! the ESP-IDF, this could have the following reasons:
284
//! - You may have to run `cargo clean`, as the `esp-idf-sys` build script does not always detect
285
//!     changes in requested extra components properly.
286
//! - `libcoap-sys` is a transient dependency: the `esp-idf-sys` build script
287
//!     [only considers metadata from the root crate and its direct dependencies](https://docs.esp-rs.org/esp-idf-sys/esp_idf_sys/#extra-esp-idf-components)
288
//!     to determine which components to install.
289
//!     In order to solve this, you can either add `libcoap-sys` as a direct dependency, or copy
290
//!     this crate's `src/wrapper.h` file and add the following snippet to your own `Cargo.toml`.
291
//!     ```cargo
292
//!     [[package.metadata.esp-idf-sys.extra_components]]
293
//!     remote_component = { name = "espressif/coap", version = "4.3.5~3" }
294
//!     bindings_header = "src/wrapper.h"
295
//!     ```
296
//!     Afterward, run `cargo clean` (see the issue mentioned above) and try again.
297
//!
298
//! It will then parse the generated bindings file and re-export all symbols in `esp-idf-sys` that
299
//! are related to `libcoap`.
300
//!
301
//! Note that `esp-idf-sys` may use a different version of `bindgen` than the other build systems
302
//! and that bindings might differ slightly as a result.
303
//!
304
//! Unlike most other targets, this one will use `esp-idf-sys` instead of `libc` to provide its
305
//! standard library types (see the generic information section above).
306

            
307
// Bindgen translates the C headers, clippy's and rustfmt's recommendations are not applicable here.
308
#![allow(clippy::all)]
309
#![allow(non_camel_case_types)]
310
#![allow(deref_nullptr)]
311
#![allow(non_snake_case)]
312
#![allow(non_upper_case_globals)]
313

            
314
use core::ffi::c_void;
315

            
316
use c_stdlib::{epoll_event, fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t};
317
/// Re-export of the crate that provides libc data types used by libcoap.
318
///
319
/// In most cases, this will be libc, but on the ESP-IDF, it will be esp_idf_sys.
320
#[cfg(target_os = "espidf")]
321
pub use esp_idf_sys as c_stdlib;
322
/// Re-export of the crate that provides libc data types used by libcoap.
323
///
324
/// In most cases, this will be libc, but on the ESP-IDF, it will be esp_idf_sys.
325
#[cfg(not(target_os = "espidf"))]
326
pub use libc as c_stdlib;
327
// use dtls backend libraries in cases where they set our linker flags, otherwise rustc will
328
// optimize them out, resulting in missing symbols.
329
#[allow(unused_imports)]
330
#[cfg(used_dtls_crate = "mbedtls")]
331
use mbedtls_sys as _;
332
#[allow(unused_imports)]
333
#[cfg(used_dtls_crate = "openssl")]
334
use openssl_sys as _;
335
#[allow(unused_imports)]
336
#[cfg(used_dtls_crate = "tinydtls")]
337
use tinydtls_sys as _;
338
#[cfg(used_dtls_crate = "wolfssl")]
339
use wolfssl_sys as _;
340

            
341
// Add check whether the libcoap component is enabled when building for the ESP-IDF.
342
#[cfg(all(target_os = "espidf", not(esp_idf_comp_espressif__coap_enabled)))]
343
compile_error!(concat!(
344
    "You are building libcoap-sys for an ESP-IDF target, but have not added the\n",
345
    "espressif/coap remote component (see the libcoap-sys documentation for more information)"
346
));
347

            
348
include!(env!("BINDINGS_FILE"));
349

            
350
/// Compares instances of coap_str_const_t and/or coap_string_t.
351
///
352
/// This macro is a reimplementation of the macro defined in coap_str.h, see
353
/// <https://libcoap.net/doc/reference/develop/group__string.html#ga7f43c10b486dc6d45c37fcaf987d711b>.
354
#[macro_export]
355
macro_rules! coap_string_equal {
356
    ( $string1:expr, $string2:expr ) => {{
357
        use libcoap_sys::coap_string_equal_internal;
358
        let s1 = $string1;
359
        let s2 = $string2;
360
        coap_string_equal_internal((*s1).s, (*s1).length, (*s2).s, (*s2).length)
361
    }};
362
}
363

            
364
/// Internal only function for CoAP string comparisons.
365
///
366
/// *DO NOT USE THIS FUNCTION DIRECTLY.* It is only public because it is used by the
367
/// [coap_string_equal] macro, which is the function you probably wanted to call instead.
368
///
369
/// # Safety
370
///
371
/// This function should not be called directly, use [coap_string_equal] instead.
372
pub unsafe fn coap_string_equal_internal(
373
    str1_ptr: *const u8,
374
    str1_len: usize,
375
    str2_ptr: *const u8,
376
    str2_len: usize,
377
) -> bool {
378
    str1_len == str2_len
379
        && (str1_len == 0
380
            || !str1_ptr.is_null()
381
                && !str2_ptr.is_null()
382
                && memcmp(str1_ptr as *const c_void, str2_ptr as *const c_void, str1_len as _) == 0)
383
}
384

            
385
/// Execute feature check function and panic with an error message if the feature is not supported.
386
///
387
/// SAFETY: check_fn will be called, make sure to wrap this macro in unsafe if the function is
388
/// unsafe.
389
#[cfg(any(not(target_os = "espidf"), esp_idf_comp_espressif__coap_enabled))]
390
macro_rules! feature_check {
391
    ($feature:literal, $check_fn:ident) => {
392
        #[cfg(feature = $feature)]
393
        // SAFETY: Function is always safe to call.
394
        if $check_fn() != 1 {
395
            panic!("Required feature \"{}\" is not supported by libcoap", $feature)
396
        }
397
    };
398
    ( $(($feature:literal, $check_fn:ident)),* ) => {
399
        $(
400
            feature_check!($feature, $check_fn);
401
        )*
402
    }
403
}
404

            
405
#[cfg(any(not(target_os = "espidf"), esp_idf_comp_espressif__coap_enabled))]
406
macro_rules! dtls_backend_string {
407
    ($backend:ident) => {
408
        match $backend {
409
            coap_tls_library_t_COAP_TLS_LIBRARY_OPENSSL => "openssl",
410
            coap_tls_library_t_COAP_TLS_LIBRARY_GNUTLS => "gnutls",
411
            coap_tls_library_t_COAP_TLS_LIBRARY_MBEDTLS => "mbedtls",
412
            coap_tls_library_t_COAP_TLS_LIBRARY_TINYDTLS => "tinydtls",
413
            coap_tls_library_t_COAP_TLS_LIBRARY_WOLFSSL => "wolfssl",
414
            coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS => "notls",
415
            _ => "unknown",
416
        }
417
    };
418
}
419

            
420
#[cfg(any(not(target_os = "espidf"), esp_idf_comp_espressif__coap_enabled))]
421
macro_rules! panic_wrong_dtls {
422
    ($presumed_backend:ident, $detected_backend:ident) => {
423
        panic!(
424
            concat!(
425
                "compile-time detected DTLS backend \"{}\" does not match run-time detected DTLS backend \"{}\".\n",
426
                "This almost certainly means that libcoap-sys was linked against a different version of \n",
427
                "libcoap than the one whose headers were used for binding generation."
428
            ),
429
            dtls_backend_string!($presumed_backend),
430
            dtls_backend_string!($detected_backend)
431
        )
432
    };
433
}
434

            
435
/// Initialize the CoAP library and additionally perform runtime checks to ensure that required
436
/// features (as enabled in `Cargo.toml`) are available and that the used DTLS library matches the
437
/// one that was determined during compile-time.
438
///
439
/// You *should* prefer using this function over [`coap_startup()`], as without calling this function
440
/// some of the features enabled using the Cargo features may not actually be available.
441
///
442
/// Either this function or [`coap_startup()`] must be run once before any libcoap function is called.
443
///
444
/// If you are absolutely 100% certain that all features you require are always available (or are
445
/// prepared to deal with error return values/different behavior on your own if they aren't), you
446
/// may use [`coap_startup()`] instead.
447
// Make sure that if we're compiling for the ESP-IDF, this function is only compiled if the
448
// libcoap component is installed in the ESP-IDF.
449
// This way, these function calls will not cause missing function or struct definition errors that
450
// clutter up the error log, and the only error message will be the way more descriptive
451
// compile_error macro invocation at the start of this file.
452
#[cfg(any(not(target_os = "espidf"), esp_idf_comp_espressif__coap_enabled))]
453
751
pub fn coap_startup_with_feature_checks() {
454
751
    unsafe {
455
751
        feature_check!(
456
751
            ("af-unix", coap_af_unix_is_supported),
457
751
            ("async", coap_async_is_supported),
458
751
            ("client", coap_client_is_supported),
459
751
            ("dtls", coap_dtls_is_supported),
460
751
            ("dtls-cid", coap_dtls_cid_is_supported),
461
751
            ("dtls-psk", coap_dtls_psk_is_supported),
462
751
            ("dtls-pki", coap_dtls_pki_is_supported),
463
751
            ("dtls-pkcs11", coap_dtls_pkcs11_is_supported),
464
751
            ("dtls-rpk", coap_dtls_rpk_is_supported),
465
751
            ("epoll", coap_epoll_is_supported),
466
751
            ("ipv4", coap_ipv4_is_supported),
467
751
            ("ipv6", coap_ipv6_is_supported),
468
751
            ("observe-persist", coap_observe_persist_is_supported),
469
751
            ("oscore", coap_oscore_is_supported),
470
751
            ("q-block", coap_q_block_is_supported),
471
751
            ("server", coap_server_is_supported),
472
751
            ("tcp", coap_tcp_is_supported),
473
751
            ("thread-safe", coap_threadsafe_is_supported),
474
751
            ("tls", coap_tls_is_supported),
475
751
            ("websockets", coap_ws_is_supported),
476
751
            ("secure-websockets", coap_wss_is_supported)
477
751
        );
478
    }
479

            
480
    // ESP-IDF is missing the coap_tls_library_t type.
481
    #[cfg(not(target_os = "espidf"))]
482
    {
483
751
        let presumed_dtls_backend = if cfg!(dtls_backend = "openssl") {
484
397
            coap_tls_library_t_COAP_TLS_LIBRARY_OPENSSL
485
354
        } else if cfg!(dtls_backend = "gnutls") {
486
199
            coap_tls_library_t_COAP_TLS_LIBRARY_GNUTLS
487
155
        } else if cfg!(dtls_backend = "mbedtls") {
488
89
            coap_tls_library_t_COAP_TLS_LIBRARY_MBEDTLS
489
66
        } else if cfg!(dtls_backend = "tinydtls") {
490
65
            coap_tls_library_t_COAP_TLS_LIBRARY_TINYDTLS
491
1
        } else if cfg!(dtls_backend = "wolfssl") {
492
1
            coap_tls_library_t_COAP_TLS_LIBRARY_WOLFSSL
493
        } else {
494
            coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS
495
        };
496

            
497
751
        let actual_dtls_backend = unsafe { *coap_get_tls_library_version() }.type_;
498
751

            
499
751
        if presumed_dtls_backend != coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS
500
751
            && actual_dtls_backend != presumed_dtls_backend
501
        {
502
            panic_wrong_dtls!(presumed_dtls_backend, actual_dtls_backend);
503
751
        }
504
751
    }
505
751

            
506
751
    // SAFETY: Function is always safe to call.
507
751
    unsafe { coap_startup() }
508
751
}
509

            
510
#[cfg(all(test, not(target_os = "espidf")))]
511
mod tests {
512
    use std::{
513
        ffi::c_void,
514
        net::{SocketAddr, UdpSocket},
515
        os::raw::c_int,
516
        sync::{Arc, Barrier},
517
    };
518

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

            
521
    use super::*;
522

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

            
526
    /// Creates a coap_address_t from a &SocketAddr.
527
10
    fn coap_address_from_socketaddr(addr: &SocketAddr) -> coap_address_t {
528
10
        match addr {
529
            SocketAddr::V4(addr) => {
530
                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
531
                // to use a pointer instead.
532
                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
533
                unsafe {
534
                    let mut coap_addr = coap_address_t {
535
                        size: std::mem::size_of::<sockaddr_in>() as socklen_t,
536
                        addr: std::mem::zeroed(),
537
                    };
538
                    coap_addr.addr.sin = sockaddr_in {
539
                        sin_family: AF_INET as sa_family_t,
540
                        sin_port: addr.port().to_be(),
541
                        sin_addr: in_addr {
542
                            s_addr: u32::from_ne_bytes(addr.ip().octets()),
543
                        },
544
                        sin_zero: Default::default(),
545
                    };
546
                    coap_addr
547
                }
548
            },
549
10
            SocketAddr::V6(addr) => {
550
10
                // addr is a bindgen-type union wrapper, so we can't assign to it directly and have
551
10
                // to use a pointer instead.
552
10
                // SAFETY: addr is not read before it is assigned properly, assignment cannot fail.
553
10
                unsafe {
554
10
                    let mut coap_addr = coap_address_t {
555
10
                        size: std::mem::size_of::<sockaddr_in6>() as socklen_t,
556
10
                        addr: std::mem::zeroed(),
557
10
                    };
558
10
                    coap_addr.addr.sin6 = sockaddr_in6 {
559
10
                        sin6_family: AF_INET6 as sa_family_t,
560
10
                        sin6_port: addr.port().to_be(),
561
10
                        sin6_addr: in6_addr {
562
10
                            s6_addr: addr.ip().octets(),
563
10
                        },
564
10
                        sin6_flowinfo: addr.flowinfo(),
565
10
                        sin6_scope_id: addr.scope_id(),
566
10
                    };
567
10
                    coap_addr
568
                }
569
            },
570
        }
571
10
    }
572

            
573
    /// Response handler for the CoAP client/server test (client-side)
574
    /// # Safety
575
    /// Assumes all pointers to be valid and pointing to data structures containing valid data
576
    /// according to the specification of the method handler function provided in libcoap.
577
    /// Assumes that the application data in the CoAP context associated with the session is a
578
    /// pointer to a boolean specifying whether a successful request was received (will be set to
579
    /// true by this function).
580
5
    unsafe extern "C" fn test_resource_handler(
581
5
        _resource: *mut coap_resource_t,
582
5
        session: *mut coap_session_t,
583
5
        _incoming_pdu: *const coap_pdu_t,
584
5
        _query: *const coap_string_t,
585
5
        response_pdu: *mut coap_pdu_t,
586
5
    ) {
587
5
        let mut buf: [u8; 3] = [0; 3];
588
5
        coap_add_option(
589
5
            response_pdu,
590
5
            COAP_OPTION_CONTENT_TYPE as coap_option_num_t,
591
5
            coap_encode_var_safe(buf.as_mut_ptr(), buf.len(), COAP_MEDIATYPE_TEXT_PLAIN) as usize,
592
5
            buf.as_ptr(),
593
5
        );
594
5
        coap_add_data(
595
5
            response_pdu,
596
5
            COAP_TEST_RESOURCE_RESPONSE.len(),
597
5
            COAP_TEST_RESOURCE_RESPONSE.as_ptr(),
598
5
        );
599
5
        coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void);
600
5
        coap_pdu_set_code(response_pdu, coap_pdu_code_t_COAP_RESPONSE_CODE_CONTENT);
601
5
    }
602

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

            
622
5
        assert_eq!(data, COAP_TEST_RESOURCE_RESPONSE.as_bytes());
623
5
        coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void);
624
5
        return coap_response_t_COAP_RESPONSE_OK;
625
5
    }
626

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

            
634
5
        let address: coap_address_t = coap_address_from_socketaddr(addr);
635
5

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

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

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

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

            
663
5
        barrier.wait();
664
        loop {
665
5
            let time_spent_millis = unsafe { coap_io_process(context, 10000) };
666
5
            // SAFETY: We are the only ones setting or accessing this value, so we know it is a
667
5
            // const bool pointer (we have also set it to *false before and only ever set it to
668
5
            // *true afterwards).
669
5
            if unsafe { *(coap_get_app_data(context) as *const bool).as_ref().unwrap() } {
670
5
                break;
671
            }
672
            if time_spent_millis == -1 || time_spent_millis >= 10000 {
673
                panic!("Test timeout exceeded");
674
            }
675
        }
676
        // SAFETY: Context is not referenced outside this function, and handlers (which get the
677
        // context from libcoap) will not be called after the context is freed.
678
        // This also seems to free all resources.
679
5
        unsafe {
680
5
            coap_free_context(context);
681
5
        }
682
5
    }
683

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

            
703
5
        let preparation_barrier = Arc::new(Barrier::new(2));
704
5

            
705
5
        let server_address_clone = server_address.clone();
706
5
        let preparation_barrier_clone = preparation_barrier.clone();
707
5
        let server_thread_handle =
708
5
            std::thread::spawn(move || run_coap_test_server(&server_address_clone, preparation_barrier_clone));
709
5

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

            
714
5
        preparation_barrier.wait();
715
5

            
716
5
        let server_address = coap_address_from_socketaddr(&server_address);
717
5

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

            
723
5
        // SAFETY: context and client_session were asserted to be valid.
724
5
        // Casting *const to *mut is fine because we don't mutate the value pointed to and the
725
5
        // pointer is not used by libcoap (the application data pointer is intended to be used by
726
5
        // client applications to store and/or modify their own data, which is why it is a mutable
727
5
        // pointer in the first place).
728
5
        // coap_request_pdu is asserted to be valid before it is used.
729
5
        unsafe {
730
5
            coap_register_response_handler(context, Some(test_response_handler));
731
5
            coap_set_app_data(context, (&false) as *const bool as *mut c_void);
732
5
            let coap_request_pdu = coap_new_pdu(
733
5
                coap_pdu_type_t_COAP_MESSAGE_NON,
734
5
                coap_pdu_code_t_COAP_REQUEST_CODE_GET,
735
5
                client_session,
736
5
            );
737
5
            assert!(!coap_request_pdu.is_null());
738
5
            assert_ne!(
739
5
                coap_add_option(
740
5
                    coap_request_pdu,
741
5
                    COAP_OPTION_URI_PATH as coap_option_num_t,
742
5
                    COAP_TEST_RESOURCE_URI.len(),
743
5
                    COAP_TEST_RESOURCE_URI.as_ptr(),
744
5
                ),
745
5
                0
746
5
            );
747
5
            assert_ne!(coap_send(client_session, coap_request_pdu), COAP_INVALID_MID);
748
        }
749

            
750
        loop {
751
            // SAFETY: context is asserted to be valid, no known side effects that would violate any guarantees
752
5
            let time_spent_millis = unsafe { coap_io_process(context, 10000) };
753
5
            // SAFETY: We are the only ones setting or accessing this value, so we know it is a
754
5
            // const bool pointer (we have also set it to *false before and only ever set it to
755
5
            // *true afterwards).
756
5
            if unsafe { *(coap_get_app_data(context) as *const bool).as_ref().unwrap() } {
757
5
                break;
758
            }
759
            if time_spent_millis == -1 || time_spent_millis >= 10000 {
760
                panic!("Test timeout exceeded");
761
            }
762
        }
763
        // SAFETY: Context is not references outside this function, and handlers (which get the
764
        // context from libcoap) will not be called after the context is freed.
765
        // This also seems to free all resources.
766
5
        unsafe {
767
5
            coap_free_context(context);
768
5
        }
769
5
        server_thread_handle.join().expect("Error waiting for server thread");
770
5
    }
771
}