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

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

            
346
include!(env!("BINDINGS_FILE"));
347

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

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

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

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

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

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

            
478
    // ESP-IDF is missing the coap_tls_library_t type.
479
    #[cfg(not(target_os = "espidf"))]
480
    {
481
749
        let presumed_dtls_backend = if cfg!(dtls_backend = "openssl") {
482
375
            coap_tls_library_t_COAP_TLS_LIBRARY_OPENSSL
483
374
        } else if cfg!(dtls_backend = "gnutls") {
484
198
            coap_tls_library_t_COAP_TLS_LIBRARY_GNUTLS
485
176
        } else if cfg!(dtls_backend = "mbedtls") {
486
111
            coap_tls_library_t_COAP_TLS_LIBRARY_MBEDTLS
487
65
        } else if cfg!(dtls_backend = "tinydtls") {
488
65
            coap_tls_library_t_COAP_TLS_LIBRARY_TINYDTLS
489
        } else if cfg!(dtls_backend = "wolfssl") {
490
            coap_tls_library_t_COAP_TLS_LIBRARY_WOLFSSL
491
        } else {
492
            coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS
493
        };
494

            
495
749
        let actual_dtls_backend = unsafe { *coap_get_tls_library_version() }.type_;
496
749

            
497
749
        if presumed_dtls_backend != coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS
498
749
            && actual_dtls_backend != presumed_dtls_backend
499
        {
500
            panic_wrong_dtls!(presumed_dtls_backend, actual_dtls_backend);
501
749
        }
502
749
    }
503
749

            
504
749
    // SAFETY: Function is always safe to call.
505
749
    unsafe { coap_startup() }
506
749
}
507

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

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

            
519
    use super::*;
520

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

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

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

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

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

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

            
632
4
        let address: coap_address_t = coap_address_from_socketaddr(addr);
633
4

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

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

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

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

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

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

            
701
4
        let preparation_barrier = Arc::new(Barrier::new(2));
702
4

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

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

            
712
4
        preparation_barrier.wait();
713
4

            
714
4
        let server_address = coap_address_from_socketaddr(&server_address);
715
4

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

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

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