From ad838a84379d404e70aba0fdf4dc9dfc3443affe Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Thu, 19 Dec 2024 17:28:48 +0100 Subject: [PATCH 01/26] refactor(sys): convert build script to directory --- libcoap-sys/Cargo.toml | 1 + libcoap-sys/{build.rs => build/main.rs} | 0 2 files changed, 1 insertion(+) rename libcoap-sys/{build.rs => build/main.rs} (100%) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 769576b4..6b8e374d 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -18,6 +18,7 @@ categories = ["external-ffi-bindings", "network-programming", "embedded"] keywords = ["coap", "libcoap"] exclude = ["src/libcoap/ext/"] resolver = "2" +build = "build/main.rs" # Current reason for MSRV (please update when increasing MSRV): Transient dependency "home" requires Rust 1.81. rust-version = "1.81.0" diff --git a/libcoap-sys/build.rs b/libcoap-sys/build/main.rs similarity index 100% rename from libcoap-sys/build.rs rename to libcoap-sys/build/main.rs From 5a136d25d56633a6eaa872b4f4f5898e9cc4767f Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Thu, 16 Jan 2025 22:00:54 +0100 Subject: [PATCH 02/26] refactor(sys): implement pkg-config based build system for sys crate This commit also moves the existing build script code into a separate file and removes all other build systems (for now, they will be re-added in the following commits. --- libcoap-sys/Cargo.toml | 2 + libcoap-sys/build/bindings.rs | 99 +++ libcoap-sys/build/build_system/esp_idf.rs | 1 + libcoap-sys/build/build_system/manual.rs | 0 libcoap-sys/build/build_system/mod.rs | 20 + libcoap-sys/build/build_system/pkgconfig.rs | 70 ++ libcoap-sys/build/build_system/vendored.rs | 1 + libcoap-sys/build/main.rs | 887 +++----------------- libcoap-sys/build/metadata.rs | 200 +++++ libcoap-sys/old_build.rs | 634 ++++++++++++++ libcoap-sys/src/lib.rs | 20 +- 11 files changed, 1141 insertions(+), 793 deletions(-) create mode 100644 libcoap-sys/build/bindings.rs create mode 100644 libcoap-sys/build/build_system/esp_idf.rs create mode 100644 libcoap-sys/build/build_system/manual.rs create mode 100644 libcoap-sys/build/build_system/mod.rs create mode 100644 libcoap-sys/build/build_system/pkgconfig.rs create mode 100644 libcoap-sys/build/build_system/vendored.rs create mode 100644 libcoap-sys/build/metadata.rs create mode 100644 libcoap-sys/old_build.rs diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 6b8e374d..955d2b6b 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -152,6 +152,8 @@ pkg-config = "^0.3.24" regex = "1.10.5" embuild = { version = "0.32.0", features = ["bindgen", "espidf", "cmake"] } version-compare = "0.2.0" +anyhow = "1.0.94" +enumset = "1.1.5" [package.metadata.docs.rs] features = ["dtls", "dtls_backend_openssl", "vendored"] diff --git a/libcoap-sys/build/bindings.rs b/libcoap-sys/build/bindings.rs new file mode 100644 index 00000000..be30d1a4 --- /dev/null +++ b/libcoap-sys/build/bindings.rs @@ -0,0 +1,99 @@ +use std::{cell::RefCell, fmt::Debug, rc::Rc}; + +use anyhow::{Context, Result}; +use bindgen::{ + callbacks::{IntKind, ParseCallbacks}, + EnumVariation, +}; + +use crate::metadata::{LibcoapDefineInfo, LibcoapFeature}; + +/// Implementation of bindgen's [ParseCallbacks] that allow reading some meta-information about the +/// used libcoap version from its defines (package version, supported features, ...) +#[derive(Debug, Default)] +pub struct LibcoapDefineParser { + defines: Rc>, +} + +impl LibcoapDefineParser { + pub fn new() -> (Rc>, Self) { + let target = std::env::var_os("TARGET").unwrap_or_default(); + let host = std::env::var_os("HOST").unwrap_or_default(); + + if target != host { + println!("cargo:warning=libcoap-rs compile-time feature checks may be inaccurate when cross compiling, see https://libcoap.net/doc/reference/4.3.5/man_coap_supported.html for more information."); + } + + let value: LibcoapDefineParser = Default::default(); + (Rc::clone(&value.defines), value) + } +} + +impl ParseCallbacks for LibcoapDefineParser { + fn int_macro(&self, name: &str, value: i64) -> Option { + self.defines.borrow_mut().supported_features |= LibcoapFeature::features_from_define(name, value); + None + } + + fn str_macro(&self, name: &str, value: &[u8]) { + /*// Will allow this here, as we might want to add additional cfg flags later on. + #[allow(clippy::single_match)] + match name { + "LIBCOAP_PACKAGE_VERSION" => { + let version_str = String::from_utf8_lossy(value); + println!("cargo:rustc-cfg=libcoap_version=\"{}\"", version_str.as_ref()); + println!("cargo:libcoap_version={}", version_str.as_ref()); + let version = Version::from(version_str.as_ref()).expect("invalid libcoap version"); + match version.compare(Version::from("4.3.4").unwrap()) { + Cmp::Gt => println!("cargo:rustc-cfg=non_inlined_coap_send_rst"), + _ => {}, + } + self.defines.borrow_mut().package_version = version.to_string(); + }, + _ => {}, + }*/ + } + + fn include_file(&self, filename: &str) { + /*let header_path = Path::new(filename); + if header_path.file_name().eq(&Some(OsStr::new("coap_defines.h"))) { + self.defines.borrow_mut().feature_defines_available = true; + }*/ + } +} + +pub fn generate_libcoap_bindings( + bindgen_builder_configurator: impl FnOnce(bindgen::Builder) -> Result, +) -> Result { + let mut builder = bindgen::Builder::default() + .header("src/wrapper.h") + .default_enum_style(EnumVariation::Rust { non_exhaustive: true }) + // Causes invalid syntax for some reason, so we have to disable it. + .generate_comments(false) + .dynamic_link_require_all(true) + .allowlist_function("(oscore|coap)_.*") + .allowlist_type("(oscore|coap)_.*") + .allowlist_var("(oscore|coap)_.*") + .allowlist_function("(OSCORE|COAP)_.*") + .allowlist_type("(OSCORE|COAP)_.*") + .allowlist_var("(OSCORE|COAP|LIBCOAP)_.*") + // We use the definitions made by the libc crate instead + .blocklist_type("sockaddr(_in|_in6)?") + .blocklist_type("in6?_(addr|port)(_t)?") + .blocklist_type("in6_addr__bindgen_ty_1") + .blocklist_type("(__)?socklen_t") + .blocklist_type("fd_set") + .blocklist_type("sa_family_t") + .blocklist_type("(__)?time_t") + .blocklist_type("__fd_mask") + .blocklist_type("epoll_event") + // Are generated because they are typedef-ed inside of the C headers, blocklisting them + // will instead replace them with the appropriate rust types. + // See https://github.com/rust-lang/rust-bindgen/issues/1215 for an open issue concerning + // this problem. + .blocklist_type("__(u)?int(8|16|32|64|128)_t") + .size_t_is_usize(true); + builder = bindgen_builder_configurator(builder)?; + + builder.generate().context("unable to generate bindings") +} diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs new file mode 100644 index 00000000..b4d50cf9 --- /dev/null +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -0,0 +1 @@ +#![cfg(target_os = "espidf")] diff --git a/libcoap-sys/build/build_system/manual.rs b/libcoap-sys/build/build_system/manual.rs new file mode 100644 index 00000000..e69de29b diff --git a/libcoap-sys/build/build_system/mod.rs b/libcoap-sys/build/build_system/mod.rs new file mode 100644 index 00000000..db7c386a --- /dev/null +++ b/libcoap-sys/build/build_system/mod.rs @@ -0,0 +1,20 @@ +use std::path::PathBuf; + +use anyhow::Result; +use enumset::EnumSet; +use version_compare::Version; + +use crate::metadata::LibcoapFeature; + +pub mod esp_idf; +pub mod manual; +pub mod pkgconfig; +pub mod vendored; + +pub trait BuildSystem { + fn detected_features(&self) -> Option>; + + fn version(&self) -> Option; + + fn generate_bindings(&mut self) -> Result; +} diff --git a/libcoap-sys/build/build_system/pkgconfig.rs b/libcoap-sys/build/build_system/pkgconfig.rs new file mode 100644 index 00000000..6401d9ba --- /dev/null +++ b/libcoap-sys/build/build_system/pkgconfig.rs @@ -0,0 +1,70 @@ +use std::{cell::RefCell, path::PathBuf}; + +use anyhow::Context; +use enumset::EnumSet; +use pkg_config::Library; +use version_compare::Version; + +use crate::{ + bindings::{generate_libcoap_bindings, LibcoapDefineParser}, + build_system::BuildSystem, + metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature, MINIMUM_LIBCOAP_VERSION}, +}; + +pub struct PkgConfigBuildSystem { + define_info: Option, + out_dir: PathBuf, + library: Library, +} + +impl PkgConfigBuildSystem { + /// Obtain some built version of libcoap and set the appropriate linker flags to link with it + /// (and its dependencies, if any). + pub fn link_with_libcoap(out_dir: PathBuf, requested_dtls_backend: Option) -> anyhow::Result { + let mut prober = pkg_config::Config::new(); + let prober = prober + .atleast_version(MINIMUM_LIBCOAP_VERSION) + .cargo_metadata(true) + .env_metadata(true); + let library = if let Some(requested_dtls_backend) = requested_dtls_backend { + // Use the libcoap version corresponding to the requested DTLS library, if one has been set. + prober.probe(&format!("libcoap-3-{}", requested_dtls_backend.pkg_config_suffix())) + } else { + // Otherwise, use the "default" version. + prober.probe("libcoap-3") + }; + + library + .map(|lib| Self { + out_dir, + define_info: None, + library: lib, + }) + .context("unable to probe library using pkg-config") + } +} + +impl BuildSystem for PkgConfigBuildSystem { + fn detected_features(&self) -> Option> { + self.define_info.as_ref().map(|v| v.supported_features) + } + + fn version(&self) -> Option { + Version::from(&self.library.version) + .map(Some) + .expect("unable to parse version string obtained from pkg-config") + } + + fn generate_bindings(&mut self) -> anyhow::Result { + let (define_info, define_parser) = LibcoapDefineParser::new(); + let bindings = generate_libcoap_bindings(|builder| Ok(builder.parse_callbacks(Box::new(define_parser))))?; + + self.define_info = Some(RefCell::take(&define_info)); + + let out_path = self.out_dir.join("bindings.rs"); + bindings + .write_to_file(&out_path) + .context("unable to write bindings to file")?; + Ok(out_path) + } +} diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs new file mode 100644 index 00000000..6e2f1b8f --- /dev/null +++ b/libcoap-sys/build/build_system/vendored.rs @@ -0,0 +1 @@ +pub struct VendoredBuildSystem {} diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 476c0e3f..0322d922 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -1,804 +1,135 @@ -// SPDX-License-Identifier: BSD-2-CLAUSE -/* - * build.rs - build script for libcoap Rust bindings. - * This file is part of the libcoap-sys crate, see the README and LICENSE files for - * more information and terms of use. - * Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved. - * See the README as well as the LICENSE file for more information. - */ +use std::{env, env::VarError, path::PathBuf}; -use std::{ - cell::RefCell, - collections::BTreeSet, - default::Default, - env, - ffi::{OsStr, OsString}, - fmt::{Debug, Display}, - io::ErrorKind, - path::{Path, PathBuf}, - process::Command, - rc::Rc, -}; +use anyhow::{anyhow, bail, Context, Result}; +use enumset::EnumSet; +use version_compare::Version; -use bindgen::{ - callbacks::{IntKind, ParseCallbacks}, - EnumVariation, +use crate::{ + build_system::{pkgconfig::PkgConfigBuildSystem, BuildSystem}, + metadata::{DtlsBackend, LibcoapFeature, MINIMUM_LIBCOAP_VERSION}, }; -use pkg_config::probe_library; -use version_compare::{Cmp, Version}; - -/// Features whose availability can be checked during compile time based on `#define` directives. -const COMPILE_TIME_FEATURE_CHECKS: [&str; 16] = [ - "af-unix", - "async", - "client", - "small-stack", - "tcp", - "epoll", - "ipv4", - "ipv6", - "oscore", - "q-block", - "server", - "thread-recursive-lock-detection", - "thread-safe", - "dtls", - "observe-persist", - "websockets", -]; - -/// Data structure describing meta-information about the used version of libcoap. -#[derive(Debug)] -struct LibcoapMetadata { - package_version: String, - version: i64, - feature_defines_available: bool, - feature_defines: BTreeSet, - dtls_backend: Option, -} - -impl Default for LibcoapMetadata { - fn default() -> Self { - Self { - package_version: Default::default(), - version: 0, - feature_defines_available: false, - // By default, TCP is assumed to be supported if COAP_DISABLE_TCP is unset. - feature_defines: BTreeSet::from(["tcp".to_string()]), - dtls_backend: None, - } - } -} - -/// Implementation of bindgen's [ParseCallbacks] that allow reading some meta-information about the -/// used libcoap version from its defines (package version, supported features, ...) -#[derive(Debug, Default)] -struct CoapDefineParser { - defines: Rc>, -} - -impl ParseCallbacks for CoapDefineParser { - fn int_macro(&self, name: &str, value: i64) -> Option { - match name { - "LIBCOAP_VERSION" => { - self.defines.borrow_mut().version = value; - }, - "COAP_AF_UNIX_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("af-unix".to_string()); - }, - "COAP_ASYNC_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("async".to_string()); - }, - "COAP_CLIENT_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("client".to_string()); - }, - "COAP_CONSTRAINED_STACK" => { - self.defines - .borrow_mut() - .feature_defines - .insert("small-stack".to_string()); - }, - "COAP_DISABLE_TCP" => { - if value == 1 { - self.defines.borrow_mut().feature_defines.remove("tcp"); - } - }, - "COAP_EPOLL_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("epoll".to_string()); - }, - "COAP_IPV4_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("ipv4".to_string()); - }, - "COAP_IPV6_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("ipv6".to_string()); - }, - "COAP_OSCORE_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("oscore".to_string()); - }, - "COAP_Q_BLOCK_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("q-block".to_string()); - }, - "COAP_SERVER_SUPPORT" => { - self.defines.borrow_mut().feature_defines.insert("server".to_string()); - }, - "COAP_THREAD_RECURSIVE_CHECK" => { - self.defines - .borrow_mut() - .feature_defines - .insert("thread-recursive-lock-detection".to_string()); - }, - "COAP_THREAD_SAFE" => { - self.defines - .borrow_mut() - .feature_defines - .insert("thread-safe".to_string()); - }, - "COAP_WITH_LIBGNUTLS" => { - self.defines.borrow_mut().dtls_backend = Some(DtlsBackend::GnuTls); - self.defines.borrow_mut().feature_defines.insert("dtls".to_string()); - }, - "COAP_WITH_LIBMBEDTLS" => { - self.defines.borrow_mut().dtls_backend = Some(DtlsBackend::MbedTls); - self.defines.borrow_mut().feature_defines.insert("dtls".to_string()); - }, - "COAP_WITH_LIBOPENSSL" => { - self.defines.borrow_mut().dtls_backend = Some(DtlsBackend::OpenSsl); - self.defines.borrow_mut().feature_defines.insert("dtls".to_string()); - }, - "COAP_WITH_LIBTINYDTLS" => { - self.defines.borrow_mut().dtls_backend = Some(DtlsBackend::TinyDtls); - self.defines.borrow_mut().feature_defines.insert("dtls".to_string()); - }, - // TODO(#29): as soon as we have wolfSSL support in libcoap-sys - /*"COAP_WITH_LIBWOLFSSL" => { - self.defines.borrow_mut().dtls_backend = Some(DtlsBackend::WolfSsl); - self.defines - .borrow_mut() - .feature_defines - .insert("dtls".to_string()); - },*/ - "COAP_WITH_OBSERVE_PERSIST" => { - self.defines - .borrow_mut() - .feature_defines - .insert("observe-persist".to_string()); - }, - "COAP_WS_SUPPORT" => { - self.defines - .borrow_mut() - .feature_defines - .insert("websockets".to_string()); - }, - _ => {}, - } - None - } - - fn str_macro(&self, name: &str, value: &[u8]) { - // Will allow this here, as we might want to add additional cfg flags later on. - #[allow(clippy::single_match)] - match name { - "LIBCOAP_PACKAGE_VERSION" => { - let version_str = String::from_utf8_lossy(value); - println!("cargo:rustc-cfg=libcoap_version=\"{}\"", version_str.as_ref()); - println!("cargo:libcoap_version={}", version_str.as_ref()); - let version = Version::from(version_str.as_ref()).expect("invalid libcoap version"); - match version.compare(Version::from("4.3.4").unwrap()) { - Cmp::Gt => println!("cargo:rustc-cfg=non_inlined_coap_send_rst"), - _ => {}, - } - self.defines.borrow_mut().package_version = version.to_string(); - }, - _ => {}, - } - } - - fn include_file(&self, filename: &str) { - let header_path = Path::new(filename); - if header_path.file_name().eq(&Some(OsStr::new("coap_defines.h"))) { - self.defines.borrow_mut().feature_defines_available = true; - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DtlsBackend { - GnuTls, - OpenSsl, - MbedTls, - TinyDtls, -} -impl Display for DtlsBackend { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let str = match self { - DtlsBackend::GnuTls => "gnutls", - DtlsBackend::OpenSsl => "openssl", - DtlsBackend::MbedTls => "mbedtls", - DtlsBackend::TinyDtls => "tinydtls", - } - .to_string(); - write!(f, "{}", str) - } -} - -fn get_target_mcu() -> &'static str { - let cfg_flags = embuild::espidf::sysenv::cfg_args().expect("missing cfg flags from IDF"); - let mcus = [ - "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32c2", "esp32h2", "esp32c5", "esp32c6", "esp32p4", - ]; - for mcu in mcus { - if cfg_flags.get(mcu).is_some() { - return mcu; - } - } - panic!("unknown ESP target MCU, please add target to libcoap-sys build.rs file!") -} - -fn get_builder_espidf() -> bindgen::Builder { - embuild::espidf::sysenv::output(); - let esp_idf_path = embuild::espidf::sysenv::idf_path().expect("missing IDF path"); - let esp_idf_buildroot = env::var("DEP_ESP_IDF_ROOT").expect("DEP_ESP_IDF_ROOT is not set"); - let esp_include_path = embuild::espidf::sysenv::cincl_args().expect("missing IDF cincl args"); - let embuild_env = embuild::espidf::sysenv::env_path().expect("missing IDF env path"); - let esp_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH is not set"); - // Determine compiler path - // SAFETY: Always safe to call in a single-threaded environment (see docs of env::set_var). - unsafe { env::set_var("PATH", embuild_env) }; - let cmake_info = embuild::cmake::Query::new( - &Path::new(&esp_idf_buildroot).join("build"), - "cargo", - &[ - embuild::cmake::file_api::ObjKind::Codemodel, - embuild::cmake::file_api::ObjKind::Toolchains, - embuild::cmake::file_api::ObjKind::Cache, - ], - ) - .expect("unable to query cmake API for compiler path") - .get_replies() - .expect("unable to get cmake query replies for compiler path"); - let compiler = cmake_info - .get_toolchains() - .map_err(|_e| "Can't get toolchains") - .and_then(|mut t| { - t.take(embuild::cmake::file_api::codemodel::Language::C) - .ok_or("No C toolchain") - }) - .and_then(|t| t.compiler.path.ok_or("No compiler path set")) - .expect("unable to determine compiler path"); +mod bindings; +mod build_system; +mod metadata; - // Parse include arguments - // Regexes are correct and never change, therefore it is ok to unwrap here. - let arg_splitter = regex::Regex::new(r##"(?:[^\\]"[^"]*[^\\]")?(\s)"##).unwrap(); - let apostrophe_remover = regex::Regex::new(r##"^"(?.*)"$"##).unwrap(); - let esp_clang_args = arg_splitter - .split(esp_include_path.args.as_str()) - .map(|x| apostrophe_remover.replace(x.trim(), "$content").to_string()) - .collect::>(); - let bindgen_builder = embuild::bindgen::Factory { - clang_args: esp_clang_args.clone(), - linker: Some(compiler), - mcu: None, - force_cpp: false, - sysroot: None, - } - .builder() - .expect("unable to create bindgen builder for libcoap bindings from ESP-IDF"); - - let clang_target = if esp_arch.starts_with("riscv32") { - "riscv32" - } else { - esp_arch.as_str() - }; - let short_target = if esp_arch.starts_with("riscv32") { - "riscv" - } else { - esp_arch.as_str() - }; - let target_mcu = get_target_mcu(); - - bindgen_builder - .clang_args(&esp_clang_args) - .clang_arg("-target") - .clang_arg(clang_target) - .clang_arg("-DESP_PLATFORM") - .clang_arg("-DLWIP_IPV4=1") - .clang_arg("-DLWIP_IPV6=1") - .clang_arg("-DconfigUSE_PASSIVE_IDLE_HOOK=1") - .clang_arg(format!("-I{}/components/newlib/platform_include", esp_idf_path)) - .clang_arg(format!("-I{}/components/lwip/port/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/lwip/port/esp32xx/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/lwip/lwip/src/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/lwip/port/freertos/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/esp_system/include", esp_idf_path)) - .clang_arg(format!( - "-I{}/components/freertos/config/include/freertos", - esp_idf_path - )) - .clang_arg(format!("-I{}/components/freertos/esp_additions/include", esp_idf_path)) - .clang_arg(format!( - "-I{}/components/freertos/esp_additions/include/freertos", - esp_idf_path - )) - .clang_arg(format!( - "-I{}/components/freertos/esp_additions/arch/{}/include", - esp_idf_path, short_target - )) // for older espidf - .clang_arg(format!( - "-I{}/components/freertos/config/{}/include", - esp_idf_path, short_target - )) // for newer espidf - .clang_arg(format!("-I{}/components/{}/include", esp_idf_path, short_target)) - .clang_arg(format!( - "-I{}/components/{}/{}/include", - esp_idf_path, short_target, target_mcu - )) - .clang_arg(format!("-I{}/components/esp_hw_support/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/esp_common/include", esp_idf_path)) - .clang_arg(format!( - "-I{}/components/freertos/FreeRTOS-Kernel-SMP/include", - esp_idf_path - )) - .clang_arg(format!( - "-I{}/components/freertos/FreeRTOS-Kernel-SMP/portable/{}/include/freertos", - esp_idf_path, short_target - )) - .clang_arg(format!("-I{}/components/soc/{}/include", esp_idf_path, target_mcu)) - .clang_arg(format!("-I{}/components/heap/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/esp_rom/include", esp_idf_path)) - .clang_arg(format!( - "-I{}/managed_components/espressif__coap/port/include", - esp_idf_buildroot - )) - .clang_arg(format!( - "-I{}/managed_components/espressif__coap/libcoap/include", - esp_idf_buildroot - )) - .clang_arg(format!("-I{}/build/config/", esp_idf_buildroot)) - .allowlist_type("epoll_event") -} - -fn get_builder() -> bindgen::Builder { - bindgen::Builder::default().blocklist_type("epoll_event") -} +fn main() -> Result<()> { + let out_dir = PathBuf::from( + env::var_os("OUT_DIR").expect("no OUT_DIR was provided (are we not running as a cargo build script?)"), + ); -fn build_vendored_library( - out_dir: &OsString, - dtls_backend: Option<&DtlsBackend>, - mut builder: bindgen::Builder, -) -> bindgen::Builder { - let libcoap_src_dir = Path::new(&out_dir).join("libcoap"); + let requested_features: EnumSet = EnumSet::::all() + .iter() + .filter(|feat| std::env::var_os(format!("CARGO_FEATURE_{}", feat.cargo_feature_var_name())).is_some()) + .collect(); - // Even though libcoap supports out-of-source builds, autogen.sh (or the corresponding - // autotools) modify files in the source tree, which causes verification problems when - // running cargo package. - // Therefore, we copy the libcoap source over to the output directory and build from there. - let copy_options = fs_extra::dir::CopyOptions { - overwrite: true, - ..Default::default() - }; - match std::fs::remove_dir_all(&libcoap_src_dir) { - Ok(_) => {}, - Err(e) if e.kind() == ErrorKind::NotFound => {}, - e => e.expect("unable to clear libcoap build directory"), + let requested_dtls_backend: Option = match env::var("LIBCOAP_RS_DTLS_BACKEND") { + Ok(v) => v.parse().map(Some), + Err(VarError::NotPresent) => Ok(None), + Err(e) => Err(anyhow!(e)), } - fs_extra::dir::copy( - Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("libcoap"), - Path::new(&out_dir), - ©_options, - ) - .expect("unable to prepare libcoap build source directory"); - let current_dir_backup = env::current_dir().expect("unable to get current directory"); - env::set_current_dir(&libcoap_src_dir).expect("unable to change to libcoap build dir"); - Command::new(libcoap_src_dir.join("autogen.sh")) - .status() - .expect("unable to execute autogen.sh"); - let mut build_config = autotools::Config::new(&libcoap_src_dir); - build_config.out_dir(out_dir); - if let Some(dtls_backend) = dtls_backend { - build_config - .enable("dtls", None) - .with(dtls_backend.to_string().as_str(), None); - - // If DTLS is vendored we need to tell libcoap about the vendored version - match dtls_backend { - DtlsBackend::TinyDtls => { - // We do not ship tinydtls with our source distribution. Instead, we use tinydtls-sys. - build_config.without("submodule-tinydtls", None); - - // If tinydtls-sys is built with the vendored feature, the library is built alongside - // the Rust crate. To use the version built by the tinydtls-sys build script, we use the - // environment variables set by the build script. - if let Some(tinydtls_include) = env::var_os("DEP_TINYDTLS_INCLUDE") { - build_config.env( - "TinyDTLS_CFLAGS", - format!( - "-I{} -I{}", - tinydtls_include - .to_str() - .expect("DEP_TINYDTLS_INCLUDE is not a valid string"), - Path::new(&tinydtls_include) - .join("tinydtls") - .to_str() - .expect("DEP_TINYDTLS_INCLUDE is not a valid string") - ), - ); - }; - - if let Some(tinydtls_libs) = env::var_os("DEP_TINYDTLS_LIBS") { - build_config.env( - "TinyDTLS_LIBS", - format!( - "-L{}", - tinydtls_libs.to_str().expect("DEP_TINYDTLS_LIBS is invalid string") - ), - ); - - build_config.env( - "PKG_CONFIG_PATH", - Path::new(tinydtls_libs.as_os_str()) - .join("lib") - .join("pkgconfig") - .into_os_string(), - ); - } - }, - DtlsBackend::OpenSsl => { - // Set include path according to the path provided by openssl-sys (relevant if - // openssl-sys is vendored) - if let Some(openssl_include) = env::var_os("DEP_OPENSSL_INCLUDE") { - build_config.env( - "OpenSSL_CFLAGS", - format!( - "-I{}", - openssl_include.to_str().expect("DEP_OPENSSL_INCLUDE is invalid path") - ), - ); - build_config.env( - "PKG_CONFIG_PATH", - Path::new(openssl_include.as_os_str()) - .parent() - .expect("DEP_OPENSSL_INCLUDE has no parent directory") - .join("lib") - .join("pkgconfig") - .into_os_string(), - ); + .context("unable to parse environment variable LIBCOAP_RS_DTLS_BACKEND")?; + + let chosen_build_system = match env::var("LIBCOAP_RS_BUILD_SYSTEM") { + Ok(v) => Ok(Some(v)), + Err(VarError::NotPresent) => Ok(None), + Err(e) => Err(e).context("unable to parse environment variable LIBCOAP_BUILD_SYSTEM"), + }?; + + let bypass_compile_time_feature_checks = match env::var("LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS") { + Ok(v) if v == "0" => Ok(false), + Ok(v) => Ok(true), + Err(VarError::NotPresent) => Ok(false), + Err(e) => Err(e).context("unable to parse environment variable LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS"), + }?; + + let mut build_system: Box = match chosen_build_system.as_ref().map(String::as_str) { + Some("pkgconfig") => PkgConfigBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend) + .context("unable to link libcoap using force-configured build system pkgconfig") + .map(|v| Box::::from(Box::new(v))), + Some(v) => Err(anyhow!("unknown build system {v}")), + None => { + #[cfg(target_os = "espidf")] + { + link_libcoap_espidf() + } + #[cfg(not(target_os = "espidf"))] + { + #[cfg(unix)] + { + link_libcoap_unix(out_dir, requested_features, requested_dtls_backend) } - }, - DtlsBackend::MbedTls => { - // Set include path according to the path provided by mbedtls-sys (relevant if - // mbedtls-sys is vendored). - // libcoap doesn't support overriding the MbedTLS CFLAGS, but doesn't set those - // either, so we just set CFLAGS and hope they propagate. - if let Some(mbedtls_include) = env::var_os("DEP_MBEDTLS_INCLUDE") { - // the config.h of mbedtls-sys-auto is generated separately from all other - // includes in the root of mbedtls-sys-auto's OUT_DIR. - // In order to let libcoap read use the correct config file, we need to copy - // this file into our own OUT_DIR under include/mbedtls/config.h, so that we - // can then set OUT_DIR/include as an additional include path. - let config_h = env::var_os("DEP_MBEDTLS_CONFIG_H") - .expect("DEP_MBEDTLS_INCLUDE is set but DEP_MBEDTLS_CONFIG_H is not"); - let config_path = Path::new(&config_h); - let out_include = Path::new(&out_dir).join("include"); - std::fs::create_dir_all(out_include.join("mbedtls")) - .expect("unable to prepare include directory for mbedtls config.h"); - std::fs::copy(config_path, out_include.join("mbedtls").join("config.h")) - .expect("unable to copy mbedtls config.h to include directory"); - let mbedtls_library_path = config_path - .parent() - .expect("DEP_MBEDTLS_CONFIG_H has no parent directory") - .join("build") - .join("library"); - build_config.env( - "MbedTLS_CFLAGS", - format!( - "-I{} -I{}", - out_include.to_str().expect("OUT_DIR is not a valid string"), - mbedtls_include - .to_str() - .expect("DEP_MBEDTLS_INCLUDE is not a valid string") - ), - ); - build_config.env( - "MbedTLS_LIBS", - format!( - "-L{0} -l:libmbedtls.a -l:libmbedcrypto.a -l:libmbedx509.a", - mbedtls_library_path - .to_str() - .expect("DEP_MBEDTLS_CONFIG_H is not a valid string"), - ), - ); + #[cfg(windows)] + { + link_libcoap_windows() } - }, - DtlsBackend::GnuTls => { - // Vendoring not supported - }, - } - } else { - build_config.disable("dtls", None); - } - build_config - // Disable shared library compilation because the vendored library will always be - // statically linked - .disable("shared", None) - // Disable any documentation for vendored C library - .disable("documentation", None) - .disable("doxygen", None) - .disable("manpages", None) - // This would install the license into the documentation directory, but we don't use the - // generated documentation anywhere. - .disable("license-install", None) - // Disable tests and examples as well as test coverage - .disable("tests", None) - .disable("examples", None) - .disable("gcov", None); - - // Enable debug symbols if enabled in Rust - match env::var_os("DEBUG") - .expect("env variable DEBUG that should have been set by cargo is not set") - .to_str() - .expect("env variable DEBUG is not valid") - { - "0" | "false" => {}, - _ => { - build_config.with("debug", None); + } }, - } - // Enable dependency features based on selected cargo features. - build_config - .enable("oscore", Some(if cfg!(feature = "oscore") { "yes" } else { "no" })) - .enable("ipv4-support", Some(if cfg!(feature = "ipv4") { "yes" } else { "no" })) - .enable("ipv6-support", Some(if cfg!(feature = "ipv6") { "yes" } else { "no" })) - .enable( - "af-unix-support", - Some(if cfg!(feature = "af-unix") { "yes" } else { "no" }), - ) - .enable("tcp", Some(if cfg!(feature = "tcp") { "yes" } else { "no" })) - .enable( - "websockets", - Some(if cfg!(feature = "websockets") { "yes" } else { "no" }), - ) - .enable("async", Some(if cfg!(feature = "async") { "yes" } else { "no" })) - .enable( - "observe-persist", - Some(if cfg!(feature = "observe-persist") { "yes" } else { "no" }), - ) - .enable("q-block", Some(if cfg!(feature = "q-block") { "yes" } else { "no" })) - .enable( - "thread-safe", - Some(if cfg!(feature = "thread-safe") { "yes" } else { "no" }), - ) - .enable( - "thread-recursive-lock-detection", - Some(if cfg!(feature = "thread-recursive-lock-detection") { - "yes" - } else { - "no" - }), - ) - .enable( - "small-stack", - Some(if cfg!(feature = "small-stack") { "yes" } else { "no" }), - ) - .enable("server-mode", Some(if cfg!(feature = "server") { "yes" } else { "no" })) - .enable("client-mode", Some(if cfg!(feature = "client") { "yes" } else { "no" })) - .with("epoll", Some(if cfg!(feature = "epoll") { "yes" } else { "no" })); - - // Run build - let dst = build_config.build(); + }?; - // Add the built library to the search path + let bindings_file = build_system.generate_bindings()?; println!( - "cargo:rustc-link-search=native={}", - dst.join("lib") - .to_str() - .expect("libcoap build output dir is not a valid string") + "cargo:rustc-env=BINDINGS_FILE={}", + bindings_file.canonicalize()?.display() ); - println!( - "cargo:include={}", - dst.join("include") - .to_str() - .expect("libcoap build output dir is not a valid string") - ); - builder = builder - .clang_arg(format!( - "-I{}", - dst.join("include") - .to_str() - .expect("libcoap build output dir is not a valid string") - )) - .clang_arg(format!( - "-L{}", - dst.join("lib") - .to_str() - .expect("libcoap build output dir is not a valid string") - )); - env::set_current_dir(current_dir_backup).expect("unable to switch back to source dir"); - builder -} - -fn main() { - println!("cargo::rustc-check-cfg=cfg(feature_checks_available)"); - println!("cargo::rustc-check-cfg=cfg(non_inlined_coap_send_rst)"); - println!("cargo:rerun-if-changed=src/libcoap/"); - println!("cargo:rerun-if-changed=src/wrapper.h"); - // Read required environment variables. - let out_dir = env::var_os("OUT_DIR").expect("unsupported OUT_DIR"); - let target_os = env::var("CARGO_CFG_TARGET_OS").expect("invalid TARGET_OS environment variable"); - let mut bindgen_builder = match target_os.as_str() { - "espidf" => get_builder_espidf(), - _ => get_builder(), - }; - - let mut dtls_backend = Option::None; - if cfg!(feature = "dtls") { - // We can only select one TLS backend at a time for libcoap, but cargo does not support mutually - // exclusive features, and it would be really bad if a project that uses multiple dependencies - // which depend on different TLS backends would not compile. - // Therefore, if multiple TLS backend features are enabled, we choose one based on the following - // priority order: gnutls > openssl > mbedtls > tinydtls, matching the order specified in - // https://github.com/obgm/libcoap/blob/develop/configure.ac#L494 - let mut multiple_backends = false; - if cfg!(feature = "dtls_backend_tinydtls") { - dtls_backend = Some(DtlsBackend::TinyDtls); - } - if cfg!(feature = "dtls_backend_mbedtls") { - if dtls_backend.is_some() { - multiple_backends = true; - } - println!("cargo:rerun-if-env-changed=MBEDTLS_LIBRARY_PATH"); - dtls_backend = Some(DtlsBackend::MbedTls); - } - if cfg!(feature = "dtls_backend_openssl") { - if dtls_backend.is_some() { - multiple_backends = true; - } - dtls_backend = Some(DtlsBackend::OpenSsl); - } - if cfg!(feature = "dtls_backend_gnutls") { - if dtls_backend.is_some() { - multiple_backends = true; - } - dtls_backend = Some(DtlsBackend::GnuTls); - } - if multiple_backends { - // more than one backend was set, so unwrapping is ok here. - println!("cargo:warning=Multiple DTLS backends enabled for libcoap-sys. Only one can be enabled, choosing {:?} as the backend to use. This may cause problems.", dtls_backend.as_ref().unwrap()); - } - if dtls_backend.is_none() { - println!("cargo:warning=No DTLS backend selected for libcoap-sys, aborting build."); - panic!("No DTLS backend selected for libcoap-sys, aborting build") - } + if build_system.version() < Version::from(MINIMUM_LIBCOAP_VERSION) { + println!("cargo:warning=The linked version of libcoap is lower than the minimal version required for libcoap-sys ({}), this will most likely cause errors.", MINIMUM_LIBCOAP_VERSION); } - // Build vendored library if feature was set. - if cfg!(feature = "vendored") && target_os.as_str() != "espidf" { - bindgen_builder = build_vendored_library(&out_dir, dtls_backend.as_ref(), bindgen_builder); - }; - - if target_os.as_str() != "espidf" { - // Tell cargo to link libcoap. - println!( - "cargo:rustc-link-lib={}{}", - cfg!(feature = "static").then(|| "static=").unwrap_or("dylib="), - format!( - "coap-3-{}", - &dtls_backend - .as_ref() - .map(|v| v.to_string()) - .unwrap_or_else(|| "notls".to_string()) - ) - .as_str() - ); - - // For the DTLS libraries, we need to tell cargo which external libraries to link. - // Note that these linker instructions have to be added *after* the linker instruction - // for libcoap itself, as some linkers require dependencies to be in reverse order. - if let Some(dtls_backend) = dtls_backend { - match dtls_backend { - DtlsBackend::TinyDtls => { - // Handled by tinydtls-sys - }, - DtlsBackend::OpenSsl => { - // Handled by openssl-sys - }, - DtlsBackend::MbedTls => { - // If mbedtls is vendored, mbedtls-sys-auto already takes care of linking. - if env::var_os("DEP_MBEDTLS_INCLUDE").is_none() { - // We aren't using mbedtls-sys-auto if we aren't vendoring (as it doesn't support - // mbedtls >= 3.0.0), so we need to tell cargo to link to mbedtls ourselves. - - // Try to find mbedtls using pkg-config, will emit cargo link statements if successful - if pkg_config::Config::new() - .statik(cfg!(feature = "static")) - .probe("mbedtls") - .is_err() - { - // couldn't find using pkg-config, just try linking with given library - // search path. - println!("cargo:rustc-link-lib=mbedtls",); - println!("cargo:rustc-link-lib=mbedx509",); - println!("cargo:rustc-link-lib=mbedcrypto",); - } - } - }, - DtlsBackend::GnuTls => { - // gnutls-sys is unmaintained, so we need to link to gnutls ourselves. - - // try pkg-config - if probe_library("gnutls").is_err() { - // if that doesn't work, try using the standard library search path. - println!("cargo:rustc-link-lib=gnutls") - } - }, + match build_system.detected_features() { + Some(detected_features) => { + if !bypass_compile_time_feature_checks && !requested_features.is_subset(detected_features) { + let missing_features = requested_features.difference(detected_features); + bail!( + concat!( + "the libcoap-rs compile-time feature check has determined that the following enabled features\n", + "are not supported by the used C library: {}.\n", + "If you are certain that this check is mistaken (e.g., because you are cross-compiling), you\n", + "may bypass this check by setting the `LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS` environment\n", + "variable to any non-zero value.\n", + "Be aware, however, that this might lead to more cryptic errors if the requested features are\n", + "not available after all." + ), + missing_features + .iter() + .map(|v| v.as_str()) + .collect::>() + .join(", ") + ); } - } + }, + None => { + println!("cargo:warning=The used build system for libcoap-sys does not support compile-time feature checks. Missing features may therefore only be detected during runtime."); + }, } - let libcoap_defines = Rc::new(RefCell::new(LibcoapMetadata::default())); - - let cfg_info = Box::new(CoapDefineParser { - defines: Rc::clone(&libcoap_defines), - }); + Ok(()) +} - bindgen_builder = bindgen_builder - .header("src/wrapper.h") - .default_enum_style(EnumVariation::Rust { non_exhaustive: true }) - // Causes invalid syntax for some reason, so we have to disable it. - .generate_comments(false) - .dynamic_link_require_all(true) - .allowlist_function("(oscore|coap)_.*") - .allowlist_type("(oscore|coap)_.*") - .allowlist_var("(oscore|coap)_.*") - .allowlist_function("(OSCORE|COAP)_.*") - .allowlist_type("(OSCORE|COAP)_.*") - .allowlist_var("(OSCORE|COAP|LIBCOAP)_.*") - // We use the definitions made by the libc crate instead - .blocklist_type("sockaddr(_in|_in6)?") - .blocklist_type("in6?_(addr|port)(_t)?") - .blocklist_type("in6_addr__bindgen_ty_1") - .blocklist_type("(__)?socklen_t") - .blocklist_type("fd_set") - .blocklist_type("sa_family_t") - .blocklist_type("(__)?time_t") - .blocklist_type("__fd_mask") - // Are generated because they are typedef-ed inside of the C headers, blocklisting them - // will instead replace them with the appropriate rust types. - // See https://github.com/rust-lang/rust-bindgen/issues/1215 for an open issue concerning - // this problem. - .blocklist_type("__(u)?int(8|16|32|64|128)_t") - .size_t_is_usize(true) - .parse_callbacks(cfg_info); - if !cfg!(feature = "vendored") { - // Triggers a rebuild on every cargo build invocation if used for the vendored version, as - // the included headers seem to come from our built version. - // Should be fine though, as we already printed `cargo:rerun-if-changed=src/libcoap/` at the - // start of the file. - bindgen_builder = bindgen_builder.parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); - } - let bindings = bindgen_builder.generate().expect("unable to generate bindings"); +#[cfg(target_os = "espidf")] +fn link_libcoap_espidf() -> Result { + // For ESP-IDF: Use esp-idf tooling. + todo!() +} - // Check if required features are available in libcoap. - let libcoap_defines = libcoap_defines.take(); - if libcoap_defines.feature_defines_available { - println!("cargo:rustc-cfg=feature_checks_available"); - for feature in COMPILE_TIME_FEATURE_CHECKS { - let feature_env_var_name = "CARGO_FEATURE_".to_string() + &feature.replace('-', "_").to_uppercase(); - if env::var(&feature_env_var_name).is_ok() && !libcoap_defines.feature_defines.contains(feature) { - panic!("Required feature {feature} is not available in the used version of libcoap!"); - } - } - if dtls_backend != libcoap_defines.dtls_backend { - // Should be fine, as applications should expect that the DTLS library could differ. - println!("cargo:warning=DTLS library used by libcoap does not match chosen one. This might lead to issues.") - } +#[cfg(unix)] +fn link_libcoap_unix( + out_dir: PathBuf, + requested_features: EnumSet, + requested_dtls_backend: Option, +) -> Result> { + // For unix-like systems: Use pkg-config. + if cfg!(feature = "vendored") { + todo!() } else { - println!("cargo:warning=The used version of libcoap does not provide a coap_defines.h file, either because it is too old (<4.3.5) or because this file is somehow not included. Compile-time feature checks are not available, and the availability of some features (small-stack, IPv4/IPv6,) cannot be asserted at all!"); + PkgConfigBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend) + .map(|v| Box::::from(Box::new(v))) } +} - let out_path = PathBuf::from(out_dir); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("unable to write generated bindings to file"); +#[cfg(windows)] +fn link_libcoap_windows() -> Result { + // For Windows, we currently only support manual setup (cmake would be a possible alternative). + todo!() } diff --git a/libcoap-sys/build/metadata.rs b/libcoap-sys/build/metadata.rs new file mode 100644 index 00000000..eedc140c --- /dev/null +++ b/libcoap-sys/build/metadata.rs @@ -0,0 +1,200 @@ +use std::{ + fmt::{Display, Formatter, Write}, + str::FromStr, +}; + +use anyhow::anyhow; +use enumset::{EnumSet, EnumSetType}; + +pub const MINIMUM_LIBCOAP_VERSION: &str = "4.3.5"; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct LibcoapDefineInfo { + pub version: Option, + pub supported_features: EnumSet, +} + +#[derive(EnumSetType, Debug)] +pub enum LibcoapFeature { + AfUnix, + Async, + Client, + SmallStack, + Tcp, + Epoll, + Ipv4, + Ipv6, + Oscore, + QBlock, + Server, + ThreadRecursiveLockDetection, + ThreadSafe, + Dtls, + ObservePersist, + WebSockets, + SecureWebSockets, + DtlsCid, + DtlsPsk, + DtlsPki, + DtlsPkcs11, + DtlsRpk, + Tls, +} + +impl LibcoapFeature { + pub fn define_name(&self) -> Option<&'static str> { + match self { + LibcoapFeature::AfUnix => Some("COAP_AF_UNIX_SUPPORT"), + LibcoapFeature::Async => Some("COAP_ASYNC_SUPPORT"), + LibcoapFeature::Client => Some("COAP_CLIENT_SUPPORT"), + LibcoapFeature::SmallStack => Some("COAP_CONSTRAINED_STACK"), + LibcoapFeature::Tcp => Some("COAP_DISABLE_TCP"), // TODO invert + LibcoapFeature::Epoll => Some("COAP_EPOLL_SUPPORT"), + LibcoapFeature::Ipv4 => Some("COAP_IPV4_SUPPORT"), + LibcoapFeature::Ipv6 => Some("COAP_IPV6_SUPPORT"), + LibcoapFeature::Oscore => Some("COAP_OSCORE_SUPPORT"), + // TODO proxy support? + LibcoapFeature::QBlock => Some("COAP_Q_BLOCK_SUPPORT"), + LibcoapFeature::Server => Some("COAP_SERVER_SUPPORT"), + LibcoapFeature::ThreadRecursiveLockDetection => Some("COAP_THREAD_RECURSIVE_CHECK"), + LibcoapFeature::ThreadSafe => Some("COAP_THREAD_SAFE"), + LibcoapFeature::Dtls => None, // TODO has multiple defines + LibcoapFeature::ObservePersist => Some("COAP_WITH_OBSERVE_PERSIST"), + LibcoapFeature::WebSockets => Some("COAP_WS_SUPPORT"), + _ => None, + } + } + + pub fn features_from_define(define_name: &str, define_value: i64) -> EnumSet { + match define_name { + "COAP_AF_UNIX_SUPPORT" => EnumSet::from(LibcoapFeature::AfUnix), + "COAP_ASYNC_SUPPORT" => EnumSet::from(LibcoapFeature::Async), + "COAP_CLIENT_SUPPORT" => EnumSet::from(LibcoapFeature::Client), + "COAP_CONSTRAINED_STACK" => EnumSet::from(LibcoapFeature::SmallStack), + "COAP_EPOLL_SUPPORT" => EnumSet::from(LibcoapFeature::Epoll), + "COAP_IPV4_SUPPORT" => EnumSet::from(LibcoapFeature::Ipv4), + "COAP_IPV6_SUPPORT" => EnumSet::from(LibcoapFeature::Ipv6), + "COAP_OSCORE_SUPPORT" => EnumSet::from(LibcoapFeature::Oscore), + "COAP_Q_BLOCK_SUPPORT" => EnumSet::from(LibcoapFeature::QBlock), + "COAP_SERVER_SUPPORT" => EnumSet::from(LibcoapFeature::Server), + "COAP_THREAD_RECURSIVE_CHECK" => EnumSet::from(LibcoapFeature::ThreadRecursiveLockDetection), + "COAP_THREAD_SAFE" => EnumSet::from(LibcoapFeature::ThreadSafe), + "COAP_WITH_OBSERVE_PERSIST" => EnumSet::from(LibcoapFeature::ObservePersist), + "COAP_WS_SUPPORT" => EnumSet::from(LibcoapFeature::WebSockets), + "COAP_DISABLE_TCP" => { + if define_value == 0 { + EnumSet::from(LibcoapFeature::Tcp) + } else { + EnumSet::empty() + } + }, + "COAP_WITH_LIBGNUTLS" + | "COAP_WITH_LIBMBEDTLS" + | "COAP_WITH_LIBOPENSSL" + | "COAP_WITH_LIBTINYDTLS" + | "COAP_WITH_LIBWOLFSSL" => EnumSet::from(LibcoapFeature::Dtls), + _ => EnumSet::empty(), + } + } + + pub fn configure_flag_name(&self) -> Option<&'static str> { + match self { + LibcoapFeature::AfUnix => Some("af-unix"), + LibcoapFeature::Async => Some("async"), + LibcoapFeature::Client => Some("client"), + LibcoapFeature::SmallStack => Some("small-stack"), + LibcoapFeature::Tcp => Some("tcp"), + LibcoapFeature::Epoll => Some("epoll"), + LibcoapFeature::Ipv4 => Some("ipv4"), + LibcoapFeature::Ipv6 => Some("ipv6"), + LibcoapFeature::Oscore => Some("oscore"), + LibcoapFeature::QBlock => Some("q-block"), + LibcoapFeature::Server => Some("server"), + LibcoapFeature::ThreadRecursiveLockDetection => Some("thread-recursive-lock-detection"), + LibcoapFeature::ThreadSafe => Some("thread-safe"), + LibcoapFeature::Dtls => Some("dtls"), + LibcoapFeature::ObservePersist => Some("observe-persist"), + LibcoapFeature::WebSockets => Some("websockets"), + _ => None, + } + } + + pub fn cargo_feature_var_name(&self) -> String { + self.as_str().to_uppercase().replace('-', "_") + } + + pub fn as_str(&self) -> &'static str { + match self { + LibcoapFeature::AfUnix => "af-unix", + LibcoapFeature::Async => "async", + LibcoapFeature::Client => "client", + LibcoapFeature::SmallStack => "small-stack", + LibcoapFeature::Tcp => "tcp", + LibcoapFeature::Epoll => "epoll", + LibcoapFeature::Ipv4 => "ipv4", + LibcoapFeature::Ipv6 => "ipv6", + LibcoapFeature::Oscore => "oscore", + LibcoapFeature::QBlock => "q-block", + LibcoapFeature::Server => "server", + LibcoapFeature::ThreadRecursiveLockDetection => "thread-recursive-lock-detection", + LibcoapFeature::ThreadSafe => "thread-safe", + LibcoapFeature::Dtls => "dtls", + LibcoapFeature::ObservePersist => "observe-persist", + LibcoapFeature::WebSockets => "websockets", + LibcoapFeature::SecureWebSockets => "secure-websockets", + LibcoapFeature::DtlsCid => "dtls-cid", + LibcoapFeature::DtlsPsk => "dtls-psk", + LibcoapFeature::DtlsPki => "dtls-pki", + LibcoapFeature::DtlsPkcs11 => "dtls-pkcs11", + LibcoapFeature::DtlsRpk => "dtls-rpk", + LibcoapFeature::Tls => "tls", + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DtlsBackend { + GnuTls, + OpenSsl, + MbedTls, + TinyDtls, + WolfSsl, +} + +impl FromStr for DtlsBackend { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "gnutls" => Ok(DtlsBackend::GnuTls), + "openssl" => Ok(DtlsBackend::OpenSsl), + "mbedtls" => Ok(DtlsBackend::MbedTls), + "tinydtls" => Ok(DtlsBackend::TinyDtls), + "wolfssl" => Ok(DtlsBackend::WolfSsl), + v => Err(anyhow!("unknown DTLS backend {v}")), + } + } +} + +impl Display for DtlsBackend { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(self.as_str()) + } +} + +impl DtlsBackend { + pub fn pkg_config_suffix(&self) -> &'static str { + // just keeping this here in case we ever need to change this definition for some libraries. + self.as_str() + } + + pub fn as_str(&self) -> &'static str { + match self { + DtlsBackend::GnuTls => "gnutls", + DtlsBackend::OpenSsl => "openssl", + DtlsBackend::MbedTls => "mbedtls", + DtlsBackend::TinyDtls => "tinydtls", + DtlsBackend::WolfSsl => "wolfssl", + } + } +} diff --git a/libcoap-sys/old_build.rs b/libcoap-sys/old_build.rs new file mode 100644 index 00000000..96c470f6 --- /dev/null +++ b/libcoap-sys/old_build.rs @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: BSD-2-CLAUSE +/* + * build.rs - build script for libcoap Rust bindings. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ + +use std::{ + cell::RefCell, + collections::BTreeSet, + default::Default, + env, + ffi::{OsStr, OsString}, + fmt::{Debug, Display}, + io::ErrorKind, + path::{Path, PathBuf}, + process::Command, + rc::Rc, +}; + +mod bindings; +mod build_system; +mod metadata; + +use bindgen::{ + callbacks::{IntKind, ParseCallbacks}, + EnumVariation, +}; +use pkg_config::probe_library; +use version_compare::{Cmp, Version}; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DtlsBackend { + GnuTls, + OpenSsl, + MbedTls, + TinyDtls, +} +impl Display for DtlsBackend { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + DtlsBackend::GnuTls => "gnutls", + DtlsBackend::OpenSsl => "openssl", + DtlsBackend::MbedTls => "mbedtls", + DtlsBackend::TinyDtls => "tinydtls", + } + .to_string(); + write!(f, "{}", str) + } +} + +fn get_target_mcu() -> &'static str { + let cfg_flags = embuild::espidf::sysenv::cfg_args().expect("missing cfg flags from IDF"); + let mcus = [ + "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32c2", "esp32h2", "esp32c5", "esp32c6", "esp32p4", + ]; + for mcu in mcus { + if cfg_flags.get(mcu).is_some() { + return mcu; + } + } + panic!("unknown ESP target MCU, please add target to libcoap-sys build.rs file!") +} + +fn get_builder_espidf() -> bindgen::Builder { + embuild::espidf::sysenv::output(); + let esp_idf_path = embuild::espidf::sysenv::idf_path().expect("missing IDF path"); + let esp_idf_buildroot = env::var("DEP_ESP_IDF_ROOT").expect("DEP_ESP_IDF_ROOT is not set"); + let esp_include_path = embuild::espidf::sysenv::cincl_args().expect("missing IDF cincl args"); + let embuild_env = embuild::espidf::sysenv::env_path().expect("missing IDF env path"); + let esp_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH is not set"); + + // Determine compiler path + // SAFETY: Always safe to call in a single-threaded environment (see docs of env::set_var). + unsafe { env::set_var("PATH", embuild_env) }; + let cmake_info = embuild::cmake::Query::new( + &Path::new(&esp_idf_buildroot).join("build"), + "cargo", + &[ + embuild::cmake::file_api::ObjKind::Codemodel, + embuild::cmake::file_api::ObjKind::Toolchains, + embuild::cmake::file_api::ObjKind::Cache, + ], + ) + .expect("unable to query cmake API for compiler path") + .get_replies() + .expect("unable to get cmake query replies for compiler path"); + let compiler = cmake_info + .get_toolchains() + .map_err(|_e| "Can't get toolchains") + .and_then(|mut t| { + t.take(embuild::cmake::file_api::codemodel::Language::C) + .ok_or("No C toolchain") + }) + .and_then(|t| t.compiler.path.ok_or("No compiler path set")) + .expect("unable to determine compiler path"); + + // Parse include arguments + // Regexes are correct and never change, therefore it is ok to unwrap here. + let arg_splitter = regex::Regex::new(r##"(?:[^\\]"[^"]*[^\\]")?(\s)"##).unwrap(); + let apostrophe_remover = regex::Regex::new(r##"^"(?.*)"$"##).unwrap(); + let esp_clang_args = arg_splitter + .split(esp_include_path.args.as_str()) + .map(|x| apostrophe_remover.replace(x.trim(), "$content").to_string()) + .collect::>(); + let bindgen_builder = embuild::bindgen::Factory { + clang_args: esp_clang_args.clone(), + linker: Some(compiler), + mcu: None, + force_cpp: false, + sysroot: None, + } + .builder() + .expect("unable to create bindgen builder for libcoap bindings from ESP-IDF"); + + let clang_target = if esp_arch.starts_with("riscv32") { + "riscv32" + } else { + esp_arch.as_str() + }; + let short_target = if esp_arch.starts_with("riscv32") { + "riscv" + } else { + esp_arch.as_str() + }; + let target_mcu = get_target_mcu(); + + bindgen_builder + .clang_args(&esp_clang_args) + .clang_arg("-target") + .clang_arg(clang_target) + .clang_arg("-DESP_PLATFORM") + .clang_arg("-DLWIP_IPV4=1") + .clang_arg("-DLWIP_IPV6=1") + .clang_arg("-DconfigUSE_PASSIVE_IDLE_HOOK=1") + .clang_arg(format!("-I{}/components/newlib/platform_include", esp_idf_path)) + .clang_arg(format!("-I{}/components/lwip/port/include", esp_idf_path)) + .clang_arg(format!("-I{}/components/lwip/port/esp32xx/include", esp_idf_path)) + .clang_arg(format!("-I{}/components/lwip/lwip/src/include", esp_idf_path)) + .clang_arg(format!("-I{}/components/lwip/port/freertos/include", esp_idf_path)) + .clang_arg(format!("-I{}/components/esp_system/include", esp_idf_path)) + .clang_arg(format!( + "-I{}/components/freertos/config/include/freertos", + esp_idf_path + )) + .clang_arg(format!("-I{}/components/freertos/esp_additions/include", esp_idf_path)) + .clang_arg(format!( + "-I{}/components/freertos/esp_additions/include/freertos", + esp_idf_path + )) + .clang_arg(format!( + "-I{}/components/freertos/esp_additions/arch/{}/include", + esp_idf_path, short_target + )) // for older espidf + .clang_arg(format!( + "-I{}/components/freertos/config/{}/include", + esp_idf_path, short_target + )) // for newer espidf + .clang_arg(format!("-I{}/components/{}/include", esp_idf_path, short_target)) + .clang_arg(format!( + "-I{}/components/{}/{}/include", + esp_idf_path, short_target, target_mcu + )) + .clang_arg(format!("-I{}/components/esp_hw_support/include", esp_idf_path)) + .clang_arg(format!("-I{}/components/esp_common/include", esp_idf_path)) + .clang_arg(format!( + "-I{}/components/freertos/FreeRTOS-Kernel-SMP/include", + esp_idf_path + )) + .clang_arg(format!( + "-I{}/components/freertos/FreeRTOS-Kernel-SMP/portable/{}/include/freertos", + esp_idf_path, short_target + )) + .clang_arg(format!("-I{}/components/soc/{}/include", esp_idf_path, target_mcu)) + .clang_arg(format!("-I{}/components/heap/include", esp_idf_path)) + .clang_arg(format!("-I{}/components/esp_rom/include", esp_idf_path)) + .clang_arg(format!( + "-I{}/managed_components/espressif__coap/port/include", + esp_idf_buildroot + )) + .clang_arg(format!( + "-I{}/managed_components/espressif__coap/libcoap/include", + esp_idf_buildroot + )) + .clang_arg(format!("-I{}/build/config/", esp_idf_buildroot)) + .allowlist_type("epoll_event") +} + +fn get_builder() -> bindgen::Builder { + bindgen::Builder::default().blocklist_type("epoll_event") +} + +fn build_vendored_library( + out_dir: &OsString, + dtls_backend: Option<&DtlsBackend>, + mut builder: bindgen::Builder, +) -> bindgen::Builder { + let libcoap_src_dir = Path::new(&out_dir).join("libcoap"); + + // Even though libcoap supports out-of-source builds, autogen.sh (or the corresponding + // autotools) modify files in the source tree, which causes verification problems when + // running cargo package. + // Therefore, we copy the libcoap source over to the output directory and build from there. + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + ..Default::default() + }; + match std::fs::remove_dir_all(&libcoap_src_dir) { + Ok(_) => {}, + Err(e) if e.kind() == ErrorKind::NotFound => {}, + e => e.expect("unable to clear libcoap build directory"), + } + fs_extra::dir::copy( + Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("libcoap"), + Path::new(&out_dir), + ©_options, + ) + .expect("unable to prepare libcoap build source directory"); + let current_dir_backup = env::current_dir().expect("unable to get current directory"); + env::set_current_dir(&libcoap_src_dir).expect("unable to change to libcoap build dir"); + Command::new(libcoap_src_dir.join("autogen.sh")) + .status() + .expect("unable to execute autogen.sh"); + let mut build_config = autotools::Config::new(&libcoap_src_dir); + build_config.out_dir(out_dir); + if let Some(dtls_backend) = dtls_backend { + build_config + .enable("dtls", None) + .with(dtls_backend.to_string().as_str(), None); + + // If DTLS is vendored we need to tell libcoap about the vendored version + match dtls_backend { + DtlsBackend::TinyDtls => { + // We do not ship tinydtls with our source distribution. Instead, we use tinydtls-sys. + build_config.without("submodule-tinydtls", None); + + // If tinydtls-sys is built with the vendored feature, the library is built alongside + // the Rust crate. To use the version built by the tinydtls-sys build script, we use the + // environment variables set by the build script. + if let Some(tinydtls_include) = env::var_os("DEP_TINYDTLS_INCLUDE") { + build_config.env( + "TinyDTLS_CFLAGS", + format!( + "-I{} -I{}", + tinydtls_include + .to_str() + .expect("DEP_TINYDTLS_INCLUDE is not a valid string"), + Path::new(&tinydtls_include) + .join("tinydtls") + .to_str() + .expect("DEP_TINYDTLS_INCLUDE is not a valid string") + ), + ); + }; + + if let Some(tinydtls_libs) = env::var_os("DEP_TINYDTLS_LIBS") { + build_config.env( + "TinyDTLS_LIBS", + format!( + "-L{}", + tinydtls_libs.to_str().expect("DEP_TINYDTLS_LIBS is invalid string") + ), + ); + + build_config.env( + "PKG_CONFIG_PATH", + Path::new(tinydtls_libs.as_os_str()) + .join("lib") + .join("pkgconfig") + .into_os_string(), + ); + } + }, + DtlsBackend::OpenSsl => { + // Set include path according to the path provided by openssl-sys (relevant if + // openssl-sys is vendored) + if let Some(openssl_include) = env::var_os("DEP_OPENSSL_INCLUDE") { + build_config.env( + "OpenSSL_CFLAGS", + format!( + "-I{}", + openssl_include.to_str().expect("DEP_OPENSSL_INCLUDE is invalid path") + ), + ); + build_config.env( + "PKG_CONFIG_PATH", + Path::new(openssl_include.as_os_str()) + .parent() + .expect("DEP_OPENSSL_INCLUDE has no parent directory") + .join("lib") + .join("pkgconfig") + .into_os_string(), + ); + } + }, + DtlsBackend::MbedTls => { + // Set include path according to the path provided by mbedtls-sys (relevant if + // mbedtls-sys is vendored). + // libcoap doesn't support overriding the MbedTLS CFLAGS, but doesn't set those + // either, so we just set CFLAGS and hope they propagate. + if let Some(mbedtls_include) = env::var_os("DEP_MBEDTLS_INCLUDE") { + // the config.h of mbedtls-sys-auto is generated separately from all other + // includes in the root of mbedtls-sys-auto's OUT_DIR. + // In order to let libcoap read use the correct config file, we need to copy + // this file into our own OUT_DIR under include/mbedtls/config.h, so that we + // can then set OUT_DIR/include as an additional include path. + let config_h = env::var_os("DEP_MBEDTLS_CONFIG_H") + .expect("DEP_MBEDTLS_INCLUDE is set but DEP_MBEDTLS_CONFIG_H is not"); + let config_path = Path::new(&config_h); + let out_include = Path::new(&out_dir).join("include"); + std::fs::create_dir_all(out_include.join("mbedtls")) + .expect("unable to prepare include directory for mbedtls config.h"); + std::fs::copy(config_path, out_include.join("mbedtls").join("config.h")) + .expect("unable to copy mbedtls config.h to include directory"); + let mbedtls_library_path = config_path + .parent() + .expect("DEP_MBEDTLS_CONFIG_H has no parent directory") + .join("build") + .join("library"); + build_config.env( + "MbedTLS_CFLAGS", + format!( + "-I{} -I{}", + out_include.to_str().expect("OUT_DIR is not a valid string"), + mbedtls_include + .to_str() + .expect("DEP_MBEDTLS_INCLUDE is not a valid string") + ), + ); + build_config.env( + "MbedTLS_LIBS", + format!( + "-L{0} -l:libmbedtls.a -l:libmbedcrypto.a -l:libmbedx509.a", + mbedtls_library_path + .to_str() + .expect("DEP_MBEDTLS_CONFIG_H is not a valid string"), + ), + ); + } + }, + DtlsBackend::GnuTls => { + // Vendoring not supported + }, + } + } else { + build_config.disable("dtls", None); + } + build_config + // Disable shared library compilation because the vendored library will always be + // statically linked + .disable("shared", None) + // Disable any documentation for vendored C library + .disable("documentation", None) + .disable("doxygen", None) + .disable("manpages", None) + // This would install the license into the documentation directory, but we don't use the + // generated documentation anywhere. + .disable("license-install", None) + // Disable tests and examples as well as test coverage + .disable("tests", None) + .disable("examples", None) + .disable("gcov", None); + + // Enable debug symbols if enabled in Rust + match env::var_os("DEBUG") + .expect("env variable DEBUG that should have been set by cargo is not set") + .to_str() + .expect("env variable DEBUG is not valid") + { + "0" | "false" => {}, + _ => { + build_config.with("debug", None); + }, + } + // Enable dependency features based on selected cargo features. + build_config + .enable("oscore", Some(if cfg!(feature = "oscore") { "yes" } else { "no" })) + .enable("ipv4-support", Some(if cfg!(feature = "ipv4") { "yes" } else { "no" })) + .enable("ipv6-support", Some(if cfg!(feature = "ipv6") { "yes" } else { "no" })) + .enable( + "af-unix-support", + Some(if cfg!(feature = "af-unix") { "yes" } else { "no" }), + ) + .enable("tcp", Some(if cfg!(feature = "tcp") { "yes" } else { "no" })) + .enable( + "websockets", + Some(if cfg!(feature = "websockets") { "yes" } else { "no" }), + ) + .enable("async", Some(if cfg!(feature = "async") { "yes" } else { "no" })) + .enable( + "observe-persist", + Some(if cfg!(feature = "observe-persist") { "yes" } else { "no" }), + ) + .enable("q-block", Some(if cfg!(feature = "q-block") { "yes" } else { "no" })) + .enable( + "thread-safe", + Some(if cfg!(feature = "thread-safe") { "yes" } else { "no" }), + ) + .enable( + "thread-recursive-lock-detection", + Some(if cfg!(feature = "thread-recursive-lock-detection") { + "yes" + } else { + "no" + }), + ) + .enable( + "small-stack", + Some(if cfg!(feature = "small-stack") { "yes" } else { "no" }), + ) + .enable("server-mode", Some(if cfg!(feature = "server") { "yes" } else { "no" })) + .enable("client-mode", Some(if cfg!(feature = "client") { "yes" } else { "no" })) + .with("epoll", Some(if cfg!(feature = "epoll") { "yes" } else { "no" })); + + // Run build + let dst = build_config.build(); + + // Add the built library to the search path + println!( + "cargo:rustc-link-search=native={}", + dst.join("lib") + .to_str() + .expect("libcoap build output dir is not a valid string") + ); + println!( + "cargo:include={}", + dst.join("include") + .to_str() + .expect("libcoap build output dir is not a valid string") + ); + builder = builder + .clang_arg(format!( + "-I{}", + dst.join("include") + .to_str() + .expect("libcoap build output dir is not a valid string") + )) + .clang_arg(format!( + "-L{}", + dst.join("lib") + .to_str() + .expect("libcoap build output dir is not a valid string") + )); + env::set_current_dir(current_dir_backup).expect("unable to switch back to source dir"); + builder +} + +fn main() { + println!("cargo::rustc-check-cfg=cfg(feature_checks_available)"); + println!("cargo::rustc-check-cfg=cfg(non_inlined_coap_send_rst)"); + println!("cargo:rerun-if-changed=src/libcoap/"); + println!("cargo:rerun-if-changed=src/wrapper.h"); + // Read required environment variables. + let out_dir = env::var_os("OUT_DIR").expect("unsupported OUT_DIR"); + let target_os = env::var("CARGO_CFG_TARGET_OS").expect("invalid TARGET_OS environment variable"); + + let mut bindgen_builder = match target_os.as_str() { + "espidf" => get_builder_espidf(), + _ => get_builder(), + }; + + let mut dtls_backend = Option::None; + if cfg!(feature = "dtls") { + // We can only select one TLS backend at a time for libcoap, but cargo does not support mutually + // exclusive features, and it would be really bad if a project that uses multiple dependencies + // which depend on different TLS backends would not compile. + // Therefore, if multiple TLS backend features are enabled, we choose one based on the following + // priority order: gnutls > openssl > mbedtls > tinydtls, matching the order specified in + // https://github.com/obgm/libcoap/blob/develop/configure.ac#L494 + let mut multiple_backends = false; + if cfg!(feature = "dtls_backend_tinydtls") { + dtls_backend = Some(DtlsBackend::TinyDtls); + } + if cfg!(feature = "dtls_backend_mbedtls") { + if dtls_backend.is_some() { + multiple_backends = true; + } + println!("cargo:rerun-if-env-changed=MBEDTLS_LIBRARY_PATH"); + dtls_backend = Some(DtlsBackend::MbedTls); + } + if cfg!(feature = "dtls_backend_openssl") { + if dtls_backend.is_some() { + multiple_backends = true; + } + dtls_backend = Some(DtlsBackend::OpenSsl); + } + if cfg!(feature = "dtls_backend_gnutls") { + if dtls_backend.is_some() { + multiple_backends = true; + } + dtls_backend = Some(DtlsBackend::GnuTls); + } + if multiple_backends { + // more than one backend was set, so unwrapping is ok here. + println!("cargo:warning=Multiple DTLS backends enabled for libcoap-sys. Only one can be enabled, choosing {:?} as the backend to use. This may cause problems.", dtls_backend.as_ref().unwrap()); + } + if dtls_backend.is_none() { + println!("cargo:warning=No DTLS backend selected for libcoap-sys, aborting build."); + panic!("No DTLS backend selected for libcoap-sys, aborting build") + } + } + + // Build vendored library if feature was set. + if cfg!(feature = "vendored") && target_os.as_str() != "espidf" { + bindgen_builder = build_vendored_library(&out_dir, dtls_backend.as_ref(), bindgen_builder); + }; + + if target_os.as_str() != "espidf" { + // Tell cargo to link libcoap. + println!( + "cargo:rustc-link-lib={}{}", + cfg!(feature = "static").then(|| "static=").unwrap_or("dylib="), + format!( + "coap-3-{}", + &dtls_backend + .as_ref() + .map(|v| v.to_string()) + .unwrap_or_else(|| "notls".to_string()) + ) + .as_str() + ); + + // For the DTLS libraries, we need to tell cargo which external libraries to link. + // Note that these linker instructions have to be added *after* the linker instruction + // for libcoap itself, as some linkers require dependencies to be in reverse order. + if let Some(dtls_backend) = dtls_backend { + match dtls_backend { + DtlsBackend::TinyDtls => { + // Handled by tinydtls-sys + }, + DtlsBackend::OpenSsl => { + // Handled by openssl-sys + }, + DtlsBackend::MbedTls => { + // If mbedtls is vendored, mbedtls-sys-auto already takes care of linking. + if env::var_os("DEP_MBEDTLS_INCLUDE").is_none() { + // We aren't using mbedtls-sys-auto if we aren't vendoring (as it doesn't support + // mbedtls >= 3.0.0), so we need to tell cargo to link to mbedtls ourselves. + + // Try to find mbedtls using pkg-config, will emit cargo link statements if successful + if pkg_config::Config::new() + .statik(cfg!(feature = "static")) + .probe("mbedtls") + .is_err() + { + // couldn't find using pkg-config, just try linking with given library + // search path. + println!("cargo:rustc-link-lib=mbedtls",); + println!("cargo:rustc-link-lib=mbedx509",); + println!("cargo:rustc-link-lib=mbedcrypto",); + } + } + }, + DtlsBackend::GnuTls => { + // gnutls-sys is unmaintained, so we need to link to gnutls ourselves. + + // try pkg-config + if probe_library("gnutls").is_err() { + // if that doesn't work, try using the standard library search path. + println!("cargo:rustc-link-lib=gnutls") + } + }, + } + } + } + + let libcoap_defines = Rc::new(RefCell::new(LibcoapDefineMetadata::default())); + + let cfg_info = Box::new(CoapDefineParser { + defines: Rc::clone(&libcoap_defines), + }); + + bindgen_builder = bindgen_builder + .header("src/wrapper.h") + .default_enum_style(EnumVariation::Rust { non_exhaustive: true }) + // Causes invalid syntax for some reason, so we have to disable it. + .generate_comments(false) + .dynamic_link_require_all(true) + .allowlist_function("(oscore|coap)_.*") + .allowlist_type("(oscore|coap)_.*") + .allowlist_var("(oscore|coap)_.*") + .allowlist_function("(OSCORE|COAP)_.*") + .allowlist_type("(OSCORE|COAP)_.*") + .allowlist_var("(OSCORE|COAP|LIBCOAP)_.*") + // We use the definitions made by the libc crate instead + .blocklist_type("sockaddr(_in|_in6)?") + .blocklist_type("in6?_(addr|port)(_t)?") + .blocklist_type("in6_addr__bindgen_ty_1") + .blocklist_type("(__)?socklen_t") + .blocklist_type("fd_set") + .blocklist_type("sa_family_t") + .blocklist_type("(__)?time_t") + .blocklist_type("__fd_mask") + // Are generated because they are typedef-ed inside of the C headers, blocklisting them + // will instead replace them with the appropriate rust types. + // See https://github.com/rust-lang/rust-bindgen/issues/1215 for an open issue concerning + // this problem. + .blocklist_type("__(u)?int(8|16|32|64|128)_t") + .size_t_is_usize(true) + .parse_callbacks(cfg_info); + if !cfg!(feature = "vendored") { + // Triggers a rebuild on every cargo build invocation if used for the vendored version, as + // the included headers seem to come from our built version. + // Should be fine though, as we already printed `cargo:rerun-if-changed=src/libcoap/` at the + // start of the file. + bindgen_builder = bindgen_builder.parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); + } + let bindings = bindgen_builder.generate().expect("unable to generate bindings"); + + // Check if required features are available in libcoap. + let libcoap_defines = libcoap_defines.take(); + if libcoap_defines.feature_defines_available { + println!("cargo:rustc-cfg=feature_checks_available"); + for feature in COMPILE_TIME_FEATURE_CHECKS { + let feature_env_var_name = "CARGO_FEATURE_".to_string() + &feature.replace('-', "_").to_uppercase(); + if env::var(&feature_env_var_name).is_ok() && !libcoap_defines.feature_defines.contains(feature) { + panic!("Required feature {feature} is not available in the used version of libcoap!"); + } + } + if dtls_backend != libcoap_defines.dtls_backend { + // Should be fine, as applications should expect that the DTLS library could differ. + println!("cargo:warning=DTLS library used by libcoap does not match chosen one. This might lead to issues.") + } + } else { + println!("cargo:warning=The used version of libcoap does not provide a coap_defines.h file, either because it is too old (<4.3.5) or because this file is somehow not included. Compile-time feature checks are not available, and the availability of some features (small-stack, IPv4/IPv6,) cannot be asserted at all!"); + } + + let out_path = PathBuf::from(out_dir); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("unable to write generated bindings to file"); +} diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 1444fdf8..83aac3dc 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -88,11 +88,11 @@ use std::ffi::c_void; -#[allow(unused_imports)] -use libc::{fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t}; #[allow(unused_imports)] #[cfg(not(target_os = "espidf"))] use libc::epoll_event; +#[allow(unused_imports)] +use libc::{fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t}; // use dtls backend libraries in cases where they set our linker flags, otherwise cargo will // optimize them out. #[allow(unused_imports)] @@ -105,16 +105,7 @@ use openssl_sys as _; #[cfg(feature = "dtls_backend_tinydtls")] use tinydtls_sys as _; -#[cfg(target_family = "windows")] -include!(concat!(env!("OUT_DIR"), "\\bindings.rs")); -#[cfg(not(target_family = "windows"))] -include!(concat!(env!("OUT_DIR"), "/bindings.rs")); - -#[inline] -#[cfg(not(non_inlined_coap_send_rst))] -pub unsafe fn coap_send_rst(session: *mut coap_session_t, request: *const coap_pdu_t) -> coap_mid_t { - coap_send_message_type(session, request, crate::coap_pdu_type_t::COAP_MESSAGE_RST) -} +include!(env!("BINDINGS_FILE")); /// Compares instances of coap_str_const_t and/or coap_string_t. /// @@ -300,8 +291,9 @@ mod tests { sync::{Arc, Barrier}, }; - use libc::{AF_INET, AF_INET6, in6_addr, in_addr, sa_family_t, size_t}; + use libc::{in6_addr, in_addr, sa_family_t, size_t, AF_INET, AF_INET6}; + use super::*; use crate::{ coap_pdu_code_t::{COAP_REQUEST_CODE_GET, COAP_RESPONSE_CODE_CONTENT}, coap_proto_t::COAP_PROTO_UDP, @@ -309,8 +301,6 @@ mod tests { coap_response_t::COAP_RESPONSE_OK, }; - use super::*; - const COAP_TEST_RESOURCE_URI: &str = "test"; const COAP_TEST_RESOURCE_RESPONSE: &str = "Hello World!"; From d640bdf3824b5b93b1f96b2f55b02e9cd5d34912 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Thu, 16 Jan 2025 23:08:48 +0100 Subject: [PATCH 03/26] fix(sys): Fix include paths for pkg-config build system, update bindgen --- libcoap-sys/Cargo.toml | 2 +- libcoap-sys/build/bindings.rs | 11 +++++++---- libcoap-sys/build/build_system/pkgconfig.rs | 13 ++++++++++++- libcoap-sys/build/main.rs | 3 ++- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 955d2b6b..5cc13ff5 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -145,7 +145,7 @@ tinydtls-sys = { version = "^0.2.0", default-features = false, optional = true } esp-idf-sys = { version = "0.35.0" } [build-dependencies] -bindgen = "0.69.4" +bindgen = { version = "0.71.1", features = ["experimental"] } autotools = "^0.2.3" fs_extra = "^1.2" pkg-config = "^0.3.24" diff --git a/libcoap-sys/build/bindings.rs b/libcoap-sys/build/bindings.rs index be30d1a4..78bf20a7 100644 --- a/libcoap-sys/build/bindings.rs +++ b/libcoap-sys/build/bindings.rs @@ -1,11 +1,11 @@ use std::{cell::RefCell, fmt::Debug, rc::Rc}; - +use std::path::PathBuf; use anyhow::{Context, Result}; use bindgen::{ callbacks::{IntKind, ParseCallbacks}, EnumVariation, }; - +use embuild::bindgen::BindgenExt; use crate::metadata::{LibcoapDefineInfo, LibcoapFeature}; /// Implementation of bindgen's [ParseCallbacks] that allow reading some meta-information about the @@ -65,11 +65,14 @@ impl ParseCallbacks for LibcoapDefineParser { pub fn generate_libcoap_bindings( bindgen_builder_configurator: impl FnOnce(bindgen::Builder) -> Result, ) -> Result { + let source_root = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not set (are we not running as a cargo build script?)")); let mut builder = bindgen::Builder::default() - .header("src/wrapper.h") + .emit_diagnostics() + .header(source_root.join("src").join("wrapper.h").to_str().context("unable to convert header path to &str")?.to_string()) .default_enum_style(EnumVariation::Rust { non_exhaustive: true }) // Causes invalid syntax for some reason, so we have to disable it. - .generate_comments(false) + .generate_comments(true) + .generate_cstr(true) .dynamic_link_require_all(true) .allowlist_function("(oscore|coap)_.*") .allowlist_type("(oscore|coap)_.*") diff --git a/libcoap-sys/build/build_system/pkgconfig.rs b/libcoap-sys/build/build_system/pkgconfig.rs index 6401d9ba..398363a3 100644 --- a/libcoap-sys/build/build_system/pkgconfig.rs +++ b/libcoap-sys/build/build_system/pkgconfig.rs @@ -57,7 +57,18 @@ impl BuildSystem for PkgConfigBuildSystem { fn generate_bindings(&mut self) -> anyhow::Result { let (define_info, define_parser) = LibcoapDefineParser::new(); - let bindings = generate_libcoap_bindings(|builder| Ok(builder.parse_callbacks(Box::new(define_parser))))?; + let bindings = generate_libcoap_bindings(|builder| { + Ok(builder + .parse_callbacks(Box::new(define_parser)) + // If the pkg-config provided include path coincides with a system include directory, + // setting the "-I{}" command line argument will not do anything, potentially resulting + // in clang using different CoAP headers than provided by pkg-config, e.g., if there + // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. + // Therefore, we use `-isystem` instead. + // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir + .clang_args(self.library.include_paths.iter().map(|v| format!("-isystem{}", v.display()))) + ) + })?; self.define_info = Some(RefCell::take(&define_info)); diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 0322d922..897f33d0 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -79,7 +79,8 @@ fn main() -> Result<()> { match build_system.detected_features() { Some(detected_features) => { - if !bypass_compile_time_feature_checks && !requested_features.is_subset(detected_features) { + let compile_time_checkable_features: EnumSet = requested_features.iter().filter(|feat| feat.define_name().is_some()).collect(); + if !bypass_compile_time_feature_checks && !compile_time_checkable_features.is_subset(detected_features) { let missing_features = requested_features.difference(detected_features); bail!( concat!( From 93b1c6998aa049fbb1288662eacb1041437aa928 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Fri, 17 Jan 2025 03:48:38 +0100 Subject: [PATCH 04/26] refactor(sys): re-implement vendored library build --- libcoap-sys/Cargo.toml | 29 +- libcoap-sys/build/build_system/vendored.rs | 368 ++++++++++++++++++++- libcoap-sys/build/main.rs | 6 +- libcoap-sys/build/metadata.rs | 6 +- libcoap-sys/src/lib.rs | 6 +- libcoap/Cargo.toml | 9 +- libcoap/src/crypto/psk/client.rs | 2 - libcoap/src/crypto/psk/server.rs | 1 - 8 files changed, 395 insertions(+), 32 deletions(-) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 5cc13ff5..369da2be 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -39,17 +39,20 @@ default = ["oscore", "ipv4", "ipv6", "af-unix", "tcp", "websockets", "async", "o # https://github.com/obgm/libcoap/blob/develop/configure.ac#L494 (gnutls > openssl > mbedtls > tinydtls). -# Corresponding libcoap configure flag: --with-openssl -dtls_backend_openssl = ["dtls", "dep:openssl-sys"] -dtls_backend_openssl_vendored = ["dtls_backend_openssl", "openssl-sys/vendored"] -# Corresponding libcoap configure flag: --with-gnutls -dtls_backend_gnutls = ["dtls"] -# Corresponding libcoap configure flag: --with-mbedtls -dtls_backend_mbedtls = ["dtls"] # can't use mbedtls-sys-auto to generate linker flags here, as the crate doesn't support mbedtls >= 3.0.0 -dtls_backend_mbedtls_vendored = ["dep:mbedtls-sys-auto", "dtls_backend_mbedtls"] -# Corresponding libcoap configure flags: --with-tinydtls --without-submodule-tinydtls -dtls_backend_tinydtls = ["dtls", "dep:tinydtls-sys", "tinydtls-sys/ecc", "tinydtls-sys/psk"] -dtls_backend_tinydtls_vendored = ["dtls_backend_tinydtls", "tinydtls-sys/vendored"] +# Allows using the version of OpenSSL provided by openssl-sys instead of a system-provided one. +# Note that this does not enforce the use of OpenSSL in libcoap, see the crate-level documentation for more info. +dtls-openssl-sys = ["dep:openssl-sys"] +# Tell the openssl-sys version that is possibly used by libcoap-sys to use the vendored version of its library. +dtls-openssl-sys-vendored = ["dtls-openssl-sys", "openssl-sys/vendored"] +# Allows using the version of MbedTLS provided by mbedtls-sys-auto instead of a system-provided one. +# Note that this does not enforce the use of MbedTLS in libcoap, see the crate-level documentation for more info. +dtls-mbedtls-sys = ["dep:mbedtls-sys-auto"] +# Allows using the version of TinyDTLS provided by tinydtls-sys instead of a system-provided one. +# Note that this does not enforce the use of TinyDTLS in libcoap, see the crate-level documentation for more info. +dtls-tinydtls-sys = ["dep:tinydtls-sys", "tinydtls-sys/ecc", "tinydtls-sys/psk"] +# Tell the tinydtls-sys version that is possibly used by libcoap-sys to use the vendored version of its library. +dtls-tinydtls-sys-vendored = ["dtls-tinydtls-sys", "tinydtls-sys/vendored"] + # Enabling this feature will force libcoap-sys to be built with and statically linked to a vendored version of libcoap, # which will be built by the build-script before building libcoap-sys. # This way, it is no longer required to have libcoap installed to use this crate. @@ -142,7 +145,7 @@ libc = "^0.2.126" tinydtls-sys = { version = "^0.2.0", default-features = false, optional = true } [target.'cfg(target_os="espidf")'.dependencies] -esp-idf-sys = { version = "0.35.0" } +esp-idf-sys = { version = ">0.35.0" } [build-dependencies] bindgen = { version = "0.71.1", features = ["experimental"] } @@ -150,7 +153,7 @@ autotools = "^0.2.3" fs_extra = "^1.2" pkg-config = "^0.3.24" regex = "1.10.5" -embuild = { version = "0.32.0", features = ["bindgen", "espidf", "cmake"] } +embuild = { version = ">0.32.0", features = ["bindgen", "espidf", "cmake"] } version-compare = "0.2.0" anyhow = "1.0.94" enumset = "1.1.5" diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index 6e2f1b8f..09516aab 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -1 +1,367 @@ -pub struct VendoredBuildSystem {} +use std::cell::RefCell; +use std::env; +use std::env::VarError; +use std::ffi::OsString; +use std::io::ErrorKind; +use std::path::{Path, PathBuf}; +use std::process::Command; +use anyhow::{anyhow, ensure, Context, Result}; +use enumset::EnumSet; +use version_compare::Version; +use crate::bindings::{generate_libcoap_bindings, LibcoapDefineParser}; +use crate::build_system::BuildSystem; +use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature, MINIMUM_LIBCOAP_VERSION}; + +const VENDORED_LIBCOAP_VERSION: &str = "4.3.5"; + +pub struct VendoredBuildSystem { + out_dir: PathBuf, + define_info: Option, + include_paths: Vec, +} + +impl VendoredBuildSystem { + /// Obtain some built version of libcoap and set the appropriate linker flags to link with it + /// (and its dependencies, if any). + pub fn build_libcoap(out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option) -> Result { + println!("cargo:rerun-if-changed=src/libcoap"); + + let libcoap_src_dir = out_dir.join("libcoap"); + let libcoap_build_prefix = out_dir.join("build"); + + // Even though libcoap supports out-of-source builds, autogen.sh (or the corresponding + // autotools) modify files in the source tree, which causes verification problems when + // running cargo package. + // Therefore, we copy the libcoap source over to the output directory and build from there. + let copy_options = fs_extra::dir::CopyOptions { + overwrite: true, + ..Default::default() + }; + std::fs::create_dir_all(&libcoap_src_dir)?; + std::fs::create_dir_all(&libcoap_build_prefix)?; + std::fs::remove_dir_all(&libcoap_src_dir).context("unable to clear libcoap build directory")?; + std::fs::remove_dir_all(&libcoap_build_prefix).context("unable to clear libcoap build directory")?; + std::fs::create_dir_all(&libcoap_build_prefix)?; + std::fs::create_dir_all(&libcoap_src_dir)?; + fs_extra::dir::copy( + Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("libcoap"), + &out_dir, + ©_options, + ) + .context("unable to prepare libcoap build source directory")?; + + env::set_current_dir(&libcoap_src_dir).expect("unable to change to libcoap build dir"); + ensure!(Command::new(libcoap_src_dir.join("autogen.sh")) + .status() + .context("unable to execute autogen.sh")?.success(), "autogen.sh returned an error code"); + + let mut build_config = autotools::Config::new(&libcoap_src_dir); + + let mut build_config = build_config + // Disable shared library compilation because the vendored library will always be + // statically linked + .out_dir(&libcoap_build_prefix) + .disable("shared", None) + .enable("static", None) + // Disable any documentation for vendored C library + .disable("documentation", None) + .disable("doxygen", None) + .disable("manpages", None) + // This would install the license into the documentation directory, but we don't use the + // generated documentation anywhere. + .disable("license-install", None) + // Disable tests and examples as well as test coverage + .disable("tests", None) + .disable("examples", None) + .disable("gcov", None) + // We do not include the TinyDTLS submodule in our source distribution, make sure that + // libcoap doesn't try to use it. + // This will generate a warning message if TinyDTLS isn't explicitly enabled, but this + // has no negative consequences. + .without("submodule-tinydtls", None); + + for feature in requested_features { + if let Some(feature_flag) = feature.configure_flag_name() { + build_config = build_config.enable(feature_flag, None); + } + } + for feature in EnumSet::::all().difference(requested_features) { + if let Some(feature_flag) = feature.configure_flag_name() { + build_config = build_config.disable(feature_flag, None); + } + } + let pkg_config_path_bak = env::var_os("PKG_CONFIG_PATH"); + + let link_using_pkgconfig = if requested_features.contains(LibcoapFeature::Dtls) { + + // Check if we have any DTLS libraries already added as a Rust dependency. + // For each one, set the appropriate PKG_CONFIG_PATHs, CFLAGS and/or LIBS to use them + // instead of system versions if they are going to be used. + let mut additional_pkg_config_paths: Vec = vec![]; + let mut dtls_libraries_linked_by_other_crates = EnumSet::::empty(); + #[cfg(feature = "dtls-tinydtls-sys")] + { + let (pkg_config_path, linked) = Self::configure_tinydtls_sys(build_config)?; + if let Some(pkg_config_path) = pkg_config_path { + additional_pkg_config_paths.push(pkg_config_path) + } + if linked { + dtls_libraries_linked_by_other_crates |= DtlsBackend::TinyDtls + } + } + #[cfg(feature = "dtls-openssl-sys")] + { + let (pkg_config_path, linked) = Self::configure_openssl_sys(build_config)?; + if let Some(pkg_config_path) = pkg_config_path { + additional_pkg_config_paths.push(pkg_config_path) + } + if linked { + dtls_libraries_linked_by_other_crates |= DtlsBackend::OpenSsl + } + } + #[cfg(feature = "dtls-mbedtls-sys")] + { + let (pkg_config_path, linked) = Self::configure_mbedtls_sys(&out_dir, build_config)?; + if let Some(pkg_config_path) = pkg_config_path { + additional_pkg_config_paths.push(pkg_config_path) + } + if linked { + dtls_libraries_linked_by_other_crates |= DtlsBackend::MbedTls + } + } + + // Add libcoap's own build directory to the PKG_CONFIG_PATH (might be used later on to + // find the generated .pc file to link against libcoap). + additional_pkg_config_paths.push(libcoap_build_prefix.join("lib").join("pkgconfig")); + + let pkg_config_path = match env::var("PKG_CONFIG_PATH") { + Ok(v) => { format!("{}:{}", additional_pkg_config_paths.iter().map(|v| v.to_str().ok_or(anyhow!("unable to convert PKG_CONFIG_PATH value to UTF-8"))).collect::>>()?.join(":"), v) } + Err(VarError::NotPresent) => { additional_pkg_config_paths.iter().map(|v| v.to_str().ok_or(anyhow!("unable to convert PKG_CONFIG_PATH value to UTF-8"))).collect::>>()?.join(":") }, + Err(e) => Err(e).context("PKG_CONFIG_PATH is not a valid UTF-8 string")? + }; + build_config.env("PKG_CONFIG_PATH", &pkg_config_path); + + // SAFETY: We are single-threaded here. + unsafe { + env::set_var("PKG_CONFIG_PATH", pkg_config_path) + } + + // Choose a DTLS backend. + // If we are not using one of the DTLS libraries already linked by another rust crate, + // we need to link the DTLS library as well. Set a boolean variable to keep track of this. + let dtls_library_already_linked = if let Some(requested_dtls_backend) = requested_dtls_backend { + // If one has been explicitly requested by the user, use that one. + build_config = build_config.with(requested_dtls_backend.as_str(), None); + dtls_libraries_linked_by_other_crates.contains(requested_dtls_backend) + } else { + // If we do have a library already linked via a rust dependency, prefer those, but + // maintain the order also used in libcoap itself. + if cfg!(feature = "dtls-openssl-sys") { + build_config.with(DtlsBackend::OpenSsl.as_str(), None); + dtls_libraries_linked_by_other_crates.contains(DtlsBackend::OpenSsl) + } else if cfg!(feature = "dtls-mbedtls-sys") { + build_config.with(DtlsBackend::MbedTls.as_str(), None); + dtls_libraries_linked_by_other_crates.contains(DtlsBackend::MbedTls) + } else if cfg!(feature = "dtls-tinydtls-sys") { + build_config.with(DtlsBackend::TinyDtls.as_str(), None); + dtls_libraries_linked_by_other_crates.contains(DtlsBackend::TinyDtls) + } else { + // Otherwise, we will rely on libcoap to find us a suitable DTLS library. + false + } + }; + + !dtls_library_already_linked + } else { + false + }; + + build_config.build(); + + if link_using_pkgconfig { + // We need to link both libcoap and its DTLS library. Use the generated pkg-config + // file to determine how to do this. + let library = pkg_config::Config::new().statik(true).exactly_version(VENDORED_LIBCOAP_VERSION).probe("libcoap-3").context("unable to link against build version of libcoap using pkg-config (which is necessary if you're not using a Rust dependency to link the DTLS library)")?; + + // SAFETY: We are still single-threaded here. + unsafe { + env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or(OsString::new())) + } + Ok(Self { + out_dir, + define_info: None, + include_paths: library.include_paths, + }) + } else { + // SAFETY: We are still single-threaded here. + unsafe { + env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or(OsString::new())) + } + println!("cargo:rustc-link-search={}", libcoap_build_prefix.join("lib").to_str().context("unable to convert OUT_DIR to a valid UTF-8 string.")?); + println!("cargo:rustc-link-lib=static=coap-3"); + Ok(Self { + out_dir, + define_info: None, + include_paths: vec![libcoap_build_prefix.join("include")], + }) + } + } + + #[cfg(feature = "dtls-tinydtls-sys")] + fn configure_tinydtls_sys(mut build_config: &mut autotools::Config) -> Result<(Option, bool)> { + if env::var_os("TinyDTLS_CFLAGS").is_some() || env::var_os("TinyDTLS_LIBS").is_some() { + // Do not use tinydtls-sys if the user manually set either the corresponding LIBS or + // CFLAGS variable. + // However, do warn the user that this might cause issues. + println!("cargo:warning=You have enabled the tinydtls-sys dependency, but have overridden either the TinyDTLS_CFLAGS or TinyDTLS_LIBS environment variable used by libcoap to find TinyDTLS."); + println!("cargo:warning=Note that attempting to link more than one version of the same library at once may cause unexpected issues and/or cryptic compilation errors, especially if both versions are statically linked."); + Ok((None, false)) + } else { + let tinydtls_include = env::var_os("DEP_TINYDTLS_INCLUDE").expect("tinydtls-sys dependency has been added, but DEP_TINYDTLS_INCLUDE has not been set"); + let tinydtls_libs = env::var_os("DEP_TINYDTLS_LIBS").expect("tinydtls-sys dependency has been added, but DEP_TINYDTLS_LIBS has not been set"); + build_config = build_config.env("TinyDTLS_CFLAGS", + format!( + "-I{} -I{}", + tinydtls_include + .to_str() + .context("DEP_TINYDTLS_INCLUDE path is not a valid UTF-8 string")?, + Path::new(&tinydtls_include) + .join("tinydtls") + .to_str() + .context("DEP_TINYDTLS_INCLUDE path is not a valid UTF-8 string")? + )); + + // Need to set TinyDTLS_LIBS explicitly to force static linking (TinyDTLS also builds a shared version of the library). + build_config = build_config.env( + "TinyDTLS_LIBS", + format!( + "-L{} -l:libtinydtls.a", + tinydtls_libs.to_str().context("DEP_TINYDTLS_LIBS path is not a valid UTF-8 string")? + )); + + // Add TinyDTLS's pkg-config directory to the path for version checking. + Ok(( + Some(PathBuf::from(tinydtls_libs) + // TODO: Isn't this already the lib subdirectory? + .join("lib") + .join("pkgconfig")), true)) + } + } + + #[cfg(feature = "dtls-openssl-sys")] + fn configure_openssl_sys(build_config: &mut autotools::Config) -> Result<(Option, bool)> { + if env::var_os("OpenSSL_CFLAGS").is_some() || env::var_os("OpenSSL_LIBS").is_some() { + // Do not use tinydtls-sys if the user manually set either the corresponding LIBS or + // CFLAGS variable. + // However, do warn the user that this might cause issues. + println!("cargo:warning=You have enabled the openssl-sys dependency, but have overridden either the OpenSSL_CFLAGS or OpenSSL_LIBS environment variable used by libcoap to find OpenSSL."); + println!("cargo:warning=Note that attempting to link more than one version of the same library at once may cause unexpected issues and/or cryptic compilation errors, especially if both versions are statically linked."); + Ok((None, false)) + } else { + let openssl_include = env::var_os("DEP_OPENSSL_INCLUDE").expect("openssl-sys dependency has been added, but DEP_OPENSSL_INCLUDE has not been set"); + let openssl_libs = + Path::new(openssl_include.as_os_str()) + .parent() + .context("DEP_OPENSSL_INCLUDE has no parent directory")? + .join("lib"); + + // Just add the OpenSSL directory to the PKG_CONFIG_PATH, that way libcoap will find it. + Ok((Some(openssl_libs.join("pkgconfig")), true)) + } + } + + #[cfg(feature = "dtls-mbedtls-sys")] + fn configure_mbedtls_sys(out_dir: &Path, mut build_config: &mut autotools::Config) -> Result<(Option, bool)> { + if env::var_os("MbedTLS_CFLAGS").is_some() || env::var_os("MbedTLS_LIBS").is_some() { + // Do not use tinydtls-sys if the user manually set either the corresponding LIBS or + // CFLAGS variable. + // However, do warn the user that this might cause issues. + println!("cargo:warning=You have enabled the mbedtls-sys dependency, but have overridden either the MbedTLS_CFLAGS or MbedTLS_LIBS environment variable used by libcoap to find MbedTLS."); + println!("cargo:warning=Note that attempting to link more than one version of the same library at once may cause unexpected issues and/or cryptic compilation errors, especially if both versions are statically linked."); + Ok((None, false)) + } else { + let mbedtls_include = env::var_os("DEP_MBEDTLS_INCLUDE").expect("mbedtls-sys dependency has been added, but DEP_MBEDTLS_INCLUDE has not been set"); + + // Can't use pkg-config here, as pkg-config was only added to MbedTLS recently. + + // the config.h of mbedtls-sys-auto is generated separately from all other + // includes in the root of mbedtls-sys-auto's OUT_DIR. + // In order to let libcoap read use the correct config file, we need to copy + // this file into our own OUT_DIR under include/mbedtls/config.h, so that we + // can then set OUT_DIR/include as an additional include path. + let config_h = env::var_os("DEP_MBEDTLS_CONFIG_H") + .expect("DEP_MBEDTLS_INCLUDE is set but DEP_MBEDTLS_CONFIG_H is not"); + + let config_path = Path::new(&config_h); + let out_include = Path::new(&out_dir).join("include"); + std::fs::create_dir_all(out_include.join("mbedtls")) + .context("unable to prepare include directory for mbedtls config.h")?; + std::fs::copy(config_path, out_include.join("mbedtls").join("config.h")) + .context("unable to copy mbedtls config.h to include directory")?; + let mbedtls_library_path = config_path + .parent() + .context("DEP_MBEDTLS_CONFIG_H has no parent directory")? + .join("build") + .join("library"); + + build_config = build_config.env( + "MbedTLS_CFLAGS", + format!( + "-I{} -I{}", + out_include.to_str().expect("OUT_DIR is not a valid UTF-8 string"), + mbedtls_include + .to_str() + .expect("DEP_MBEDTLS_INCLUDE is not a valid UTF-8 string") + ), + ); + build_config = build_config.env( + "MbedTLS_LIBS", + format!( + "-L{0} -l:libmbedtls.a -l:libmbedcrypto.a -l:libmbedx509.a", + mbedtls_library_path + .to_str() + .expect("DEP_MBEDTLS_CONFIG_H is not a valid string"), + ), + ); + + // If MbedTLS_CFLAGS and MbedTLS_LIBS are both set, libcoap will fall back to + // determining the library version using other methods. No need to add to pkg-config + // path here (as of now). + Ok((None, true)) + } + } +} + +impl BuildSystem for VendoredBuildSystem { + fn detected_features(&self) -> Option> { + self.define_info.as_ref().map(|v| v.supported_features) + } + + fn version(&self) -> Option { + Version::from(VENDORED_LIBCOAP_VERSION) + } + + fn generate_bindings(&mut self) -> anyhow::Result { + let (define_info, define_parser) = LibcoapDefineParser::new(); + let bindings = generate_libcoap_bindings(|builder| { + Ok(builder + .parse_callbacks(Box::new(define_parser)) + // If the pkg-config provided include path coincides with a system include directory, + // setting the "-I{}" command line argument will not do anything, potentially resulting + // in clang using different CoAP headers than provided by pkg-config, e.g., if there + // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. + // Therefore, we use `-isystem` instead. + // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir + .clang_args(self.include_paths.iter().map(|v| format!("-isystem{}", v.display()))) + ) + })?; + + self.define_info = Some(RefCell::take(&define_info)); + + let out_path = self.out_dir.join("bindings.rs"); + bindings + .write_to_file(&out_path) + .context("unable to write bindings to file")?; + Ok(out_path) + } +} diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 897f33d0..7e09c380 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -8,12 +8,16 @@ use crate::{ build_system::{pkgconfig::PkgConfigBuildSystem, BuildSystem}, metadata::{DtlsBackend, LibcoapFeature, MINIMUM_LIBCOAP_VERSION}, }; +use crate::build_system::vendored::VendoredBuildSystem; mod bindings; mod build_system; mod metadata; fn main() -> Result<()> { + println!("cargo:rerun-if-env-changed=LIBCOAP_RS_DTLS_BACKEND"); + println!("cargo:rerun-if-env-changed=LIBCOAP_RS_BUILD_SYSTEM"); + println!("cargo:rerun-if-env-changed=LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS"); let out_dir = PathBuf::from( env::var_os("OUT_DIR").expect("no OUT_DIR was provided (are we not running as a cargo build script?)"), ); @@ -122,7 +126,7 @@ fn link_libcoap_unix( ) -> Result> { // For unix-like systems: Use pkg-config. if cfg!(feature = "vendored") { - todo!() + VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) } else { PkgConfigBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend) .map(|v| Box::::from(Box::new(v))) diff --git a/libcoap-sys/build/metadata.rs b/libcoap-sys/build/metadata.rs index eedc140c..cc826147 100644 --- a/libcoap-sys/build/metadata.rs +++ b/libcoap-sys/build/metadata.rs @@ -101,7 +101,7 @@ impl LibcoapFeature { match self { LibcoapFeature::AfUnix => Some("af-unix"), LibcoapFeature::Async => Some("async"), - LibcoapFeature::Client => Some("client"), + LibcoapFeature::Client => Some("client-mode"), LibcoapFeature::SmallStack => Some("small-stack"), LibcoapFeature::Tcp => Some("tcp"), LibcoapFeature::Epoll => Some("epoll"), @@ -109,7 +109,7 @@ impl LibcoapFeature { LibcoapFeature::Ipv6 => Some("ipv6"), LibcoapFeature::Oscore => Some("oscore"), LibcoapFeature::QBlock => Some("q-block"), - LibcoapFeature::Server => Some("server"), + LibcoapFeature::Server => Some("server-mode"), LibcoapFeature::ThreadRecursiveLockDetection => Some("thread-recursive-lock-detection"), LibcoapFeature::ThreadSafe => Some("thread-safe"), LibcoapFeature::Dtls => Some("dtls"), @@ -152,7 +152,7 @@ impl LibcoapFeature { } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, EnumSetType)] pub enum DtlsBackend { GnuTls, OpenSsl, diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 83aac3dc..9ac4ea58 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -96,13 +96,13 @@ use libc::{fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, soc // use dtls backend libraries in cases where they set our linker flags, otherwise cargo will // optimize them out. #[allow(unused_imports)] -#[cfg(feature = "dtls_backend_mbedtls_vendored")] +#[cfg(feature = "dtls-mbedtls-sys")] use mbedtls_sys as _; #[allow(unused_imports)] -#[cfg(feature = "dtls_backend_openssl")] +#[cfg(feature = "dtls-openssl-sys")] use openssl_sys as _; #[allow(unused_imports)] -#[cfg(feature = "dtls_backend_tinydtls")] +#[cfg(feature = "dtls-tinydtls-sys")] use tinydtls_sys as _; include!(env!("BINDINGS_FILE")); diff --git a/libcoap/Cargo.toml b/libcoap/Cargo.toml index 0949d459..ef3ee875 100644 --- a/libcoap/Cargo.toml +++ b/libcoap/Cargo.toml @@ -20,14 +20,7 @@ resolver = "2" rust-version = "1.81.0" [features] -default = ["dtls-psk", "tcp", "dtls_openssl", "vendored", "libcoap-sys/default"] -dtls_tinydtls = ["libcoap-sys/dtls_backend_tinydtls"] -dtls_tinydtls_vendored = ["dtls_tinydtls", "libcoap-sys/dtls_backend_tinydtls_vendored"] -dtls_openssl = ["libcoap-sys/dtls_backend_openssl"] -dtls_openssl_vendored = ["dtls_openssl", "libcoap-sys/dtls_backend_openssl_vendored"] -dtls_gnutls = ["libcoap-sys/dtls_backend_gnutls"] -dtls_mbedtls = ["libcoap-sys/dtls_backend_mbedtls"] -dtls_mbedtls_vendored = ["dtls_mbedtls", "libcoap-sys/dtls_backend_mbedtls_vendored"] +default = ["dtls-psk", "tcp", "vendored", "libcoap-sys/default"] dtls-psk = ["libcoap-sys/dtls", "libcoap-sys/dtls-psk"] dtls-pki = ["libcoap-sys/dtls", "libcoap-sys/dtls-pki"] dtls-rpk = ["libcoap-sys/dtls", "libcoap-sys/dtls-rpk"] diff --git a/libcoap/src/crypto/psk/client.rs b/libcoap/src/crypto/psk/client.rs index e8b09d1a..4bfa9a19 100644 --- a/libcoap/src/crypto/psk/client.rs +++ b/libcoap/src/crypto/psk/client.rs @@ -41,9 +41,7 @@ impl<'a> ClientPskContextBuilder<'a> { raw_cfg: Box::new(coap_dtls_cpsk_t { version: COAP_DTLS_CPSK_SETUP_VERSION as u8, reserved: Default::default(), - #[cfg(dtls_ec_jpake_support)] ec_jpake: 0, - #[cfg(dtls_cid_support)] use_cid: 0, validate_ih_call_back: None, ih_call_back_arg: std::ptr::null_mut(), diff --git a/libcoap/src/crypto/psk/server.rs b/libcoap/src/crypto/psk/server.rs index eeb61bb7..06656a56 100644 --- a/libcoap/src/crypto/psk/server.rs +++ b/libcoap/src/crypto/psk/server.rs @@ -46,7 +46,6 @@ impl<'a> ServerPskContextBuilder<'a> { raw_cfg: Box::new(coap_dtls_spsk_t { version: COAP_DTLS_SPSK_SETUP_VERSION as u8, reserved: Default::default(), - #[cfg(dtls_ec_jpake_support)] ec_jpake: 0, validate_id_call_back: None, id_call_back_arg: std::ptr::null_mut(), From 6767a7ece626c7571c06bcc41cb04ca4baf78643 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Fri, 17 Jan 2025 04:03:07 +0100 Subject: [PATCH 05/26] fix(sys): only link the DTLS library crate that is _actually_ used. Previously, all DTLS libraries enabled via the dtls-[LIBRARY]-sys feature (formerly known as the dtls_backend_[LIBRARY] features) were referenced in lib.rs, which caused all of them to be linked into the resulting binary, even though only one of them was actually used. This change ensures that only the actually used one is referenced in lib.rs --- libcoap-sys/build/bindings.rs | 1 - libcoap-sys/build/build_system/vendored.rs | 40 ++++++++++++---------- libcoap-sys/build/main.rs | 1 + libcoap-sys/src/lib.rs | 10 +++--- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/libcoap-sys/build/bindings.rs b/libcoap-sys/build/bindings.rs index 78bf20a7..bf43078a 100644 --- a/libcoap-sys/build/bindings.rs +++ b/libcoap-sys/build/bindings.rs @@ -67,7 +67,6 @@ pub fn generate_libcoap_bindings( ) -> Result { let source_root = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not set (are we not running as a cargo build script?)")); let mut builder = bindgen::Builder::default() - .emit_diagnostics() .header(source_root.join("src").join("wrapper.h").to_str().context("unable to convert header path to &str")?.to_string()) .default_enum_style(EnumVariation::Rust { non_exhaustive: true }) // Causes invalid syntax for some reason, so we have to disable it. diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index 09516aab..9082bd97 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -147,28 +147,32 @@ impl VendoredBuildSystem { } // Choose a DTLS backend. - // If we are not using one of the DTLS libraries already linked by another rust crate, - // we need to link the DTLS library as well. Set a boolean variable to keep track of this. - let dtls_library_already_linked = if let Some(requested_dtls_backend) = requested_dtls_backend { + let selected_dtls_backend = if let Some(requested_dtls_backend) = requested_dtls_backend { // If one has been explicitly requested by the user, use that one. - build_config = build_config.with(requested_dtls_backend.as_str(), None); - dtls_libraries_linked_by_other_crates.contains(requested_dtls_backend) - } else { + Some(requested_dtls_backend) + } else if cfg!(feature = "dtls-openssl-sys") { // If we do have a library already linked via a rust dependency, prefer those, but // maintain the order also used in libcoap itself. - if cfg!(feature = "dtls-openssl-sys") { - build_config.with(DtlsBackend::OpenSsl.as_str(), None); - dtls_libraries_linked_by_other_crates.contains(DtlsBackend::OpenSsl) - } else if cfg!(feature = "dtls-mbedtls-sys") { - build_config.with(DtlsBackend::MbedTls.as_str(), None); - dtls_libraries_linked_by_other_crates.contains(DtlsBackend::MbedTls) - } else if cfg!(feature = "dtls-tinydtls-sys") { - build_config.with(DtlsBackend::TinyDtls.as_str(), None); - dtls_libraries_linked_by_other_crates.contains(DtlsBackend::TinyDtls) - } else { - // Otherwise, we will rely on libcoap to find us a suitable DTLS library. - false + Some(DtlsBackend::OpenSsl) + } else if cfg!(feature = "dtls-mbedtls-sys") { + Some(DtlsBackend::MbedTls) + } else if cfg!(feature = "dtls-tinydtls-sys") { + Some(DtlsBackend::TinyDtls) + } else { + // Otherwise, we will rely on libcoap to find us a suitable DTLS library. + None + }; + + // If we are not using one of the DTLS libraries already linked by another rust crate, + // we need to link the DTLS library as well. Set a boolean variable to keep track of this. + let dtls_library_already_linked = if let Some(selected_dtls_backend) = selected_dtls_backend { + build_config = build_config.with(selected_dtls_backend.as_str(), None); + if dtls_libraries_linked_by_other_crates.contains(selected_dtls_backend) { + println!("cargo:rustc-cfg=used_dtls_crate=\"{}\"", selected_dtls_backend.as_str()) } + dtls_libraries_linked_by_other_crates.contains(selected_dtls_backend) + } else { + false }; !dtls_library_already_linked diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 7e09c380..59b693fb 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -18,6 +18,7 @@ fn main() -> Result<()> { println!("cargo:rerun-if-env-changed=LIBCOAP_RS_DTLS_BACKEND"); println!("cargo:rerun-if-env-changed=LIBCOAP_RS_BUILD_SYSTEM"); println!("cargo:rerun-if-env-changed=LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS"); + println!("cargo:rustc-check-cfg=cfg(used_dtls_crate, values(\"mbedtls\", \"tinydtls\", \"openssl\"))"); let out_dir = PathBuf::from( env::var_os("OUT_DIR").expect("no OUT_DIR was provided (are we not running as a cargo build script?)"), ); diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 9ac4ea58..7b0f8367 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -93,16 +93,16 @@ use std::ffi::c_void; use libc::epoll_event; #[allow(unused_imports)] use libc::{fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t}; -// use dtls backend libraries in cases where they set our linker flags, otherwise cargo will -// optimize them out. +// use dtls backend libraries in cases where they set our linker flags, otherwise rustc will +// optimize them out, resulting in missing symbols. #[allow(unused_imports)] -#[cfg(feature = "dtls-mbedtls-sys")] +#[cfg(used_dtls_crate = "mbedtls")] use mbedtls_sys as _; #[allow(unused_imports)] -#[cfg(feature = "dtls-openssl-sys")] +#[cfg(used_dtls_crate = "openssl")] use openssl_sys as _; #[allow(unused_imports)] -#[cfg(feature = "dtls-tinydtls-sys")] +#[cfg(used_dtls_crate = "tinydtls")] use tinydtls_sys as _; include!(env!("BINDINGS_FILE")); From 2a692558d3b117f2a8902411fe241c5fa62136a9 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Fri, 17 Jan 2025 18:49:30 +0100 Subject: [PATCH 06/26] refactor(sys): simplify build system selection, add stubs for ESP-IDF and Manual build systems --- libcoap-sys/Cargo.toml | 10 +- libcoap-sys/build/bindings.rs | 20 +++- libcoap-sys/build/build_system/esp_idf.rs | 31 +++++- libcoap-sys/build/build_system/manual.rs | 29 +++++ libcoap-sys/build/main.rs | 128 +++++++++++++++------- libcoap/Cargo.toml | 5 + 6 files changed, 170 insertions(+), 53 deletions(-) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 369da2be..44127fe5 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -25,7 +25,7 @@ rust-version = "1.81.0" [features] # The default features match those of libcoaps configure script, except for dtls, which is disabled here because it # requires a backend to be set manually. -default = ["oscore", "ipv4", "ipv6", "af-unix", "tcp", "websockets", "async", "observe-persist", "q-block", "thread-safe", "thread-recursive-lock-detection", "server", "client", "epoll", "vendored", "static"] +default = ["oscore", "ipv4", "ipv6", "af-unix", "tcp", "websockets", "async", "observe-persist", "q-block", "thread-safe", "thread-recursive-lock-detection", "server", "client", "epoll", "vendored"] # While not specified here due to limitations in Cargo's syntax, the DTLS feature depends on one of the DTLS backends # being enabled. # If you are developing a library based on libcoap-sys and do not care about the DTLS backend, enable the dtls feature @@ -55,10 +55,9 @@ dtls-tinydtls-sys-vendored = ["dtls-tinydtls-sys", "tinydtls-sys/vendored"] # Enabling this feature will force libcoap-sys to be built with and statically linked to a vendored version of libcoap, # which will be built by the build-script before building libcoap-sys. +# which will be built by the build-script before building libcoap-sys. # This way, it is no longer required to have libcoap installed to use this crate. -vendored = ["static"] -# Enable this feature to use static linking to libcoap instead of dynamic linking. -static = [] +vendored = [] # --- FEATURE FLAGS --- # Note that setting the feature flags currently has no effect on the generated Rust code, because the libcoap headers do # not use these feature flags. They only affect the features built into the vendored C library (if enabled). @@ -153,9 +152,8 @@ autotools = "^0.2.3" fs_extra = "^1.2" pkg-config = "^0.3.24" regex = "1.10.5" -embuild = { version = ">0.32.0", features = ["bindgen", "espidf", "cmake"] } version-compare = "0.2.0" -anyhow = "1.0.94" +anyhow = { version = "1.0.94", features = ["backtrace"] } enumset = "1.1.5" [package.metadata.docs.rs] diff --git a/libcoap-sys/build/bindings.rs b/libcoap-sys/build/bindings.rs index bf43078a..aead1147 100644 --- a/libcoap-sys/build/bindings.rs +++ b/libcoap-sys/build/bindings.rs @@ -1,11 +1,11 @@ -use std::{cell::RefCell, fmt::Debug, rc::Rc}; -use std::path::PathBuf; +use std::{cell::RefCell, fmt::Debug, path::PathBuf, rc::Rc}; + use anyhow::{Context, Result}; use bindgen::{ callbacks::{IntKind, ParseCallbacks}, EnumVariation, }; -use embuild::bindgen::BindgenExt; + use crate::metadata::{LibcoapDefineInfo, LibcoapFeature}; /// Implementation of bindgen's [ParseCallbacks] that allow reading some meta-information about the @@ -65,9 +65,19 @@ impl ParseCallbacks for LibcoapDefineParser { pub fn generate_libcoap_bindings( bindgen_builder_configurator: impl FnOnce(bindgen::Builder) -> Result, ) -> Result { - let source_root = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not set (are we not running as a cargo build script?)")); + let source_root = PathBuf::from( + std::env::var_os("CARGO_MANIFEST_DIR") + .expect("CARGO_MANIFEST_DIR is not set (are we not running as a cargo build script?)"), + ); let mut builder = bindgen::Builder::default() - .header(source_root.join("src").join("wrapper.h").to_str().context("unable to convert header path to &str")?.to_string()) + .header( + source_root + .join("src") + .join("wrapper.h") + .to_str() + .context("unable to convert header path to &str")? + .to_string(), + ) .default_enum_style(EnumVariation::Rust { non_exhaustive: true }) // Causes invalid syntax for some reason, so we have to disable it. .generate_comments(true) diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs index b4d50cf9..3881c439 100644 --- a/libcoap-sys/build/build_system/esp_idf.rs +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -1 +1,30 @@ -#![cfg(target_os = "espidf")] +use crate::build_system::BuildSystem; +use crate::metadata::LibcoapFeature; +use anyhow::Result; +use enumset::EnumSet; +use std::path::PathBuf; +use version_compare::Version; + +pub struct EspIdfBuildSystem { + out_dir: PathBuf, +} + +impl EspIdfBuildSystem { + pub fn new(out_dir: PathBuf) -> Result { + Ok(Self { out_dir }) + } +} + +impl BuildSystem for EspIdfBuildSystem { + fn detected_features(&self) -> Option> { + None + } + + fn version(&self) -> Option { + todo!() + } + + fn generate_bindings(&mut self) -> Result { + todo!() + } +} diff --git a/libcoap-sys/build/build_system/manual.rs b/libcoap-sys/build/build_system/manual.rs index e69de29b..d2396a55 100644 --- a/libcoap-sys/build/build_system/manual.rs +++ b/libcoap-sys/build/build_system/manual.rs @@ -0,0 +1,29 @@ +use std::path::PathBuf; + +use anyhow::{bail, Result}; +use enumset::EnumSet; +use version_compare::Version; + +use crate::{build_system::BuildSystem, metadata::LibcoapFeature}; + +pub struct ManualBuildSystem; + +impl ManualBuildSystem { + pub fn link_with_libcoap(out_dir: PathBuf) -> Result { + bail!("not yet implemented") + } +} + +impl BuildSystem for ManualBuildSystem { + fn detected_features(&self) -> Option> { + todo!() + } + + fn version(&self) -> Option { + todo!() + } + + fn generate_bindings(&mut self) -> anyhow::Result { + todo!() + } +} diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 59b693fb..259b85dd 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -1,14 +1,15 @@ -use std::{env, env::VarError, path::PathBuf}; +use std::{collections::HashMap, env, env::VarError, path::PathBuf}; use anyhow::{anyhow, bail, Context, Result}; use enumset::EnumSet; use version_compare::Version; +use crate::build_system::esp_idf::EspIdfBuildSystem; +use crate::build_system::vendored::VendoredBuildSystem; use crate::{ - build_system::{pkgconfig::PkgConfigBuildSystem, BuildSystem}, + build_system::{manual::ManualBuildSystem, pkgconfig::PkgConfigBuildSystem, BuildSystem}, metadata::{DtlsBackend, LibcoapFeature, MINIMUM_LIBCOAP_VERSION}, }; -use crate::build_system::vendored::VendoredBuildSystem; mod bindings; mod build_system; @@ -23,6 +24,12 @@ fn main() -> Result<()> { env::var_os("OUT_DIR").expect("no OUT_DIR was provided (are we not running as a cargo build script?)"), ); + let target = env::var("TARGET").expect("unable to parse TARGET env variable"); + let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("unable to parse CARGO_CFG_TARGET_ENV env variable"); + let target_os = env::var("CARGO_CFG_TARGET_OS").expect("unable to parse CARGO_CFG_TARGET_OS env variable"); + let target_family = + env::var("CARGO_CFG_TARGET_FAMILY").expect("unable to parse CARGO_CFG_TARGET_FAMILY env variable"); + let requested_features: EnumSet = EnumSet::::all() .iter() .filter(|feat| std::env::var_os(format!("CARGO_FEATURE_{}", feat.cargo_feature_var_name())).is_some()) @@ -49,27 +56,14 @@ fn main() -> Result<()> { }?; let mut build_system: Box = match chosen_build_system.as_ref().map(String::as_str) { - Some("pkgconfig") => PkgConfigBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend) - .context("unable to link libcoap using force-configured build system pkgconfig") - .map(|v| Box::::from(Box::new(v))), - Some(v) => Err(anyhow!("unknown build system {v}")), - None => { - #[cfg(target_os = "espidf")] - { - link_libcoap_espidf() - } - #[cfg(not(target_os = "espidf"))] - { - #[cfg(unix)] - { - link_libcoap_unix(out_dir, requested_features, requested_dtls_backend) - } - #[cfg(windows)] - { - link_libcoap_windows() - } - } - }, + Some(requested_build_system) => link_libcoap_explicit( + requested_build_system, + &target_os, + out_dir, + requested_features, + requested_dtls_backend, + ), + None => link_libcoap_auto(&target_os, out_dir, requested_features, requested_dtls_backend), }?; let bindings_file = build_system.generate_bindings()?; @@ -84,7 +78,10 @@ fn main() -> Result<()> { match build_system.detected_features() { Some(detected_features) => { - let compile_time_checkable_features: EnumSet = requested_features.iter().filter(|feat| feat.define_name().is_some()).collect(); + let compile_time_checkable_features: EnumSet = requested_features + .iter() + .filter(|feat| feat.define_name().is_some()) + .collect(); if !bypass_compile_time_feature_checks && !compile_time_checkable_features.is_subset(detected_features) { let missing_features = requested_features.difference(detected_features); bail!( @@ -103,6 +100,8 @@ fn main() -> Result<()> { .collect::>() .join(", ") ); + } else if bypass_compile_time_feature_checks { + println!("cargo:warning=You have bypassed the libcoap-sys compile-time feature check.") } }, None => { @@ -113,29 +112,76 @@ fn main() -> Result<()> { Ok(()) } -#[cfg(target_os = "espidf")] -fn link_libcoap_espidf() -> Result { - // For ESP-IDF: Use esp-idf tooling. - todo!() +fn link_libcoap_explicit( + requested_build_system: &str, + target_os: &str, + out_dir: PathBuf, + requested_features: EnumSet, + requested_dtls_backend: Option, +) -> Result> { + match requested_build_system { + "vendored" if target_os == "espidf" => { + EspIdfBuildSystem::new(out_dir).map(|v| Box::::from(Box::new(v))) + }, + "vendored" => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) + .map(|v| Box::::from(Box::new(v))), + "pkgconfig" if target_os != "espidf" => { + PkgConfigBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend) + .map(|v| Box::::from(Box::new(v))) + }, + "manual" => ManualBuildSystem::link_with_libcoap(out_dir).map(|v| Box::::from(Box::new(v))), + v => Err(anyhow!("build system {v} is unknown or unsupported for this target")), + } + .context(format!( + "unable to link libcoap using force-configured build system {requested_build_system}" + )) } -#[cfg(unix)] -fn link_libcoap_unix( +fn vendored_libcoap_build( + target_os: &str, out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option, ) -> Result> { - // For unix-like systems: Use pkg-config. - if cfg!(feature = "vendored") { - VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) - } else { - PkgConfigBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend) - .map(|v| Box::::from(Box::new(v))) + // TODO: Later on, we'll probably want to use the CMake based build system for any host+target + // combination that the libcoap build documentation recommends CMake for (most notably: + // Windows). + // See: https://github.com/obgm/libcoap/blob/develop/BUILDING + match target_os { + "espidf" => EspIdfBuildSystem::new(out_dir).map(|v| Box::::from(Box::new(v))), + _ => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) + .map(|v| Box::::from(Box::new(v))), } } -#[cfg(windows)] -fn link_libcoap_windows() -> Result { - // For Windows, we currently only support manual setup (cmake would be a possible alternative). - todo!() +fn link_libcoap_auto( + target_os: &str, + out_dir: PathBuf, + requested_features: EnumSet, + requested_dtls_backend: Option, +) -> Result> { + let mut errors = Vec::<(&'static str, anyhow::Error)>::new(); + // Try vendored build first if the feature is enabled and supported by the host. + // If the vendored build fails on a supported target, do not try anything else (we assume that + // the user wanted to use the vendored library for a reason). + if cfg!(feature = "vendored") { + return vendored_libcoap_build(target_os, out_dir, requested_features, requested_dtls_backend); + } + PkgConfigBuildSystem::link_with_libcoap(out_dir.clone(), requested_dtls_backend) + .map(|v| Box::::from(Box::new(v))) + .or_else(|e| { + errors.push(("pkgconfig", e)); + ManualBuildSystem::link_with_libcoap(out_dir).map(|v| Box::::from(Box::new(v))) + }) + .or_else(|e| { + errors.push(("manual", e)); + Err(anyhow!( + "unable to find a version of libcoap to link with:\n{}", + errors + .iter() + .map(|(k, v)| format!("Build system {k} failed with error: {v:?}")) + .collect::>() + .join("\n") + )) + }) } diff --git a/libcoap/Cargo.toml b/libcoap/Cargo.toml index ef3ee875..4d24cb17 100644 --- a/libcoap/Cargo.toml +++ b/libcoap/Cargo.toml @@ -28,6 +28,11 @@ tcp = ["libcoap-sys/tcp"] tls = ["libcoap-sys/tls"] rand = ["dep:rand", "dep:rand_core"] vendored = ["libcoap-sys/vendored"] +dtls-openssl-sys = ["libcoap-sys/dtls-openssl-sys"] +dtls-mbedtls-sys = ["libcoap-sys/dtls-mbedtls-sys"] +dtls-tinydtls-sys = ["libcoap-sys/dtls-tinydtls-sys"] +dtls-openssl-sys-vendored = ["libcoap-sys/dtls-openssl-sys-vendored"] +dtls-tinydtls-sys-vendored = ["libcoap-sys/dtls-tinydtls-sys-vendored"] [dependencies] libcoap-sys = { version = "^0.2.2", path = "../libcoap-sys", default-features = false, features = ["client", "server"] } From d08a16dfaf5f93f5feab4cfce186ec6f13d51d8a Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Fri, 17 Jan 2025 22:14:57 +0100 Subject: [PATCH 07/26] refactor(sys): reimplement ESP-IDF build to re-export esp-idf-sys The previous implementation of the ESP-IDF build was rather fragile, as it involved manual specification of all transiently required header paths inside esp-idf to bindgen for header generation. The new implementation instead relies on esp-idf-sys to generate the bindings and then parses ESP-IDF's binding file, re-exporting all symbols starting with coap or oscore using . --- libcoap-sys/Cargo.toml | 7 +- libcoap-sys/build/build_system/esp_idf.rs | 72 +++++++++++++++++-- libcoap-sys/build/main.rs | 17 +++-- .../examples/esp-idf/.cargo/config.toml | 16 +++++ .../esp-idf/.github/workflows/rust_ci.yml | 41 +++++++++++ libcoap-sys/examples/esp-idf/.gitignore | 4 ++ libcoap-sys/examples/esp-idf/Cargo.toml | 34 +++++++++ libcoap-sys/examples/esp-idf/build.rs | 3 + .../examples/esp-idf/components_esp32c3.lock | 20 ++++++ .../examples/esp-idf/rust-toolchain.toml | 3 + .../examples/esp-idf/sdkconfig.defaults | 10 +++ libcoap-sys/examples/esp-idf/src/main.rs | 13 ++++ libcoap-sys/examples/esp-idf/vars.log | 0 13 files changed, 224 insertions(+), 16 deletions(-) create mode 100644 libcoap-sys/examples/esp-idf/.cargo/config.toml create mode 100644 libcoap-sys/examples/esp-idf/.github/workflows/rust_ci.yml create mode 100644 libcoap-sys/examples/esp-idf/.gitignore create mode 100644 libcoap-sys/examples/esp-idf/Cargo.toml create mode 100644 libcoap-sys/examples/esp-idf/build.rs create mode 100644 libcoap-sys/examples/esp-idf/components_esp32c3.lock create mode 100644 libcoap-sys/examples/esp-idf/rust-toolchain.toml create mode 100644 libcoap-sys/examples/esp-idf/sdkconfig.defaults create mode 100644 libcoap-sys/examples/esp-idf/src/main.rs create mode 100644 libcoap-sys/examples/esp-idf/vars.log diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 44127fe5..7af9f36a 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -144,7 +144,7 @@ libc = "^0.2.126" tinydtls-sys = { version = "^0.2.0", default-features = false, optional = true } [target.'cfg(target_os="espidf")'.dependencies] -esp-idf-sys = { version = ">0.35.0" } +esp-idf-sys = { version = "0.36.1" } [build-dependencies] bindgen = { version = "0.71.1", features = ["experimental"] } @@ -155,10 +155,11 @@ regex = "1.10.5" version-compare = "0.2.0" anyhow = { version = "1.0.94", features = ["backtrace"] } enumset = "1.1.5" +syn = { version = "2.0.96", features = ["full"] } [package.metadata.docs.rs] features = ["dtls", "dtls_backend_openssl", "vendored"] [[package.metadata.esp-idf-sys.extra_components]] -remote_component = { name = "espressif/coap", version = "4.3.4~3" } -bindings_header = "src/wrapper.h" +remote_component = { name = "espressif/coap", version = "4.3.5~3" } +bindings_header = "src/wrapper.h" \ No newline at end of file diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs index 3881c439..598332c8 100644 --- a/libcoap-sys/build/build_system/esp_idf.rs +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -1,17 +1,42 @@ use crate::build_system::BuildSystem; -use crate::metadata::LibcoapFeature; -use anyhow::Result; +use crate::metadata::{DtlsBackend, LibcoapFeature}; +use anyhow::{anyhow, Context, Result}; use enumset::EnumSet; +use std::env; +use std::fs::File; +use std::io::Write; +use std::iter::once; use std::path::PathBuf; +use syn::{ForeignItem, Ident, Item}; use version_compare::Version; pub struct EspIdfBuildSystem { out_dir: PathBuf, + esp_idf_bindings_file: PathBuf, + dtls_requested: bool, } impl EspIdfBuildSystem { - pub fn new(out_dir: PathBuf) -> Result { - Ok(Self { out_dir }) + pub fn new(out_dir: PathBuf, requested_dtls_backend: Option) -> Result { + let esp_idf_bindings_file = env::var_os("DEP_ESP_IDF_ROOT") + .map(PathBuf::from) + .expect("Environment variable DEP_ESP_IDF_ROOT has not been set by esp-idf-sys") + .join("bindings.rs"); + + let dtls_requested = if let Some(backend) = requested_dtls_backend { + if backend != DtlsBackend::MbedTls { + return Err(anyhow!("libcoap only supports the MbedTLS DTLS backend when compiling for the ESP-IDF, but you have requested the {backend} backend.")); + } + true + } else { + false + }; + + Ok(Self { + out_dir, + esp_idf_bindings_file, + dtls_requested, + }) } } @@ -21,10 +46,45 @@ impl BuildSystem for EspIdfBuildSystem { } fn version(&self) -> Option { - todo!() + None } fn generate_bindings(&mut self) -> Result { - todo!() + let esp_bindings_file = + std::fs::read_to_string(&self.esp_idf_bindings_file).context("unable to read ESP-IDF bindings file")?; + let parsed_esp_bindings_file = + syn::parse_file(&esp_bindings_file).context("unable to parse ESP-IDF bidnings file")?; + let bindings_file_path = self.out_dir.join("bindings.rs"); + let mut libcoap_bindings_file = File::create(&bindings_file_path).context("unable to create bindings file")?; + for item in parsed_esp_bindings_file.items { + let ident: Box> = match item { + Item::Const(v) => Box::new(once(v.ident)), + Item::Enum(v) => Box::new(once(v.ident)), + Item::ExternCrate(v) => Box::new(once(v.ident)), + Item::Fn(v) => Box::new(once(v.sig.ident)), + Item::Macro(v) => Box::new(v.ident.into_iter()), + Item::Mod(v) => Box::new(once(v.ident)), + Item::Static(v) => Box::new(once(v.ident)), + Item::Struct(v) => Box::new(once(v.ident)), + Item::Trait(v) => Box::new(once(v.ident)), + Item::TraitAlias(v) => Box::new(once(v.ident)), + Item::Type(v) => Box::new(once(v.ident)), + Item::Union(v) => Box::new(once(v.ident)), + Item::ForeignMod(v) => Box::new(v.items.into_iter().filter_map(|fe| match fe { + ForeignItem::Fn(fi) => Some(fi.sig.ident), + ForeignItem::Static(fi) => Some(fi.ident), + ForeignItem::Type(fi) => Some(fi.ident), + _ => None, + })), + _ => Box::new(std::iter::empty()), + }; + for ident in ident.map(|i| i.to_string()) { + if ident.to_lowercase().starts_with("coap") || ident.to_lowercase().starts_with("oscore") { + write!(&mut libcoap_bindings_file, "pub use esp_idf_sys::{};\n", ident) + .context("unable to write to bindings file")?; + } + } + } + Ok(bindings_file_path) } } diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 259b85dd..d8a404f7 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -72,7 +72,8 @@ fn main() -> Result<()> { bindings_file.canonicalize()?.display() ); - if build_system.version() < Version::from(MINIMUM_LIBCOAP_VERSION) { + let version = build_system.version(); + if version.is_none() || version < Version::from(MINIMUM_LIBCOAP_VERSION) { println!("cargo:warning=The linked version of libcoap is lower than the minimal version required for libcoap-sys ({}), this will most likely cause errors.", MINIMUM_LIBCOAP_VERSION); } @@ -121,7 +122,7 @@ fn link_libcoap_explicit( ) -> Result> { match requested_build_system { "vendored" if target_os == "espidf" => { - EspIdfBuildSystem::new(out_dir).map(|v| Box::::from(Box::new(v))) + EspIdfBuildSystem::new(out_dir, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) }, "vendored" => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) .map(|v| Box::::from(Box::new(v))), @@ -148,7 +149,9 @@ fn vendored_libcoap_build( // Windows). // See: https://github.com/obgm/libcoap/blob/develop/BUILDING match target_os { - "espidf" => EspIdfBuildSystem::new(out_dir).map(|v| Box::::from(Box::new(v))), + "espidf" => { + EspIdfBuildSystem::new(out_dir, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) + }, _ => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) .map(|v| Box::::from(Box::new(v))), } @@ -164,7 +167,7 @@ fn link_libcoap_auto( // Try vendored build first if the feature is enabled and supported by the host. // If the vendored build fails on a supported target, do not try anything else (we assume that // the user wanted to use the vendored library for a reason). - if cfg!(feature = "vendored") { + if cfg!(feature = "vendored") || target_os == "espidf" { return vendored_libcoap_build(target_os, out_dir, requested_features, requested_dtls_backend); } PkgConfigBuildSystem::link_with_libcoap(out_dir.clone(), requested_dtls_backend) @@ -173,15 +176,15 @@ fn link_libcoap_auto( errors.push(("pkgconfig", e)); ManualBuildSystem::link_with_libcoap(out_dir).map(|v| Box::::from(Box::new(v))) }) - .or_else(|e| { + .map_err(|e| { errors.push(("manual", e)); - Err(anyhow!( + anyhow!( "unable to find a version of libcoap to link with:\n{}", errors .iter() .map(|(k, v)| format!("Build system {k} failed with error: {v:?}")) .collect::>() .join("\n") - )) + ) }) } diff --git a/libcoap-sys/examples/esp-idf/.cargo/config.toml b/libcoap-sys/examples/esp-idf/.cargo/config.toml new file mode 100644 index 00000000..11494290 --- /dev/null +++ b/libcoap-sys/examples/esp-idf/.cargo/config.toml @@ -0,0 +1,16 @@ +[build] +target = "riscv32imc-esp-espidf" + +[target.riscv32imc-esp-espidf] +linker = "ldproxy" +runner = "espflash flash --monitor" +rustflags = [ "--cfg", "espidf_time64"] + +[unstable] +build-std = ["std", "panic_abort"] + +[env] +MCU="esp32c3" +# Note: this variable is not used by the pio builder (`cargo build --features pio`) +ESP_IDF_VERSION = "v5.2.3" + diff --git a/libcoap-sys/examples/esp-idf/.github/workflows/rust_ci.yml b/libcoap-sys/examples/esp-idf/.github/workflows/rust_ci.yml new file mode 100644 index 00000000..e7e9b1ec --- /dev/null +++ b/libcoap-sys/examples/esp-idf/.github/workflows/rust_ci.yml @@ -0,0 +1,41 @@ +name: Continuous Integration + +on: + push: + paths-ignore: + - "**/README.md" + pull_request: + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + rust-checks: + name: Rust Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + action: + - command: build + args: --release + - command: fmt + args: --all -- --check --color always + - command: clippy + args: --all-targets --all-features --workspace -- -D warnings + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Rust + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: nightly + components: rust-src rustfmt clippy + - name: Enable caching + uses: Swatinem/rust-cache@v2 + - name: Install ldproxy + run: cargo install ldproxy + - name: Run command + run: cargo ${{ matrix.action.command }} ${{ matrix.action.args }} diff --git a/libcoap-sys/examples/esp-idf/.gitignore b/libcoap-sys/examples/esp-idf/.gitignore new file mode 100644 index 00000000..73a638b5 --- /dev/null +++ b/libcoap-sys/examples/esp-idf/.gitignore @@ -0,0 +1,4 @@ +/.vscode +/.embuild +/target +/Cargo.lock diff --git a/libcoap-sys/examples/esp-idf/Cargo.toml b/libcoap-sys/examples/esp-idf/Cargo.toml new file mode 100644 index 00000000..12c6fa4e --- /dev/null +++ b/libcoap-sys/examples/esp-idf/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "esp-idf" +version = "0.1.0" +authors = ["Hugo Hakim Damer "] +edition = "2021" +resolver = "2" +rust-version = "1.77" + +[[bin]] +name = "esp-idf" +harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors + +[profile.release] +opt-level = "s" + +[profile.dev] +debug = true # Symbols are nice and they don't increase the size on Flash +opt-level = "z" + +[features] +default = [] + +experimental = ["esp-idf-svc/experimental"] + +[dependencies] +log = "0.4" +esp-idf-svc = { version = "0.51", features = ["critical-section", "embassy-time-driver", "embassy-sync"] } +esp-idf-sys = { version = "0.36.1" } +libcoap-sys = { version = "*", path = "../../" } + +[build-dependencies] +embuild = "0.33" + +[workspace] diff --git a/libcoap-sys/examples/esp-idf/build.rs b/libcoap-sys/examples/esp-idf/build.rs new file mode 100644 index 00000000..112ec3f7 --- /dev/null +++ b/libcoap-sys/examples/esp-idf/build.rs @@ -0,0 +1,3 @@ +fn main() { + embuild::espidf::sysenv::output(); +} diff --git a/libcoap-sys/examples/esp-idf/components_esp32c3.lock b/libcoap-sys/examples/esp-idf/components_esp32c3.lock new file mode 100644 index 00000000..b6391610 --- /dev/null +++ b/libcoap-sys/examples/esp-idf/components_esp32c3.lock @@ -0,0 +1,20 @@ +dependencies: + espressif/coap: + component_hash: a5d1b781b15980d9af136b5e63315dd5f5ec00215cc755f395ad2fb4977c7668 + dependencies: + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 4.3.5~3 + idf: + source: + type: idf + version: 5.2.3 +direct_dependencies: +- espressif/coap +manifest_hash: 13fcf89b865b0f073a765a0dfdcf062a0063b4af7a631d3d37ef94c920538a25 +target: esp32c3 +version: 2.0.0 diff --git a/libcoap-sys/examples/esp-idf/rust-toolchain.toml b/libcoap-sys/examples/esp-idf/rust-toolchain.toml new file mode 100644 index 00000000..f70d2254 --- /dev/null +++ b/libcoap-sys/examples/esp-idf/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly" +components = ["rust-src"] diff --git a/libcoap-sys/examples/esp-idf/sdkconfig.defaults b/libcoap-sys/examples/esp-idf/sdkconfig.defaults new file mode 100644 index 00000000..c25b89d9 --- /dev/null +++ b/libcoap-sys/examples/esp-idf/sdkconfig.defaults @@ -0,0 +1,10 @@ +# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K) +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000 + +# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default). +# This allows to use 1 ms granularity for thread sleeps (10 ms by default). +#CONFIG_FREERTOS_HZ=1000 + +# Workaround for https://github.com/espressif/esp-idf/issues/7631 +#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n +#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n diff --git a/libcoap-sys/examples/esp-idf/src/main.rs b/libcoap-sys/examples/esp-idf/src/main.rs new file mode 100644 index 00000000..f60f82b5 --- /dev/null +++ b/libcoap-sys/examples/esp-idf/src/main.rs @@ -0,0 +1,13 @@ + +use libcoap_sys as _; + +fn main() { + // It is necessary to call this function once. Otherwise some patches to the runtime + // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 + esp_idf_svc::sys::link_patches(); + + // Bind the log crate to the ESP Logging facilities + esp_idf_svc::log::EspLogger::initialize_default(); + + log::info!("Hello, world!"); +} diff --git a/libcoap-sys/examples/esp-idf/vars.log b/libcoap-sys/examples/esp-idf/vars.log new file mode 100644 index 00000000..e69de29b From 478bfd0af20ee64a46f2dd95d5ae15d3d4105f62 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Sat, 18 Jan 2025 01:26:27 +0100 Subject: [PATCH 08/26] refactor(sys): improve reliability of new build script, add ESP-IDF compile-time checks --- libcoap-sys/Cargo.toml | 5 +- libcoap-sys/build/bindings.rs | 37 +-- libcoap-sys/build/build_system/esp_idf.rs | 57 ++++- libcoap-sys/build/build_system/pkgconfig.rs | 8 +- libcoap-sys/build/build_system/vendored.rs | 12 +- libcoap-sys/build/main.rs | 21 +- libcoap-sys/build/metadata.rs | 67 +++++- .../examples/esp-idf/sdkconfig.defaults | 11 + libcoap-sys/src/lib.rs | 214 +++++++++--------- 9 files changed, 262 insertions(+), 170 deletions(-) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 7af9f36a..ed626327 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -155,11 +155,12 @@ regex = "1.10.5" version-compare = "0.2.0" anyhow = { version = "1.0.94", features = ["backtrace"] } enumset = "1.1.5" -syn = { version = "2.0.96", features = ["full"] } +syn = { version = "2.0.96" } +embuild = { version = "0.33.0", features = ["espidf"] } [package.metadata.docs.rs] features = ["dtls", "dtls_backend_openssl", "vendored"] [[package.metadata.esp-idf-sys.extra_components]] remote_component = { name = "espressif/coap", version = "4.3.5~3" } -bindings_header = "src/wrapper.h" \ No newline at end of file +bindings_header = "src/wrapper.h" diff --git a/libcoap-sys/build/bindings.rs b/libcoap-sys/build/bindings.rs index aead1147..07102e90 100644 --- a/libcoap-sys/build/bindings.rs +++ b/libcoap-sys/build/bindings.rs @@ -6,7 +6,7 @@ use bindgen::{ EnumVariation, }; -use crate::metadata::{LibcoapDefineInfo, LibcoapFeature}; +use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}; /// Implementation of bindgen's [ParseCallbacks] that allow reading some meta-information about the /// used libcoap version from its defines (package version, supported features, ...) @@ -31,34 +31,21 @@ impl LibcoapDefineParser { impl ParseCallbacks for LibcoapDefineParser { fn int_macro(&self, name: &str, value: i64) -> Option { - self.defines.borrow_mut().supported_features |= LibcoapFeature::features_from_define(name, value); + let mut defines = self.defines.borrow_mut(); + defines.supported_features |= LibcoapFeature::features_from_define(name, value); + if let Some(dtls_backend) = DtlsBackend::library_from_define(name, value) { + if let Some(old_backend) = defines.dtls_backend.replace(dtls_backend) { + println!("cargo:warning=The libcoap header files indicate that more than one DTLS library is active at the same time ({dtls_backend} and {old_backend}), which should not be possible. Are the header paths misconfigured?"); + } + } None } fn str_macro(&self, name: &str, value: &[u8]) { - /*// Will allow this here, as we might want to add additional cfg flags later on. - #[allow(clippy::single_match)] - match name { - "LIBCOAP_PACKAGE_VERSION" => { - let version_str = String::from_utf8_lossy(value); - println!("cargo:rustc-cfg=libcoap_version=\"{}\"", version_str.as_ref()); - println!("cargo:libcoap_version={}", version_str.as_ref()); - let version = Version::from(version_str.as_ref()).expect("invalid libcoap version"); - match version.compare(Version::from("4.3.4").unwrap()) { - Cmp::Gt => println!("cargo:rustc-cfg=non_inlined_coap_send_rst"), - _ => {}, - } - self.defines.borrow_mut().package_version = version.to_string(); - }, - _ => {}, - }*/ - } - - fn include_file(&self, filename: &str) { - /*let header_path = Path::new(filename); - if header_path.file_name().eq(&Some(OsStr::new("coap_defines.h"))) { - self.defines.borrow_mut().feature_defines_available = true; - }*/ + if name == "LIBCOAP_PACKAGE_VERSION" { + let version_str = String::from_utf8_lossy(value); + self.defines.borrow_mut().version = Some(version_str.to_string()) + } } } diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs index 598332c8..c6c2cd26 100644 --- a/libcoap-sys/build/build_system/esp_idf.rs +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -13,36 +13,46 @@ use version_compare::Version; pub struct EspIdfBuildSystem { out_dir: PathBuf, esp_idf_bindings_file: PathBuf, - dtls_requested: bool, + requested_features: EnumSet, } impl EspIdfBuildSystem { - pub fn new(out_dir: PathBuf, requested_dtls_backend: Option) -> Result { + pub fn new(out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option) -> Result { + embuild::espidf::sysenv::output(); let esp_idf_bindings_file = env::var_os("DEP_ESP_IDF_ROOT") .map(PathBuf::from) .expect("Environment variable DEP_ESP_IDF_ROOT has not been set by esp-idf-sys") .join("bindings.rs"); - let dtls_requested = if let Some(backend) = requested_dtls_backend { + if let Some(backend) = requested_dtls_backend { if backend != DtlsBackend::MbedTls { return Err(anyhow!("libcoap only supports the MbedTLS DTLS backend when compiling for the ESP-IDF, but you have requested the {backend} backend.")); } - true - } else { - false - }; + } Ok(Self { out_dir, esp_idf_bindings_file, - dtls_requested, + requested_features }) } } impl BuildSystem for EspIdfBuildSystem { fn detected_features(&self) -> Option> { - None + // We ensure the availability of some requested features by generating checks for the + // cfg values set by esp-idf-sys based on the used sdkconfig. + // Therefore, we can tell the build script feature checker that all requested features are + // available (to mute the warning about there being no feature check). + + // However, do warn the user if features are requested that cannot be checked this way, but + // would be checkable if the defines-based checker was used. + let uncheckable_features: EnumSet = self.requested_features.iter().filter(|v| v.define_name().is_some() && v.sdkconfig_flag_name().is_none()).collect(); + if !uncheckable_features.is_empty() { + println!("cargo:warning=When building for ESP-IDF, the availability of the following requested features that usually can be checked during compile time can only be checked during runtime instead: {}", uncheckable_features.iter().map(|v| v.as_str()).collect::>().join(", ")) + } + + Some(self.requested_features) } fn version(&self) -> Option { @@ -50,13 +60,19 @@ impl BuildSystem for EspIdfBuildSystem { } fn generate_bindings(&mut self) -> Result { + // Find, read and parse the Rust bindings generated by esp-idf-sys. let esp_bindings_file = std::fs::read_to_string(&self.esp_idf_bindings_file).context("unable to read ESP-IDF bindings file")?; let parsed_esp_bindings_file = syn::parse_file(&esp_bindings_file).context("unable to parse ESP-IDF bidnings file")?; + + // Create file for our own bindings. let bindings_file_path = self.out_dir.join("bindings.rs"); let mut libcoap_bindings_file = File::create(&bindings_file_path).context("unable to create bindings file")?; + + // Iterate over all items in the esp-idf-sys bindings file. for item in parsed_esp_bindings_file.items { + // Find the list of identifiers provided by this item. let ident: Box> = match item { Item::Const(v) => Box::new(once(v.ident)), Item::Enum(v) => Box::new(once(v.ident)), @@ -78,13 +94,34 @@ impl BuildSystem for EspIdfBuildSystem { })), _ => Box::new(std::iter::empty()), }; + for ident in ident.map(|i| i.to_string()) { + // If the item belongs to the libcoap crate (starts with coap or oscore), re-export it in our bindings. if ident.to_lowercase().starts_with("coap") || ident.to_lowercase().starts_with("oscore") { - write!(&mut libcoap_bindings_file, "pub use esp_idf_sys::{};\n", ident) + writeln!(&mut libcoap_bindings_file, "pub use esp_idf_sys::{};", ident) .context("unable to write to bindings file")?; } } } + + // Add check whether the libcoap component is enabled in ESP-IDF to generated bindings file. + println!("cargo::rustc-check-cfg=cfg(esp_idf_comp_espressif__coap_enabled)"); + writeln!(&mut libcoap_bindings_file, "#[cfg(not(esp_idf_comp_espressif__coap_enabled))]") + .context("unable to write to bindings file")?; + writeln!(&mut libcoap_bindings_file, "compile_error!(\"You are building libcoap-sys for an ESP-IDF target, but have not added the espressif/coap remote component (see the libcoap-sys documentation for more information)\");") + .context("unable to write to bindings file")?; + + + for (feature_name, feature_flag) in self.requested_features.iter().filter_map(|v| v.sdkconfig_flag_name().map(|flag| (v.as_str(), flag))) { + + // For some reason, embuild adds expected cfg flags for some, but not all feature-related sdkconfig flags, causing warnings if we don't do this. + println!("cargo::rustc-check-cfg=cfg(esp_idf_{})", feature_flag.to_lowercase()); + + writeln!(&mut libcoap_bindings_file, "#[cfg(not(esp_idf_{}))]", feature_flag.to_lowercase()).context("unable to write to bindings file")?; + writeln!(&mut libcoap_bindings_file, "compile_error!(\"Requested feature \\\"{feature_name}\\\" is not enabled in ESP-IDF sdkconfig.defaults (set `CONFIG_{feature_flag}=y` to fix this)\");") + .context("unable to write to bindings file")?; + } + Ok(bindings_file_path) } } diff --git a/libcoap-sys/build/build_system/pkgconfig.rs b/libcoap-sys/build/build_system/pkgconfig.rs index 398363a3..ea461f6a 100644 --- a/libcoap-sys/build/build_system/pkgconfig.rs +++ b/libcoap-sys/build/build_system/pkgconfig.rs @@ -1,6 +1,6 @@ use std::{cell::RefCell, path::PathBuf}; -use anyhow::Context; +use anyhow::{anyhow, Context}; use enumset::EnumSet; use pkg_config::Library; use version_compare::Version; @@ -72,6 +72,12 @@ impl BuildSystem for PkgConfigBuildSystem { self.define_info = Some(RefCell::take(&define_info)); + if let Some(version) = &self.define_info.as_ref().unwrap().version { + if Version::from(&self.library.version) != Version::from(version) { + return Err(anyhow!("The library version indicated by pkg-config does not match the one indicated by the headers. Are the include paths misconfigured?")) + } + } + let out_path = self.out_dir.join("bindings.rs"); bindings .write_to_file(&out_path) diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index 9082bd97..ee6a1ca8 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -2,7 +2,6 @@ use std::cell::RefCell; use std::env; use std::env::VarError; use std::ffi::OsString; -use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::process::Command; use anyhow::{anyhow, ensure, Context, Result}; @@ -10,7 +9,7 @@ use enumset::EnumSet; use version_compare::Version; use crate::bindings::{generate_libcoap_bindings, LibcoapDefineParser}; use crate::build_system::BuildSystem; -use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature, MINIMUM_LIBCOAP_VERSION}; +use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}; const VENDORED_LIBCOAP_VERSION: &str = "4.3.5"; @@ -98,6 +97,8 @@ impl VendoredBuildSystem { // For each one, set the appropriate PKG_CONFIG_PATHs, CFLAGS and/or LIBS to use them // instead of system versions if they are going to be used. let mut additional_pkg_config_paths: Vec = vec![]; + // May be unused if none of the DTLS crate features has been enabled. + #[allow(unused_mut)] let mut dtls_libraries_linked_by_other_crates = EnumSet::::empty(); #[cfg(feature = "dtls-tinydtls-sys")] { @@ -246,7 +247,6 @@ impl VendoredBuildSystem { // Add TinyDTLS's pkg-config directory to the path for version checking. Ok(( Some(PathBuf::from(tinydtls_libs) - // TODO: Isn't this already the lib subdirectory? .join("lib") .join("pkgconfig")), true)) } @@ -362,6 +362,12 @@ impl BuildSystem for VendoredBuildSystem { self.define_info = Some(RefCell::take(&define_info)); + if let Some(version) = &self.define_info.as_ref().unwrap().version { + if Version::from(VENDORED_LIBCOAP_VERSION) != Version::from(version) { + return Err(anyhow!("The library version indicated by the headers does not match the vendored version that should be in use. Are the include paths misconfigured?")) + } + } + let out_path = self.out_dir.join("bindings.rs"); bindings .write_to_file(&out_path) diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index d8a404f7..5739470b 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, env, env::VarError, path::PathBuf}; +use std::{env, env::VarError, path::PathBuf}; use anyhow::{anyhow, bail, Context, Result}; use enumset::EnumSet; @@ -24,15 +24,11 @@ fn main() -> Result<()> { env::var_os("OUT_DIR").expect("no OUT_DIR was provided (are we not running as a cargo build script?)"), ); - let target = env::var("TARGET").expect("unable to parse TARGET env variable"); - let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("unable to parse CARGO_CFG_TARGET_ENV env variable"); let target_os = env::var("CARGO_CFG_TARGET_OS").expect("unable to parse CARGO_CFG_TARGET_OS env variable"); - let target_family = - env::var("CARGO_CFG_TARGET_FAMILY").expect("unable to parse CARGO_CFG_TARGET_FAMILY env variable"); let requested_features: EnumSet = EnumSet::::all() .iter() - .filter(|feat| std::env::var_os(format!("CARGO_FEATURE_{}", feat.cargo_feature_var_name())).is_some()) + .filter(|feat| env::var_os(format!("CARGO_FEATURE_{}", feat.cargo_feature_var_suffix())).is_some()) .collect(); let requested_dtls_backend: Option = match env::var("LIBCOAP_RS_DTLS_BACKEND") { @@ -50,12 +46,12 @@ fn main() -> Result<()> { let bypass_compile_time_feature_checks = match env::var("LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS") { Ok(v) if v == "0" => Ok(false), - Ok(v) => Ok(true), + Ok(_v) => Ok(true), Err(VarError::NotPresent) => Ok(false), Err(e) => Err(e).context("unable to parse environment variable LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS"), }?; - let mut build_system: Box = match chosen_build_system.as_ref().map(String::as_str) { + let mut build_system: Box = match chosen_build_system.as_deref() { Some(requested_build_system) => link_libcoap_explicit( requested_build_system, &target_os, @@ -73,8 +69,10 @@ fn main() -> Result<()> { ); let version = build_system.version(); - if version.is_none() || version < Version::from(MINIMUM_LIBCOAP_VERSION) { + if version.is_some() && version < Version::from(MINIMUM_LIBCOAP_VERSION) { println!("cargo:warning=The linked version of libcoap is lower than the minimal version required for libcoap-sys ({}), this will most likely cause errors.", MINIMUM_LIBCOAP_VERSION); + } else if version.is_none() { + println!("cargo:warning=Unable to automatically detect the linked version of libcoap, please manually ensure that the used version is at least {}.", MINIMUM_LIBCOAP_VERSION); } match build_system.detected_features() { @@ -122,8 +120,9 @@ fn link_libcoap_explicit( ) -> Result> { match requested_build_system { "vendored" if target_os == "espidf" => { - EspIdfBuildSystem::new(out_dir, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) + EspIdfBuildSystem::new(out_dir, requested_features, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) }, + "vendored" if cfg!(not(feature = "vendored")) => Err(anyhow!("LIBCOAP_RS_BUILD_SYSTEM has been set to \"vendored\", but the corresponding crate feature \"vendored\" has not been enabled.")), "vendored" => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) .map(|v| Box::::from(Box::new(v))), "pkgconfig" if target_os != "espidf" => { @@ -150,7 +149,7 @@ fn vendored_libcoap_build( // See: https://github.com/obgm/libcoap/blob/develop/BUILDING match target_os { "espidf" => { - EspIdfBuildSystem::new(out_dir, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) + EspIdfBuildSystem::new(out_dir, requested_features, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) }, _ => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) .map(|v| Box::::from(Box::new(v))), diff --git a/libcoap-sys/build/metadata.rs b/libcoap-sys/build/metadata.rs index cc826147..8cd0377e 100644 --- a/libcoap-sys/build/metadata.rs +++ b/libcoap-sys/build/metadata.rs @@ -1,5 +1,5 @@ use std::{ - fmt::{Display, Formatter, Write}, + fmt::{Display, Formatter}, str::FromStr, }; @@ -11,6 +11,7 @@ pub const MINIMUM_LIBCOAP_VERSION: &str = "4.3.5"; #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] pub struct LibcoapDefineInfo { pub version: Option, + pub dtls_backend: Option, pub supported_features: EnumSet, } @@ -42,30 +43,37 @@ pub enum LibcoapFeature { } impl LibcoapFeature { + /// Returns the name of the #define in `coap_defines.h` that corresponds to the given feature, + /// or None if there is no direct correspondence to such a #define. pub fn define_name(&self) -> Option<&'static str> { match self { LibcoapFeature::AfUnix => Some("COAP_AF_UNIX_SUPPORT"), LibcoapFeature::Async => Some("COAP_ASYNC_SUPPORT"), LibcoapFeature::Client => Some("COAP_CLIENT_SUPPORT"), LibcoapFeature::SmallStack => Some("COAP_CONSTRAINED_STACK"), - LibcoapFeature::Tcp => Some("COAP_DISABLE_TCP"), // TODO invert + LibcoapFeature::Tcp => Some("COAP_DISABLE_TCP"), LibcoapFeature::Epoll => Some("COAP_EPOLL_SUPPORT"), LibcoapFeature::Ipv4 => Some("COAP_IPV4_SUPPORT"), LibcoapFeature::Ipv6 => Some("COAP_IPV6_SUPPORT"), LibcoapFeature::Oscore => Some("COAP_OSCORE_SUPPORT"), - // TODO proxy support? LibcoapFeature::QBlock => Some("COAP_Q_BLOCK_SUPPORT"), LibcoapFeature::Server => Some("COAP_SERVER_SUPPORT"), LibcoapFeature::ThreadRecursiveLockDetection => Some("COAP_THREAD_RECURSIVE_CHECK"), LibcoapFeature::ThreadSafe => Some("COAP_THREAD_SAFE"), - LibcoapFeature::Dtls => None, // TODO has multiple defines LibcoapFeature::ObservePersist => Some("COAP_WITH_OBSERVE_PERSIST"), LibcoapFeature::WebSockets => Some("COAP_WS_SUPPORT"), _ => None, } } + /// Returns the set of features that are supposedly enabled if the #define with the name + /// `define_name` is set to `define_value` in `coap_defines.h`, or an empty set if the provided + /// define does not correspond to such a feature. pub fn features_from_define(define_name: &str, define_value: i64) -> EnumSet { + // Only consider values != 0. + if define_name != "COAP_DISABLE_TCP" && define_value == 0 { + return EnumSet::empty(); + } match define_name { "COAP_AF_UNIX_SUPPORT" => EnumSet::from(LibcoapFeature::AfUnix), "COAP_ASYNC_SUPPORT" => EnumSet::from(LibcoapFeature::Async), @@ -97,6 +105,8 @@ impl LibcoapFeature { } } + /// Return the configure argument name (--enable-) that can be provided to libcoap's + /// configure-script to enable the given feature (or None if no such flag is available). pub fn configure_flag_name(&self) -> Option<&'static str> { match self { LibcoapFeature::AfUnix => Some("af-unix"), @@ -119,7 +129,30 @@ impl LibcoapFeature { } } - pub fn cargo_feature_var_name(&self) -> String { + /// Return the ESP-IDF sdkconfig option name that must be set to enable this feature, or None + /// if no configuration option is available. + /// Reference: https://github.com/espressif/idf-extra-components/blob/master/coap/Kconfig + pub fn sdkconfig_flag_name(&self) -> Option<&'static str> { + match self { + LibcoapFeature::DtlsPsk => Some("COAP_MBEDTLS_PSK"), + LibcoapFeature::DtlsPki => Some("COAP_MBEDTLS_PKI"), + LibcoapFeature::Tcp => Some("COAP_TCP_SUPPORT"), + LibcoapFeature::Oscore => Some("COAP_OSCORE_SUPPORT"), + LibcoapFeature::ObservePersist => Some("COAP_OBSERVE_PERSIST"), + LibcoapFeature::QBlock => Some("COAP_Q_BLOCK"), + LibcoapFeature::Async => Some("COAP_ASYNC_SUPPORT"), + LibcoapFeature::ThreadSafe => Some("COAP_THREAD_SAFE"), + LibcoapFeature::ThreadRecursiveLockDetection => Some("COAP_THREAD_RECURSIVE_CHECK"), + LibcoapFeature::WebSockets => Some("COAP_WEBSOCKETS"), + LibcoapFeature::Client => Some("COAP_CLIENT_SUPPORT"), + LibcoapFeature::Server => Some("COAP_SERVER_SUPPORT"), + _ => None, + } + } + + /// Returns the suffix of the CARGO_FEATURE_ environment variable that must be set + /// during build script execution if the feature has been enabled. + pub fn cargo_feature_var_suffix(&self) -> String { self.as_str().to_uppercase().replace('-', "_") } @@ -152,7 +185,7 @@ impl LibcoapFeature { } } -#[derive(Debug, EnumSetType)] +#[derive(Debug, EnumSetType, Hash)] pub enum DtlsBackend { GnuTls, OpenSsl, @@ -183,8 +216,11 @@ impl Display for DtlsBackend { } impl DtlsBackend { + /// Returns the suffix that has to be appended to libcoap-3- to find a library + /// version linked against the right DTLS library. pub fn pkg_config_suffix(&self) -> &'static str { - // just keeping this here in case we ever need to change this definition for some libraries. + // just keeping this here in case we ever need to change this definition to something + // different than self.as_str() for some libraries. self.as_str() } @@ -197,4 +233,21 @@ impl DtlsBackend { DtlsBackend::WolfSsl => "wolfssl", } } + + /// Returns the DTLS library that is supposedly enabled if the #define with the name + /// `define_name` is set to `define_value` in `coap_defines.h`, or None if the provided + /// define does not correspond to a DTLS library. + pub fn library_from_define(define_name: &str, define_value: i64) -> Option { + if define_value == 0 { + return None + } + match define_name { + "COAP_WITH_LIBGNUTLS" => Some(DtlsBackend::GnuTls), + "COAP_WITH_LIBMBEDTLS" => Some(DtlsBackend::MbedTls), + "COAP_WITH_LIBOPENSSL" => Some(DtlsBackend::OpenSsl), + "COAP_WITH_LIBTINYDTLS" => Some(DtlsBackend::TinyDtls), + "COAP_WITH_LIBWOLFSSL" => Some(DtlsBackend::WolfSsl), + _ => None, + } + } } diff --git a/libcoap-sys/examples/esp-idf/sdkconfig.defaults b/libcoap-sys/examples/esp-idf/sdkconfig.defaults index c25b89d9..81361c8b 100644 --- a/libcoap-sys/examples/esp-idf/sdkconfig.defaults +++ b/libcoap-sys/examples/esp-idf/sdkconfig.defaults @@ -8,3 +8,14 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000 # Workaround for https://github.com/espressif/esp-idf/issues/7631 #CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n #CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n + +CONFIG_COAP_CLIENT_SUPPORT=y +CONFIG_COAP_SERVER_SUPPORT=y +CONFIG_COAP_TCP_SUPPORT=y +CONFIG_COAP_OSCORE_SUPPORT=y +CONFIG_COAP_Q_BLOCK=y +CONFIG_COAP_SERVER_SUPPORT=y +CONFIG_COAP_THREAD_RECURSIVE_CHECK=y +CONFIG_COAP_THREAD_SAFE=y +CONFIG_COAP_OBSERVE_PERSIST=y +CONFIG_COAP_WEBSOCKETS=y \ No newline at end of file diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 7b0f8367..5231ba15 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -165,118 +165,110 @@ pub unsafe fn coap_string_equal_internal( /// This behavior will be changed as soon as libcoap 4.3.5 is released, after which libcoap 4.3.5 /// will become the minimum supported version and these checks will be mandatory. pub fn coap_startup_with_feature_checks() { - // only compile checks if they are available for the given libcoap version. - #[cfg(feature_checks_available)] - { - #[cfg(feature = "af-unix")] - // SAFETY: Function is always safe to call. - if unsafe { coap_af_unix_is_supported() != 1 } { - panic!("Required feature \"af-unix\" is not supported by libcoap") - } - #[cfg(feature = "async")] - // SAFETY: Function is always safe to call. - if unsafe { coap_async_is_supported() != 1 } { - panic!("Required feature \"async\" is not supported by libcoap") - } - #[cfg(feature = "client")] - // SAFETY: Function is always safe to call. - if unsafe { coap_client_is_supported() != 1 } { - panic!("Required feature \"ipv4\" is not supported by libcoap") - } - #[cfg(feature = "dtls")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_is_supported() != 1 } { - panic!("Required feature \"dtls\" is not supported by libcoap") - } - #[cfg(feature = "dtls-cid")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_cid_is_supported() != 1 } { - panic!("Required feature \"dtls-cid\" is not supported by libcoap") - } - #[cfg(feature = "dtls-psk")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_psk_is_supported() != 1 } { - panic!("Required feature \"dtls-psk\" is not supported by libcoap") - } - #[cfg(feature = "dtls-pki")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_pki_is_supported() != 1 } { - panic!("Required feature \"dtls-pki\" is not supported by libcoap") - } - #[cfg(feature = "dtls-pkcs11")] - // SAFETY: Function is always safe to call. - if !unsafe { coap_dtls_pkcs11_is_supported() == 1 } { - panic!("Required feature \"dtls-pkcs11\" is not supported by libcoap") - } - #[cfg(feature = "dtls-rpk")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_rpk_is_supported() != 1 } { - panic!("Required feature \"dtls-rpk\" is not supported by libcoap") - } - #[cfg(feature = "epoll")] - // SAFETY: Function is always safe to call. - if unsafe { coap_epoll_is_supported() != 1 } { - panic!("Required feature \"epoll\" is not supported by libcoap") - } - #[cfg(feature = "ipv4")] - // SAFETY: Function is always safe to call. - if !unsafe { coap_ipv4_is_supported() == 1 } { - panic!("Required feature \"ipv4\" is not supported by libcoap") - } - #[cfg(feature = "ipv6")] - // SAFETY: Function is always safe to call. - if !unsafe { coap_ipv6_is_supported() == 1 } { - panic!("Required feature \"ipv6\" is not supported by libcoap") - } - #[cfg(feature = "observe-persist")] - // SAFETY: Function is always safe to call. - if unsafe { coap_observe_persist_is_supported() != 1 } { - panic!("Required feature \"ipv4\" is not supported by libcoap") - } - #[cfg(feature = "oscore")] - // SAFETY: Function is always safe to call. - if unsafe { coap_oscore_is_supported() != 1 } { - panic!("Required feature \"oscore\" is not supported by libcoap") - } - #[cfg(feature = "q-block")] - // SAFETY: Function is always safe to call. - if unsafe { coap_q_block_is_supported() != 1 } { - panic!("Required feature \"q-block\" is not supported by libcoap") - } - #[cfg(feature = "server")] - // SAFETY: Function is always safe to call. - if unsafe { coap_server_is_supported() != 1 } { - panic!("Required feature \"ipv4\" is not supported by libcoap") - } - #[cfg(feature = "tcp")] - // SAFETY: Function is always safe to call. - if unsafe { coap_tcp_is_supported() != 1 } { - panic!("Required feature \"tcp\" is not supported by libcoap") - } - #[cfg(feature = "thread-safe")] - // SAFETY: Function is always safe to call. - if unsafe { coap_threadsafe_is_supported() != 1 } { - panic!("Required feature \"thread-safe\" is not supported by libcoap") - } - #[cfg(feature = "tls")] - // SAFETY: Function is always safe to call. - if unsafe { coap_tls_is_supported() != 1 } { - panic!("Required feature \"tls\" is not supported by libcoap") - } - #[cfg(feature = "websockets")] - // SAFETY: Function is always safe to call. - if unsafe { coap_ws_is_supported() != 1 } { - panic!("Required feature \"websockets\" is not supported by libcoap") - } - #[cfg(feature = "secure-websockets")] - // SAFETY: Function is always safe to call. - if unsafe { coap_wss_is_supported() != 1 } { - panic!("Required feature \"websockets\" is not supported by libcoap") - } + #[cfg(feature = "af-unix")] + // SAFETY: Function is always safe to call. + if unsafe { coap_af_unix_is_supported() != 1 } { + panic!("Required feature \"af-unix\" is not supported by libcoap") + } + #[cfg(feature = "async")] + // SAFETY: Function is always safe to call. + if unsafe { coap_async_is_supported() != 1 } { + panic!("Required feature \"async\" is not supported by libcoap") } - #[cfg(not(feature_checks_available))] - { - println!("WARNING: coap_startup_with_feature_checks() could not assert the availability of features because the linked version of libcoap is too old (< 4.3.5rc3)!") + #[cfg(feature = "client")] + // SAFETY: Function is always safe to call. + if unsafe { coap_client_is_supported() != 1 } { + panic!("Required feature \"ipv4\" is not supported by libcoap") + } + #[cfg(feature = "dtls")] + // SAFETY: Function is always safe to call. + if unsafe { coap_dtls_is_supported() != 1 } { + panic!("Required feature \"dtls\" is not supported by libcoap") + } + #[cfg(feature = "dtls-cid")] + // SAFETY: Function is always safe to call. + if unsafe { coap_dtls_cid_is_supported() != 1 } { + panic!("Required feature \"dtls-cid\" is not supported by libcoap") + } + #[cfg(feature = "dtls-psk")] + // SAFETY: Function is always safe to call. + if unsafe { coap_dtls_psk_is_supported() != 1 } { + panic!("Required feature \"dtls-psk\" is not supported by libcoap") + } + #[cfg(feature = "dtls-pki")] + // SAFETY: Function is always safe to call. + if unsafe { coap_dtls_pki_is_supported() != 1 } { + panic!("Required feature \"dtls-pki\" is not supported by libcoap") + } + #[cfg(feature = "dtls-pkcs11")] + // SAFETY: Function is always safe to call. + if !unsafe { coap_dtls_pkcs11_is_supported() == 1 } { + panic!("Required feature \"dtls-pkcs11\" is not supported by libcoap") + } + #[cfg(feature = "dtls-rpk")] + // SAFETY: Function is always safe to call. + if unsafe { coap_dtls_rpk_is_supported() != 1 } { + panic!("Required feature \"dtls-rpk\" is not supported by libcoap") + } + #[cfg(feature = "epoll")] + // SAFETY: Function is always safe to call. + if unsafe { coap_epoll_is_supported() != 1 } { + panic!("Required feature \"epoll\" is not supported by libcoap") + } + #[cfg(feature = "ipv4")] + // SAFETY: Function is always safe to call. + if !unsafe { coap_ipv4_is_supported() == 1 } { + panic!("Required feature \"ipv4\" is not supported by libcoap") + } + #[cfg(feature = "ipv6")] + // SAFETY: Function is always safe to call. + if !unsafe { coap_ipv6_is_supported() == 1 } { + panic!("Required feature \"ipv6\" is not supported by libcoap") + } + #[cfg(feature = "observe-persist")] + // SAFETY: Function is always safe to call. + if unsafe { coap_observe_persist_is_supported() != 1 } { + panic!("Required feature \"ipv4\" is not supported by libcoap") + } + #[cfg(feature = "oscore")] + // SAFETY: Function is always safe to call. + if unsafe { coap_oscore_is_supported() != 1 } { + panic!("Required feature \"oscore\" is not supported by libcoap") + } + #[cfg(feature = "q-block")] + // SAFETY: Function is always safe to call. + if unsafe { coap_q_block_is_supported() != 1 } { + panic!("Required feature \"q-block\" is not supported by libcoap") + } + #[cfg(feature = "server")] + // SAFETY: Function is always safe to call. + if unsafe { coap_server_is_supported() != 1 } { + panic!("Required feature \"ipv4\" is not supported by libcoap") + } + #[cfg(feature = "tcp")] + // SAFETY: Function is always safe to call. + if unsafe { coap_tcp_is_supported() != 1 } { + panic!("Required feature \"tcp\" is not supported by libcoap") + } + #[cfg(feature = "thread-safe")] + // SAFETY: Function is always safe to call. + if unsafe { coap_threadsafe_is_supported() != 1 } { + panic!("Required feature \"thread-safe\" is not supported by libcoap") + } + #[cfg(feature = "tls")] + // SAFETY: Function is always safe to call. + if unsafe { coap_tls_is_supported() != 1 } { + panic!("Required feature \"tls\" is not supported by libcoap") + } + #[cfg(feature = "websockets")] + // SAFETY: Function is always safe to call. + if unsafe { coap_ws_is_supported() != 1 } { + panic!("Required feature \"websockets\" is not supported by libcoap") + } + #[cfg(feature = "secure-websockets")] + // SAFETY: Function is always safe to call. + if unsafe { coap_wss_is_supported() != 1 } { + panic!("Required feature \"websockets\" is not supported by libcoap") } // SAFETY: Function is always safe to call. unsafe { coap_startup() } From 88758b1fb6841ed00918b863034f3f683ace3e9c Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Sat, 18 Jan 2025 02:01:59 +0100 Subject: [PATCH 09/26] feat(sys): Allow manual specification of include and library paths --- libcoap-sys/build/build_system/manual.rs | 87 +++++++++++++++++++-- libcoap-sys/build/build_system/pkgconfig.rs | 2 +- libcoap-sys/build/main.rs | 4 +- libcoap-sys/build/metadata.rs | 2 +- 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/libcoap-sys/build/build_system/manual.rs b/libcoap-sys/build/build_system/manual.rs index d2396a55..395d7596 100644 --- a/libcoap-sys/build/build_system/manual.rs +++ b/libcoap-sys/build/build_system/manual.rs @@ -1,29 +1,102 @@ +use std::cell::RefCell; +use std::env; +use std::env::VarError; use std::path::PathBuf; -use anyhow::{bail, Result}; +use anyhow::{Context, Result}; use enumset::EnumSet; use version_compare::Version; use crate::{build_system::BuildSystem, metadata::LibcoapFeature}; +use crate::bindings::{generate_libcoap_bindings, LibcoapDefineParser}; +use crate::metadata::{DtlsBackend, LibcoapDefineInfo}; -pub struct ManualBuildSystem; +pub struct ManualBuildSystem { + out_dir: PathBuf, + include_dirs: Vec, + define_info: Option, +} impl ManualBuildSystem { - pub fn link_with_libcoap(out_dir: PathBuf) -> Result { - bail!("not yet implemented") + pub fn link_with_libcoap(out_dir: PathBuf, requested_dtls_backend: Option) -> Result { + println!("cargo:rerun-if-env-changed=LIBCOAP_RS_INCLUDE_DIRS"); + println!("cargo:rerun-if-env-changed=LIBCOAP_RS_LIB_DIRS"); + println!("cargo:rerun-if-env-changed=LIBCOAP_RS_STATIC"); + println!("cargo:rerun-if-env-changed=LIBCOAP_RS_ADDITIONAL_LIBRARIES"); + + // Parse environment variables. + let include_dirs: Vec = env::var("LIBCOAP_RS_INCLUDE_DIRS").context("LIBCOAP_RS_INCLUDE_DIRS has not been set or is not valid unicode")?.split(":").map(PathBuf::from).collect(); + let lib_dirs: Vec = env::var("LIBCOAP_RS_LIB_DIRS").context("LIBCOAP_RS_LIB_DIRS has not been set or is not valid unicode")?.split(":").map(PathBuf::from).collect(); + let additional_libraries: Vec = match env::var("LIBCOAP_RS_ADDITIONAL_LIBRARIES") { + Ok(v) => v.split(":").map(ToString::to_string).collect(), + Err(VarError::NotPresent) => vec![], + Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_ADDITIONAL_LIBRARIES environment variable.") + }; + let use_static = match env::var("LIBCOAP_RS_STATIC") { + Ok(v) => { if v == "0" || v == "" { false } else { true } }, + Err(VarError::NotPresent) => false, + Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_STATIC environment variable.") + }; + + // Determine name of libcoap library. + let library_name = if let Some(backend) = requested_dtls_backend { + format!("coap-3-{}", backend.library_suffix()) + } else { + "coap-3".to_string() + }; + + // Add given library paths to search path. + for lib_dir in lib_dirs { + println!("cargo:rustc-link-search={}", lib_dir.display()); + } + // Instruct rustc to link with the desired version of libcoap. + println!("cargo:rustc-link-lib={}{}", if use_static { "static=" } else { "" }, library_name); + + // Instruct rustc to link with additional libraries (note that this *must* happen *after* + // linking with libcoap, at least with some linkers). + for additional_library in additional_libraries { + println!("cargo:rustc-link-lib={}", additional_library); + } + + Ok(Self { + out_dir, + include_dirs, + define_info: None, + }) } } impl BuildSystem for ManualBuildSystem { fn detected_features(&self) -> Option> { - todo!() + self.define_info.as_ref().map(|v| v.supported_features) } fn version(&self) -> Option { - todo!() + self.define_info.as_ref().and_then(|i| i.version.as_ref().map(|v| Version::from(v.as_str()))) + .expect("unable to parse version string obtained from coap_defines.h") } fn generate_bindings(&mut self) -> anyhow::Result { - todo!() + let (define_info, define_parser) = LibcoapDefineParser::new(); + let bindings = generate_libcoap_bindings(|builder| { + Ok(builder + .parse_callbacks(Box::new(define_parser)) + // If the pkg-config provided include path coincides with a system include directory, + // setting the "-I{}" command line argument will not do anything, potentially resulting + // in clang using different CoAP headers than provided by pkg-config, e.g., if there + // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. + // Therefore, we use `-isystem` instead. + // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir + .clang_args(self.include_dirs.iter().map(|v| format!("-isystem{}", v.display()))) + ) + })?; + + self.define_info = Some(RefCell::take(&define_info)); + + let out_path = self.out_dir.join("bindings.rs"); + bindings + .write_to_file(&out_path) + .context("unable to write bindings to file")?; + Ok(out_path) } } diff --git a/libcoap-sys/build/build_system/pkgconfig.rs b/libcoap-sys/build/build_system/pkgconfig.rs index ea461f6a..19f53521 100644 --- a/libcoap-sys/build/build_system/pkgconfig.rs +++ b/libcoap-sys/build/build_system/pkgconfig.rs @@ -28,7 +28,7 @@ impl PkgConfigBuildSystem { .env_metadata(true); let library = if let Some(requested_dtls_backend) = requested_dtls_backend { // Use the libcoap version corresponding to the requested DTLS library, if one has been set. - prober.probe(&format!("libcoap-3-{}", requested_dtls_backend.pkg_config_suffix())) + prober.probe(&format!("libcoap-3-{}", requested_dtls_backend.library_suffix())) } else { // Otherwise, use the "default" version. prober.probe("libcoap-3") diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 5739470b..a826ec39 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -129,7 +129,7 @@ fn link_libcoap_explicit( PkgConfigBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend) .map(|v| Box::::from(Box::new(v))) }, - "manual" => ManualBuildSystem::link_with_libcoap(out_dir).map(|v| Box::::from(Box::new(v))), + "manual" => ManualBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend).map(|v| Box::::from(Box::new(v))), v => Err(anyhow!("build system {v} is unknown or unsupported for this target")), } .context(format!( @@ -173,7 +173,7 @@ fn link_libcoap_auto( .map(|v| Box::::from(Box::new(v))) .or_else(|e| { errors.push(("pkgconfig", e)); - ManualBuildSystem::link_with_libcoap(out_dir).map(|v| Box::::from(Box::new(v))) + ManualBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) }) .map_err(|e| { errors.push(("manual", e)); diff --git a/libcoap-sys/build/metadata.rs b/libcoap-sys/build/metadata.rs index 8cd0377e..be920c92 100644 --- a/libcoap-sys/build/metadata.rs +++ b/libcoap-sys/build/metadata.rs @@ -218,7 +218,7 @@ impl Display for DtlsBackend { impl DtlsBackend { /// Returns the suffix that has to be appended to libcoap-3- to find a library /// version linked against the right DTLS library. - pub fn pkg_config_suffix(&self) -> &'static str { + pub fn library_suffix(&self) -> &'static str { // just keeping this here in case we ever need to change this definition to something // different than self.as_str() for some libraries. self.as_str() From 8bd6e93cbf458b964810c3ce7f300373790252e8 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Mon, 20 Jan 2025 15:56:35 +0100 Subject: [PATCH 10/26] fix(test): properly propagate panics produced (in test server setup) --- libcoap/tests/common/dtls.rs | 17 ++++++++++++ libcoap/tests/common/mod.rs | 52 +++++++++++++++++++++++------------- 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/libcoap/tests/common/dtls.rs b/libcoap/tests/common/dtls.rs index 138ab58f..63220103 100644 --- a/libcoap/tests/common/dtls.rs +++ b/libcoap/tests/common/dtls.rs @@ -7,6 +7,8 @@ use libcoap_rs::message::CoapMessageCommon; use libcoap_rs::protocol::{CoapMessageCode, CoapResponseCode}; use libcoap_rs::session::{CoapClientSession, CoapSessionCommon}; use libcoap_rs::CoapContext; +use libcoap_sys::{coap_get_tls_library_version, coap_package_version, coap_tls_library_t}; +use std::ffi::CStr; use std::path::PathBuf; use std::time::Duration; @@ -23,6 +25,21 @@ pub fn dtls_client_server_request_common( ServerPkiRpkCryptoContext<'static>: From>, ClientCryptoContext<'static>: From>, { + let tls_library = match unsafe { *coap_get_tls_library_version() }.type_ { + coap_tls_library_t::COAP_TLS_LIBRARY_NOTLS => "notls", + coap_tls_library_t::COAP_TLS_LIBRARY_TINYDTLS => "tinydtls", + coap_tls_library_t::COAP_TLS_LIBRARY_OPENSSL => "openssl", + coap_tls_library_t::COAP_TLS_LIBRARY_GNUTLS => "gnutls", + coap_tls_library_t::COAP_TLS_LIBRARY_MBEDTLS => "mbedtls", + coap_tls_library_t::COAP_TLS_LIBRARY_WOLFSSL => "wolfssl", + _ => "unknown", + }; + println!( + "Libcoap-Version: {}, DTLS library: {}", + unsafe { CStr::from_ptr(coap_package_version()) }.to_string_lossy(), + tls_library + ); + let server_address = common::get_unused_server_addr(); let client_crypto_ctx = client_ctx_setup(PkiRpkContextBuilder::<'static, KTY, NonCertVerifying>::new(client_key)); let server_handle = common::spawn_test_server(move |mut context: CoapContext| { diff --git a/libcoap/tests/common/mod.rs b/libcoap/tests/common/mod.rs index af109b34..dab19994 100644 --- a/libcoap/tests/common/mod.rs +++ b/libcoap/tests/common/mod.rs @@ -12,15 +12,15 @@ pub mod dtls; use std::net::{SocketAddr, UdpSocket}; use std::rc::Rc; -use std::sync::{Arc, Condvar, Mutex}; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Condvar, Mutex}; use std::thread::JoinHandle; use std::time::Duration; -use libcoap_rs::{CoapContext, CoapRequestHandler, CoapResource}; use libcoap_rs::message::{CoapMessageCommon, CoapRequest, CoapResponse}; use libcoap_rs::protocol::{CoapMessageCode, CoapMessageType, CoapRequestCode, CoapResponseCode}; use libcoap_rs::session::CoapSessionCommon; +use libcoap_rs::{CoapContext, CoapRequestHandler, CoapResource}; use libcoap_sys::{coap_dtls_set_log_level, coap_log_t, coap_set_log_level}; pub(crate) fn get_unused_server_addr() -> SocketAddr { @@ -51,25 +51,39 @@ pub(crate) fn spawn_test_server) -> CoapContext<' let ready_condition = Arc::new((Mutex::new(false), Condvar::new())); let ready_condition2 = Arc::clone(&ready_condition); - let server_handle = std::thread::spawn(move || { - let (ready_var, ready_cond) = &*ready_condition2; - run_test_server(|context| { - let context = context_configurator(context); - let mut ready_var = ready_var.lock().expect("ready condition mutex is poisoned"); - *ready_var = true; - ready_cond.notify_all(); - context - }); - }); + let server_handle = std::thread::Builder::new() + .name(String::from("test server")) + .spawn(move || { + let (ready_var, ready_cond) = &*ready_condition2; + run_test_server(|context| { + let context = context_configurator(context); + let mut ready_var = ready_var.lock().expect("ready condition mutex is poisoned"); + *ready_var = true; + ready_cond.notify_all(); + context + }); + }) + .expect("unable to spawn test server thread"); let (ready_var, ready_cond) = &*ready_condition; - drop( - ready_cond - .wait_while(ready_var.lock().expect("ready condition mutex is poisoned"), |ready| { - !*ready - }) - .expect("ready condition mutex is poisoned"), - ); + { + let (_guard, timeout_result) = ready_cond + .wait_timeout_while( + ready_var.lock().expect("ready condition mutex is poisoned"), + Duration::from_secs(10), + |ready| !*ready, + ) + .expect("ready condition mutex is poisoned"); + if timeout_result.timed_out() && server_handle.is_finished() { + if let Err(e) = server_handle.join() { + std::panic::resume_unwind(e); + } else { + panic!("Test server thread is dead and has not reported readiness after 10 seconds, but has also not panicked.") + } + } else if timeout_result.timed_out() { + panic!("Test server thread has not reported readiness after 10 seconds, but has also not died (deadlock?).") + } + } server_handle } From 1252c3b411295bdd592bb73e22536c81df1ad8e8 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Mon, 20 Jan 2025 17:01:08 +0100 Subject: [PATCH 11/26] chore(lib): remove remaining compatibility code for libcoap < 4.3.5 Considering that we already require libcoap 4.3.5 for the new build script of libcoap-sys, there is no more need to keep compatibility in libcoap-rs either. Also, libcoap 4.3.5 has by now been released for all supported platforms (including the ESP-IDF). --- libcoap/Cargo.toml | 8 ++++--- libcoap/build.rs | 36 ++++++++++------------------ libcoap/src/context.rs | 4 ++-- libcoap/src/crypto/psk/client.rs | 6 +++-- libcoap/src/crypto/psk/server.rs | 4 +++- libcoap/src/lib.rs | 2 +- libcoap/src/session/client.rs | 10 ++++---- libcoap/src/types.rs | 40 ++------------------------------ 8 files changed, 34 insertions(+), 76 deletions(-) diff --git a/libcoap/Cargo.toml b/libcoap/Cargo.toml index 4d24cb17..bb098208 100644 --- a/libcoap/Cargo.toml +++ b/libcoap/Cargo.toml @@ -21,9 +21,11 @@ rust-version = "1.81.0" [features] default = ["dtls-psk", "tcp", "vendored", "libcoap-sys/default"] -dtls-psk = ["libcoap-sys/dtls", "libcoap-sys/dtls-psk"] -dtls-pki = ["libcoap-sys/dtls", "libcoap-sys/dtls-pki"] -dtls-rpk = ["libcoap-sys/dtls", "libcoap-sys/dtls-rpk"] +dtls = ["libcoap-sys/dtls"] +dtls-psk = ["dtls", "libcoap-sys/dtls-psk"] +dtls-pki = ["dtls", "libcoap-sys/dtls-pki"] +dtls-rpk = ["dtls", "libcoap-sys/dtls-rpk"] +dtls-cid = ["dtls-psk", "libcoap-sys/dtls-cid"] tcp = ["libcoap-sys/tcp"] tls = ["libcoap-sys/tls"] rand = ["dep:rand", "dep:rand_core"] diff --git a/libcoap/build.rs b/libcoap/build.rs index 22c63928..895de427 100644 --- a/libcoap/build.rs +++ b/libcoap/build.rs @@ -1,32 +1,20 @@ // SPDX-License-Identifier: BSD-2-CLAUSE -use version_compare::{Cmp, Version}; +//use version_compare::Cmp; +use version_compare::Version; fn main() { - println!("cargo::rustc-check-cfg=cfg(dtls_ec_jpake_support)"); - println!("cargo::rustc-check-cfg=cfg(dtls_cid_support)"); - println!("cargo::rustc-check-cfg=cfg(coap_uri_buf_unused)"); - println!("cargo::rustc-check-cfg=cfg(dtls)"); if let Ok(libcoap_version) = std::env::var("DEP_COAP_3_LIBCOAP_VERSION") { let version = Version::from(libcoap_version.as_ref()).expect("invalid libcoap version"); - // libcoap >= 4.3.5rc2 no longer uses the buf and buflen parameters in - // coap_uri_into_options(), so we can optimize them out and save some memory. - match version.compare(Version::from("4.3.5rc2").unwrap()) { - Cmp::Gt | Cmp::Eq => { - println!("cargo:rustc-cfg=coap_uri_buf_unused"); - }, - _ => {}, - } - // libcoap >= 4.3.5rc3 supports DTLS EC JPAKE and connection ID extensions, which adds - // additional fields to some DTLS configuration structs. - match version.compare(Version::from("4.3.5rc3").unwrap()) { - Cmp::Gt | Cmp::Eq => { - println!("cargo:rustc-cfg=dtls_ec_jpake_support"); - println!("cargo:rustc-cfg=dtls_cid_support"); - }, - _ => {}, - } + println!("cargo:rustc-cfg=libcoap_version=\"{}\"", version.as_str()); + + // Uncomment and adjust this in order to create version-dependent cfg-flags. + // Note: In most cases, you probably want to check for the presence of a given feature instead. + // Matching based on the libcoap version usually only makes sense in order to either + // enable optional optimizations possible with newer versions, or to add struct fields + // that were added into existing structs without breaking backward compatibility. + /*if version > Version::from("4.3.5").unwrap() { + println!("cargo:rustc-cfg=[INSERT FLAG NAME HERE]") + }*/ } - #[cfg(any(feature = "dtls-pki", feature = "dtls-rpk", feature = "dtls-psk"))] - println!("cargo:rustc-cfg=dtls") } diff --git a/libcoap/src/context.rs b/libcoap/src/context.rs index c8be5ec1..0f35f390 100644 --- a/libcoap/src/context.rs +++ b/libcoap/src/context.rs @@ -11,7 +11,7 @@ #[cfg(feature = "dtls-pki")] use std::ffi::CString; -#[cfg(dtls)] +#[cfg(feature = "dtls")] use std::ptr::NonNull; use std::{any::Any, ffi::c_void, fmt::Debug, net::SocketAddr, ops::Sub, sync::Once, time::Duration}; #[cfg(all(feature = "dtls-pki", unix))] @@ -382,7 +382,7 @@ impl CoapContext<'_> { /// /// Note that in order to actually connect to DTLS clients, you need to set a crypto provider /// using [CoapContext::set_psk_context] and/or [CoapContext::set_pki_rpk_context]. - #[cfg(dtls)] + #[cfg(feature = "dtls")] pub fn add_endpoint_dtls(&mut self, addr: SocketAddr) -> Result<(), EndpointCreationError> { self.add_endpoint(addr, coap_proto_t::COAP_PROTO_DTLS) } diff --git a/libcoap/src/crypto/psk/client.rs b/libcoap/src/crypto/psk/client.rs index 4bfa9a19..01ba8ed5 100644 --- a/libcoap/src/crypto/psk/client.rs +++ b/libcoap/src/crypto/psk/client.rs @@ -94,10 +94,12 @@ impl ClientPskContextBuilder<'_> { /// Enables or disables support for EC JPAKE ([RFC 8236](https://datatracker.ietf.org/doc/html/rfc8236)) /// key exchanges in (D)TLS. /// + /// Note: At the time of writing (based on libcoap 4.3.5), this is only supported on MbedTLS, + /// enabling EC JPAKE on other DTLS backends has no effect. + /// /// # Implementation details (informative, not covered by semver guarantees) /// /// Equivalent to setting `ec_jpake` in the underlying [`coap_dtls_cpsk_t`] structure. - #[cfg(dtls_ec_jpake_support)] pub fn ec_jpake(mut self, ec_jpake: bool) -> Self { self.ctx.raw_cfg.ec_jpake = ec_jpake.into(); self @@ -108,7 +110,7 @@ impl ClientPskContextBuilder<'_> { /// # Implementation details (informative, not covered by semver guarantees) /// /// Equivalent to setting `use_cid` in the underlying [`coap_dtls_cpsk_t`] structure. - #[cfg(dtls_cid_support)] + #[cfg(feature = "dtls-cid")] pub fn use_cid(mut self, use_cid: bool) -> Self { self.ctx.raw_cfg.use_cid = use_cid.into(); self diff --git a/libcoap/src/crypto/psk/server.rs b/libcoap/src/crypto/psk/server.rs index 06656a56..357a7f20 100644 --- a/libcoap/src/crypto/psk/server.rs +++ b/libcoap/src/crypto/psk/server.rs @@ -104,10 +104,12 @@ impl ServerPskContextBuilder<'_> { /// Enables or disables support for EC JPAKE ([RFC 8236](https://datatracker.ietf.org/doc/html/rfc8236)) /// key exchanges in (D)TLS. /// + /// Note: At the time of writing (based on libcoap 4.3.5), this is only supported on MbedTLS, + /// enabling EC JPAKE on other DTLS backends has no effect. + /// /// # Implementation details (informative, not covered by semver guarantees) /// /// Equivalent to setting `ec_jpake` in the underlying [`coap_dtls_spsk_t`] structure. - #[cfg(dtls_ec_jpake_support)] pub fn ec_jpake(mut self, ec_jpake: bool) -> Self { self.ctx.raw_cfg.ec_jpake = ec_jpake.into(); self diff --git a/libcoap/src/lib.rs b/libcoap/src/lib.rs index 9dcda7ab..ed115a2f 100644 --- a/libcoap/src/lib.rs +++ b/libcoap/src/lib.rs @@ -207,7 +207,7 @@ pub use event::CoapEventHandler; pub use resource::{CoapRequestHandler, CoapResource}; mod context; -#[cfg(dtls)] +#[cfg(feature = "dtls")] pub mod crypto; pub mod error; mod event; diff --git a/libcoap/src/session/client.rs b/libcoap/src/session/client.rs index 47f241c2..4e1660fb 100644 --- a/libcoap/src/session/client.rs +++ b/libcoap/src/session/client.rs @@ -22,13 +22,13 @@ use crate::mem::{CoapFfiRcCell, DropInnerExclusively}; use crate::prng::coap_prng_try_fill; use crate::{context::CoapContext, error::SessionCreationError, types::CoapAddress}; -#[cfg(dtls)] +#[cfg(feature = "dtls")] use crate::crypto::ClientCryptoContext; #[derive(Debug)] struct CoapClientSessionInner<'a> { inner: CoapSessionInner<'a>, - #[cfg(dtls)] + #[cfg(feature = "dtls")] // This field is actually referred to be libcoap, so it isn't actually unused. #[allow(unused)] crypto_ctx: Option>, @@ -53,7 +53,7 @@ impl<'a> CoapClientSessionInner<'a> { let inner_session = CoapFfiRcCell::new(CoapClientSessionInner { inner: CoapSessionInner::new(raw_session), - #[cfg(dtls)] + #[cfg(feature = "dtls")] crypto_ctx: None, }); @@ -70,7 +70,7 @@ impl<'a> CoapClientSessionInner<'a> { /// # Safety /// The provided pointer for `raw_session` must be valid and point to the newly constructed raw /// session. - #[cfg(dtls)] + #[cfg(feature = "dtls")] unsafe fn new_with_crypto_ctx( raw_session: *mut coap_session_t, crypto_ctx: ClientCryptoContext<'a>, @@ -100,7 +100,7 @@ impl CoapClientSession<'_> { /// # Errors /// Will return a [SessionCreationError] if libcoap was unable to create a session (most likely /// because it was not possible to bind to a port). - #[cfg(dtls)] + #[cfg(feature = "dtls")] pub fn connect_dtls<'a>( ctx: &mut CoapContext<'a>, addr: SocketAddr, diff --git a/libcoap/src/types.rs b/libcoap/src/types.rs index 9967c799..ad7703e3 100644 --- a/libcoap/src/types.rs +++ b/libcoap/src/types.rs @@ -31,7 +31,7 @@ use libcoap_sys::coap_uri_scheme_t::{COAP_URI_SCHEME_COAPS_WS, COAP_URI_SCHEME_C use libcoap_sys::{ coap_address_t, coap_delete_optlist, coap_mid_t, coap_proto_t, coap_proto_t::{COAP_PROTO_DTLS, COAP_PROTO_NONE, COAP_PROTO_TCP, COAP_PROTO_TLS, COAP_PROTO_UDP}, - coap_split_proxy_uri, coap_split_uri, coap_str_const_t, coap_string_equal, coap_uri_into_options, + coap_split_proxy_uri, coap_split_uri, coap_str_const_t, coap_string_equal, coap_uri_into_optlist, coap_uri_scheme_t, coap_uri_scheme_t::{ COAP_URI_SCHEME_COAP, COAP_URI_SCHEME_COAPS, COAP_URI_SCHEME_COAPS_TCP, COAP_URI_SCHEME_COAP_TCP, @@ -589,48 +589,12 @@ impl CoapUri { // TODO this is a lot of copying around, however, fixing that would require an entire // rewrite of the option handling code, so it's better kept for a separate PR. - // Set size of temporary buffer for option storage. - // TODO remove when updating minimum libcoap version to 4.3.5, as this buffer is no longer - // used there. - #[cfg(coap_uri_buf_unused)] - let mut buf = []; - #[cfg(not(coap_uri_buf_unused))] - let mut buf = { - let buf_len = - // Length of UriHost option (length of host + max. 5 bytes of option header) - self.host().map(|v| v.len()+5).unwrap_or(0) - // Length of UriPort option (max. 2 bytes for the port number + max. 5 bytes of - // option header) - + self.port().map(|v| 7).unwrap_or(0) - // Length of path segment - + self.path().map(|v| v.len()).unwrap_or(0) - // Length of option headers for path segments. - // Each path segment has its own header, which can be up to 5 bytes in size. - + self.path().map(|v| (v.iter().filter(|c| **c as char == '/').count()+1)*5).unwrap_or(0) - // Length of query segment - + self.query().map(|v| v.len()).unwrap_or(0) - // Length of option headers for query segments. - // Each query segment has its own header, which can be up to 5 bytes in size. - + self.query().map(|v| (v.iter().filter(|c| **c as char == '?' || **c as char == '&').count()+1)*5).unwrap_or(0); - vec![0u8; buf_len] - }; - let mut optlist = std::ptr::null_mut(); // SAFETY: self.raw_uri is always valid after construction. The destination may be a null // pointer, optlist may be a null pointer at the start (it will be set to a valid // pointer by this call). Buf and create_port_host_opt are set according to the // libcoap documentation. - if unsafe { - coap_uri_into_options( - &self.raw_uri, - std::ptr::null(), - &mut optlist, - 1, - buf.as_mut_ptr(), - buf.len(), - ) - } < 0 - { + if unsafe { coap_uri_into_optlist(&self.raw_uri, std::ptr::null(), &mut optlist, 1) } < 0 { // We have already parsed this URI. If converting it into options fails, something went // terribly wrong. panic!("could not convert valid coap URI into options"); From 157ee9b35018fda2fae0ef360dca086af8de1da5 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Mon, 20 Jan 2025 18:13:49 +0100 Subject: [PATCH 12/26] fix(sys|test): provide version+DTLS lib to downstream crates, update libcoap-rs tests --- libcoap-sys/build/build_system/esp_idf.rs | 44 ++++-- libcoap-sys/build/build_system/manual.rs | 43 ++++-- libcoap-sys/build/build_system/mod.rs | 4 +- libcoap-sys/build/build_system/pkgconfig.rs | 14 +- libcoap-sys/build/build_system/vendored.rs | 145 ++++++++++++------- libcoap-sys/build/main.rs | 24 +-- libcoap-sys/build/metadata.rs | 6 +- libcoap/Cargo.toml | 1 + libcoap/build.rs | 76 ++++++++-- libcoap/tests/dtls_pki_client_server_test.rs | 6 +- 10 files changed, 259 insertions(+), 104 deletions(-) diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs index c6c2cd26..cf4aa3bb 100644 --- a/libcoap-sys/build/build_system/esp_idf.rs +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -17,7 +17,11 @@ pub struct EspIdfBuildSystem { } impl EspIdfBuildSystem { - pub fn new(out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option) -> Result { + pub fn new( + out_dir: PathBuf, + requested_features: EnumSet, + requested_dtls_backend: Option, + ) -> Result { embuild::espidf::sysenv::output(); let esp_idf_bindings_file = env::var_os("DEP_ESP_IDF_ROOT") .map(PathBuf::from) @@ -33,7 +37,7 @@ impl EspIdfBuildSystem { Ok(Self { out_dir, esp_idf_bindings_file, - requested_features + requested_features, }) } } @@ -47,7 +51,11 @@ impl BuildSystem for EspIdfBuildSystem { // However, do warn the user if features are requested that cannot be checked this way, but // would be checkable if the defines-based checker was used. - let uncheckable_features: EnumSet = self.requested_features.iter().filter(|v| v.define_name().is_some() && v.sdkconfig_flag_name().is_none()).collect(); + let uncheckable_features: EnumSet = self + .requested_features + .iter() + .filter(|v| v.define_name().is_some() && v.sdkconfig_flag_name().is_none()) + .collect(); if !uncheckable_features.is_empty() { println!("cargo:warning=When building for ESP-IDF, the availability of the following requested features that usually can be checked during compile time can only be checked during runtime instead: {}", uncheckable_features.iter().map(|v| v.as_str()).collect::>().join(", ")) } @@ -55,6 +63,14 @@ impl BuildSystem for EspIdfBuildSystem { Some(self.requested_features) } + fn detected_dtls_backend(&self) -> Option { + // If DTLS is a requested feature, we check during compile time whether MbedTLS is + // supposed to be enabled. + self.requested_features + .contains(LibcoapFeature::Dtls) + .then_some(DtlsBackend::MbedTls) + } + fn version(&self) -> Option { None } @@ -106,18 +122,28 @@ impl BuildSystem for EspIdfBuildSystem { // Add check whether the libcoap component is enabled in ESP-IDF to generated bindings file. println!("cargo::rustc-check-cfg=cfg(esp_idf_comp_espressif__coap_enabled)"); - writeln!(&mut libcoap_bindings_file, "#[cfg(not(esp_idf_comp_espressif__coap_enabled))]") - .context("unable to write to bindings file")?; + writeln!( + &mut libcoap_bindings_file, + "#[cfg(not(esp_idf_comp_espressif__coap_enabled))]" + ) + .context("unable to write to bindings file")?; writeln!(&mut libcoap_bindings_file, "compile_error!(\"You are building libcoap-sys for an ESP-IDF target, but have not added the espressif/coap remote component (see the libcoap-sys documentation for more information)\");") .context("unable to write to bindings file")?; - - for (feature_name, feature_flag) in self.requested_features.iter().filter_map(|v| v.sdkconfig_flag_name().map(|flag| (v.as_str(), flag))) { - + for (feature_name, feature_flag) in self + .requested_features + .iter() + .filter_map(|v| v.sdkconfig_flag_name().map(|flag| (v.as_str(), flag))) + { // For some reason, embuild adds expected cfg flags for some, but not all feature-related sdkconfig flags, causing warnings if we don't do this. println!("cargo::rustc-check-cfg=cfg(esp_idf_{})", feature_flag.to_lowercase()); - writeln!(&mut libcoap_bindings_file, "#[cfg(not(esp_idf_{}))]", feature_flag.to_lowercase()).context("unable to write to bindings file")?; + writeln!( + &mut libcoap_bindings_file, + "#[cfg(not(esp_idf_{}))]", + feature_flag.to_lowercase() + ) + .context("unable to write to bindings file")?; writeln!(&mut libcoap_bindings_file, "compile_error!(\"Requested feature \\\"{feature_name}\\\" is not enabled in ESP-IDF sdkconfig.defaults (set `CONFIG_{feature_flag}=y` to fix this)\");") .context("unable to write to bindings file")?; } diff --git a/libcoap-sys/build/build_system/manual.rs b/libcoap-sys/build/build_system/manual.rs index 395d7596..b531e0a9 100644 --- a/libcoap-sys/build/build_system/manual.rs +++ b/libcoap-sys/build/build_system/manual.rs @@ -7,9 +7,9 @@ use anyhow::{Context, Result}; use enumset::EnumSet; use version_compare::Version; -use crate::{build_system::BuildSystem, metadata::LibcoapFeature}; use crate::bindings::{generate_libcoap_bindings, LibcoapDefineParser}; use crate::metadata::{DtlsBackend, LibcoapDefineInfo}; +use crate::{build_system::BuildSystem, metadata::LibcoapFeature}; pub struct ManualBuildSystem { out_dir: PathBuf, @@ -25,17 +25,31 @@ impl ManualBuildSystem { println!("cargo:rerun-if-env-changed=LIBCOAP_RS_ADDITIONAL_LIBRARIES"); // Parse environment variables. - let include_dirs: Vec = env::var("LIBCOAP_RS_INCLUDE_DIRS").context("LIBCOAP_RS_INCLUDE_DIRS has not been set or is not valid unicode")?.split(":").map(PathBuf::from).collect(); - let lib_dirs: Vec = env::var("LIBCOAP_RS_LIB_DIRS").context("LIBCOAP_RS_LIB_DIRS has not been set or is not valid unicode")?.split(":").map(PathBuf::from).collect(); + let include_dirs: Vec = env::var("LIBCOAP_RS_INCLUDE_DIRS") + .context("LIBCOAP_RS_INCLUDE_DIRS has not been set or is not valid unicode")? + .split(":") + .map(PathBuf::from) + .collect(); + let lib_dirs: Vec = env::var("LIBCOAP_RS_LIB_DIRS") + .context("LIBCOAP_RS_LIB_DIRS has not been set or is not valid unicode")? + .split(":") + .map(PathBuf::from) + .collect(); let additional_libraries: Vec = match env::var("LIBCOAP_RS_ADDITIONAL_LIBRARIES") { Ok(v) => v.split(":").map(ToString::to_string).collect(), Err(VarError::NotPresent) => vec![], - Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_ADDITIONAL_LIBRARIES environment variable.") + Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_ADDITIONAL_LIBRARIES environment variable."), }; let use_static = match env::var("LIBCOAP_RS_STATIC") { - Ok(v) => { if v == "0" || v == "" { false } else { true } }, + Ok(v) => { + if v == "0" || v == "" { + false + } else { + true + } + }, Err(VarError::NotPresent) => false, - Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_STATIC environment variable.") + Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_STATIC environment variable."), }; // Determine name of libcoap library. @@ -50,7 +64,11 @@ impl ManualBuildSystem { println!("cargo:rustc-link-search={}", lib_dir.display()); } // Instruct rustc to link with the desired version of libcoap. - println!("cargo:rustc-link-lib={}{}", if use_static { "static=" } else { "" }, library_name); + println!( + "cargo:rustc-link-lib={}{}", + if use_static { "static=" } else { "" }, + library_name + ); // Instruct rustc to link with additional libraries (note that this *must* happen *after* // linking with libcoap, at least with some linkers). @@ -71,8 +89,14 @@ impl BuildSystem for ManualBuildSystem { self.define_info.as_ref().map(|v| v.supported_features) } + fn detected_dtls_backend(&self) -> Option { + self.define_info.as_ref().and_then(|v| v.dtls_backend) + } + fn version(&self) -> Option { - self.define_info.as_ref().and_then(|i| i.version.as_ref().map(|v| Version::from(v.as_str()))) + self.define_info + .as_ref() + .and_then(|i| i.version.as_ref().map(|v| Version::from(v.as_str()))) .expect("unable to parse version string obtained from coap_defines.h") } @@ -87,8 +111,7 @@ impl BuildSystem for ManualBuildSystem { // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. // Therefore, we use `-isystem` instead. // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir - .clang_args(self.include_dirs.iter().map(|v| format!("-isystem{}", v.display()))) - ) + .clang_args(self.include_dirs.iter().map(|v| format!("-isystem{}", v.display())))) })?; self.define_info = Some(RefCell::take(&define_info)); diff --git a/libcoap-sys/build/build_system/mod.rs b/libcoap-sys/build/build_system/mod.rs index db7c386a..3ae93d1e 100644 --- a/libcoap-sys/build/build_system/mod.rs +++ b/libcoap-sys/build/build_system/mod.rs @@ -4,7 +4,7 @@ use anyhow::Result; use enumset::EnumSet; use version_compare::Version; -use crate::metadata::LibcoapFeature; +use crate::metadata::{DtlsBackend, LibcoapFeature}; pub mod esp_idf; pub mod manual; @@ -14,6 +14,8 @@ pub mod vendored; pub trait BuildSystem { fn detected_features(&self) -> Option>; + fn detected_dtls_backend(&self) -> Option; + fn version(&self) -> Option; fn generate_bindings(&mut self) -> Result; diff --git a/libcoap-sys/build/build_system/pkgconfig.rs b/libcoap-sys/build/build_system/pkgconfig.rs index 19f53521..1b0fba6e 100644 --- a/libcoap-sys/build/build_system/pkgconfig.rs +++ b/libcoap-sys/build/build_system/pkgconfig.rs @@ -49,6 +49,10 @@ impl BuildSystem for PkgConfigBuildSystem { self.define_info.as_ref().map(|v| v.supported_features) } + fn detected_dtls_backend(&self) -> Option { + self.define_info.as_ref().and_then(|v| v.dtls_backend) + } + fn version(&self) -> Option { Version::from(&self.library.version) .map(Some) @@ -66,15 +70,19 @@ impl BuildSystem for PkgConfigBuildSystem { // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. // Therefore, we use `-isystem` instead. // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir - .clang_args(self.library.include_paths.iter().map(|v| format!("-isystem{}", v.display()))) - ) + .clang_args( + self.library + .include_paths + .iter() + .map(|v| format!("-isystem{}", v.display())), + )) })?; self.define_info = Some(RefCell::take(&define_info)); if let Some(version) = &self.define_info.as_ref().unwrap().version { if Version::from(&self.library.version) != Version::from(version) { - return Err(anyhow!("The library version indicated by pkg-config does not match the one indicated by the headers. Are the include paths misconfigured?")) + return Err(anyhow!("The library version indicated by pkg-config does not match the one indicated by the headers. Are the include paths misconfigured?")); } } diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index ee6a1ca8..2fc8ee18 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -1,15 +1,15 @@ +use crate::bindings::{generate_libcoap_bindings, LibcoapDefineParser}; +use crate::build_system::BuildSystem; +use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}; +use anyhow::{anyhow, ensure, Context, Result}; +use enumset::EnumSet; use std::cell::RefCell; use std::env; use std::env::VarError; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::process::Command; -use anyhow::{anyhow, ensure, Context, Result}; -use enumset::EnumSet; use version_compare::Version; -use crate::bindings::{generate_libcoap_bindings, LibcoapDefineParser}; -use crate::build_system::BuildSystem; -use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}; const VENDORED_LIBCOAP_VERSION: &str = "4.3.5"; @@ -22,7 +22,11 @@ pub struct VendoredBuildSystem { impl VendoredBuildSystem { /// Obtain some built version of libcoap and set the appropriate linker flags to link with it /// (and its dependencies, if any). - pub fn build_libcoap(out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option) -> Result { + pub fn build_libcoap( + out_dir: PathBuf, + requested_features: EnumSet, + requested_dtls_backend: Option, + ) -> Result { println!("cargo:rerun-if-changed=src/libcoap"); let libcoap_src_dir = out_dir.join("libcoap"); @@ -47,12 +51,16 @@ impl VendoredBuildSystem { &out_dir, ©_options, ) - .context("unable to prepare libcoap build source directory")?; + .context("unable to prepare libcoap build source directory")?; env::set_current_dir(&libcoap_src_dir).expect("unable to change to libcoap build dir"); - ensure!(Command::new(libcoap_src_dir.join("autogen.sh")) - .status() - .context("unable to execute autogen.sh")?.success(), "autogen.sh returned an error code"); + ensure!( + Command::new(libcoap_src_dir.join("autogen.sh")) + .status() + .context("unable to execute autogen.sh")? + .success(), + "autogen.sh returned an error code" + ); let mut build_config = autotools::Config::new(&libcoap_src_dir); @@ -92,7 +100,6 @@ impl VendoredBuildSystem { let pkg_config_path_bak = env::var_os("PKG_CONFIG_PATH"); let link_using_pkgconfig = if requested_features.contains(LibcoapFeature::Dtls) { - // Check if we have any DTLS libraries already added as a Rust dependency. // For each one, set the appropriate PKG_CONFIG_PATHs, CFLAGS and/or LIBS to use them // instead of system versions if they are going to be used. @@ -136,16 +143,33 @@ impl VendoredBuildSystem { additional_pkg_config_paths.push(libcoap_build_prefix.join("lib").join("pkgconfig")); let pkg_config_path = match env::var("PKG_CONFIG_PATH") { - Ok(v) => { format!("{}:{}", additional_pkg_config_paths.iter().map(|v| v.to_str().ok_or(anyhow!("unable to convert PKG_CONFIG_PATH value to UTF-8"))).collect::>>()?.join(":"), v) } - Err(VarError::NotPresent) => { additional_pkg_config_paths.iter().map(|v| v.to_str().ok_or(anyhow!("unable to convert PKG_CONFIG_PATH value to UTF-8"))).collect::>>()?.join(":") }, - Err(e) => Err(e).context("PKG_CONFIG_PATH is not a valid UTF-8 string")? + Ok(v) => { + format!( + "{}:{}", + additional_pkg_config_paths + .iter() + .map(|v| v + .to_str() + .ok_or(anyhow!("unable to convert PKG_CONFIG_PATH value to UTF-8"))) + .collect::>>()? + .join(":"), + v + ) + }, + Err(VarError::NotPresent) => additional_pkg_config_paths + .iter() + .map(|v| { + v.to_str() + .ok_or(anyhow!("unable to convert PKG_CONFIG_PATH value to UTF-8")) + }) + .collect::>>()? + .join(":"), + Err(e) => Err(e).context("PKG_CONFIG_PATH is not a valid UTF-8 string")?, }; build_config.env("PKG_CONFIG_PATH", &pkg_config_path); // SAFETY: We are single-threaded here. - unsafe { - env::set_var("PKG_CONFIG_PATH", pkg_config_path) - } + unsafe { env::set_var("PKG_CONFIG_PATH", pkg_config_path) } // Choose a DTLS backend. let selected_dtls_backend = if let Some(requested_dtls_backend) = requested_dtls_backend { @@ -189,9 +213,7 @@ impl VendoredBuildSystem { let library = pkg_config::Config::new().statik(true).exactly_version(VENDORED_LIBCOAP_VERSION).probe("libcoap-3").context("unable to link against build version of libcoap using pkg-config (which is necessary if you're not using a Rust dependency to link the DTLS library)")?; // SAFETY: We are still single-threaded here. - unsafe { - env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or(OsString::new())) - } + unsafe { env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or(OsString::new())) } Ok(Self { out_dir, define_info: None, @@ -199,10 +221,14 @@ impl VendoredBuildSystem { }) } else { // SAFETY: We are still single-threaded here. - unsafe { - env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or(OsString::new())) - } - println!("cargo:rustc-link-search={}", libcoap_build_prefix.join("lib").to_str().context("unable to convert OUT_DIR to a valid UTF-8 string.")?); + unsafe { env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or(OsString::new())) } + println!( + "cargo:rustc-link-search={}", + libcoap_build_prefix + .join("lib") + .to_str() + .context("unable to convert OUT_DIR to a valid UTF-8 string.")? + ); println!("cargo:rustc-link-lib=static=coap-3"); Ok(Self { out_dir, @@ -222,33 +248,37 @@ impl VendoredBuildSystem { println!("cargo:warning=Note that attempting to link more than one version of the same library at once may cause unexpected issues and/or cryptic compilation errors, especially if both versions are statically linked."); Ok((None, false)) } else { - let tinydtls_include = env::var_os("DEP_TINYDTLS_INCLUDE").expect("tinydtls-sys dependency has been added, but DEP_TINYDTLS_INCLUDE has not been set"); - let tinydtls_libs = env::var_os("DEP_TINYDTLS_LIBS").expect("tinydtls-sys dependency has been added, but DEP_TINYDTLS_LIBS has not been set"); - build_config = build_config.env("TinyDTLS_CFLAGS", - format!( - "-I{} -I{}", - tinydtls_include - .to_str() - .context("DEP_TINYDTLS_INCLUDE path is not a valid UTF-8 string")?, - Path::new(&tinydtls_include) - .join("tinydtls") - .to_str() - .context("DEP_TINYDTLS_INCLUDE path is not a valid UTF-8 string")? - )); + let tinydtls_include = env::var_os("DEP_TINYDTLS_INCLUDE") + .expect("tinydtls-sys dependency has been added, but DEP_TINYDTLS_INCLUDE has not been set"); + let tinydtls_libs = env::var_os("DEP_TINYDTLS_LIBS") + .expect("tinydtls-sys dependency has been added, but DEP_TINYDTLS_LIBS has not been set"); + build_config = build_config.env( + "TinyDTLS_CFLAGS", + format!( + "-I{} -I{}", + tinydtls_include + .to_str() + .context("DEP_TINYDTLS_INCLUDE path is not a valid UTF-8 string")?, + Path::new(&tinydtls_include) + .join("tinydtls") + .to_str() + .context("DEP_TINYDTLS_INCLUDE path is not a valid UTF-8 string")? + ), + ); // Need to set TinyDTLS_LIBS explicitly to force static linking (TinyDTLS also builds a shared version of the library). build_config = build_config.env( "TinyDTLS_LIBS", format!( "-L{} -l:libtinydtls.a", - tinydtls_libs.to_str().context("DEP_TINYDTLS_LIBS path is not a valid UTF-8 string")? - )); + tinydtls_libs + .to_str() + .context("DEP_TINYDTLS_LIBS path is not a valid UTF-8 string")? + ), + ); // Add TinyDTLS's pkg-config directory to the path for version checking. - Ok(( - Some(PathBuf::from(tinydtls_libs) - .join("lib") - .join("pkgconfig")), true)) + Ok((Some(PathBuf::from(tinydtls_libs).join("lib").join("pkgconfig")), true)) } } @@ -262,12 +292,12 @@ impl VendoredBuildSystem { println!("cargo:warning=Note that attempting to link more than one version of the same library at once may cause unexpected issues and/or cryptic compilation errors, especially if both versions are statically linked."); Ok((None, false)) } else { - let openssl_include = env::var_os("DEP_OPENSSL_INCLUDE").expect("openssl-sys dependency has been added, but DEP_OPENSSL_INCLUDE has not been set"); - let openssl_libs = - Path::new(openssl_include.as_os_str()) - .parent() - .context("DEP_OPENSSL_INCLUDE has no parent directory")? - .join("lib"); + let openssl_include = env::var_os("DEP_OPENSSL_INCLUDE") + .expect("openssl-sys dependency has been added, but DEP_OPENSSL_INCLUDE has not been set"); + let openssl_libs = Path::new(openssl_include.as_os_str()) + .parent() + .context("DEP_OPENSSL_INCLUDE has no parent directory")? + .join("lib"); // Just add the OpenSSL directory to the PKG_CONFIG_PATH, that way libcoap will find it. Ok((Some(openssl_libs.join("pkgconfig")), true)) @@ -275,7 +305,10 @@ impl VendoredBuildSystem { } #[cfg(feature = "dtls-mbedtls-sys")] - fn configure_mbedtls_sys(out_dir: &Path, mut build_config: &mut autotools::Config) -> Result<(Option, bool)> { + fn configure_mbedtls_sys( + out_dir: &Path, + mut build_config: &mut autotools::Config, + ) -> Result<(Option, bool)> { if env::var_os("MbedTLS_CFLAGS").is_some() || env::var_os("MbedTLS_LIBS").is_some() { // Do not use tinydtls-sys if the user manually set either the corresponding LIBS or // CFLAGS variable. @@ -284,7 +317,8 @@ impl VendoredBuildSystem { println!("cargo:warning=Note that attempting to link more than one version of the same library at once may cause unexpected issues and/or cryptic compilation errors, especially if both versions are statically linked."); Ok((None, false)) } else { - let mbedtls_include = env::var_os("DEP_MBEDTLS_INCLUDE").expect("mbedtls-sys dependency has been added, but DEP_MBEDTLS_INCLUDE has not been set"); + let mbedtls_include = env::var_os("DEP_MBEDTLS_INCLUDE") + .expect("mbedtls-sys dependency has been added, but DEP_MBEDTLS_INCLUDE has not been set"); // Can't use pkg-config here, as pkg-config was only added to MbedTLS recently. @@ -341,6 +375,10 @@ impl BuildSystem for VendoredBuildSystem { self.define_info.as_ref().map(|v| v.supported_features) } + fn detected_dtls_backend(&self) -> Option { + self.define_info.as_ref().and_then(|v| v.dtls_backend) + } + fn version(&self) -> Option { Version::from(VENDORED_LIBCOAP_VERSION) } @@ -356,15 +394,14 @@ impl BuildSystem for VendoredBuildSystem { // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. // Therefore, we use `-isystem` instead. // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir - .clang_args(self.include_paths.iter().map(|v| format!("-isystem{}", v.display()))) - ) + .clang_args(self.include_paths.iter().map(|v| format!("-isystem{}", v.display())))) })?; self.define_info = Some(RefCell::take(&define_info)); if let Some(version) = &self.define_info.as_ref().unwrap().version { if Version::from(VENDORED_LIBCOAP_VERSION) != Version::from(version) { - return Err(anyhow!("The library version indicated by the headers does not match the vendored version that should be in use. Are the include paths misconfigured?")) + return Err(anyhow!("The library version indicated by the headers does not match the vendored version that should be in use. Are the include paths misconfigured?")); } } diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index a826ec39..b4bf73c4 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -68,11 +68,17 @@ fn main() -> Result<()> { bindings_file.canonicalize()?.display() ); - let version = build_system.version(); - if version.is_some() && version < Version::from(MINIMUM_LIBCOAP_VERSION) { - println!("cargo:warning=The linked version of libcoap is lower than the minimal version required for libcoap-sys ({}), this will most likely cause errors.", MINIMUM_LIBCOAP_VERSION); - } else if version.is_none() { - println!("cargo:warning=Unable to automatically detect the linked version of libcoap, please manually ensure that the used version is at least {}.", MINIMUM_LIBCOAP_VERSION); + if let Some(version) = build_system.version() { + if version < Version::from(MINIMUM_LIBCOAP_VERSION).unwrap() { + println!("cargo:warning=The linked version of libcoap is lower than the minimal version required for libcoap-sys ({}), this will most likely cause errors.", MINIMUM_LIBCOAP_VERSION); + } + println!("cargo::metadata=libcoap_version={}", version.as_str()) + } else { + println!("cargo:warning=Unable to automatically detect the linked version of libcoap, please manually ensure that the used version is at least {} for libcoap-sys to work as expected.", MINIMUM_LIBCOAP_VERSION); + } + + if let Some(dtls_backend) = build_system.detected_dtls_backend() { + println!("cargo::metadata=dtls_backend={}", dtls_backend.as_str()); } match build_system.detected_features() { @@ -148,9 +154,8 @@ fn vendored_libcoap_build( // Windows). // See: https://github.com/obgm/libcoap/blob/develop/BUILDING match target_os { - "espidf" => { - EspIdfBuildSystem::new(out_dir, requested_features, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) - }, + "espidf" => EspIdfBuildSystem::new(out_dir, requested_features, requested_dtls_backend) + .map(|v| Box::::from(Box::new(v))), _ => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) .map(|v| Box::::from(Box::new(v))), } @@ -173,7 +178,8 @@ fn link_libcoap_auto( .map(|v| Box::::from(Box::new(v))) .or_else(|e| { errors.push(("pkgconfig", e)); - ManualBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) + ManualBuildSystem::link_with_libcoap(out_dir, requested_dtls_backend) + .map(|v| Box::::from(Box::new(v))) }) .map_err(|e| { errors.push(("manual", e)); diff --git a/libcoap-sys/build/metadata.rs b/libcoap-sys/build/metadata.rs index be920c92..e716d3f8 100644 --- a/libcoap-sys/build/metadata.rs +++ b/libcoap-sys/build/metadata.rs @@ -6,6 +6,10 @@ use std::{ use anyhow::anyhow; use enumset::{EnumSet, EnumSetType}; +/// Minimum required version of libcoap in order to build the bindgen-generated bindings. +/// +/// Note that this is *not* the minimum supported version of the safe wrapper, and should only be +/// increased if building on older versions causes issues with libcoap-sys specifically. pub const MINIMUM_LIBCOAP_VERSION: &str = "4.3.5"; #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] @@ -239,7 +243,7 @@ impl DtlsBackend { /// define does not correspond to a DTLS library. pub fn library_from_define(define_name: &str, define_value: i64) -> Option { if define_value == 0 { - return None + return None; } match define_name { "COAP_WITH_LIBGNUTLS" => Some(DtlsBackend::GnuTls), diff --git a/libcoap/Cargo.toml b/libcoap/Cargo.toml index bb098208..56a70808 100644 --- a/libcoap/Cargo.toml +++ b/libcoap/Cargo.toml @@ -48,6 +48,7 @@ thiserror = "^1.0" [build-dependencies] version-compare = "0.2.0" +anyhow = "1.0.95" [package.metadata.docs.rs] features = ["dtls", "dtls_openssl", "vendored", "url"] diff --git a/libcoap/build.rs b/libcoap/build.rs index 895de427..0a27e9ab 100644 --- a/libcoap/build.rs +++ b/libcoap/build.rs @@ -1,20 +1,68 @@ // SPDX-License-Identifier: BSD-2-CLAUSE -//use version_compare::Cmp; +use anyhow::{bail, Result}; +use std::env::VarError; use version_compare::Version; -fn main() { - if let Ok(libcoap_version) = std::env::var("DEP_COAP_3_LIBCOAP_VERSION") { - let version = Version::from(libcoap_version.as_ref()).expect("invalid libcoap version"); - println!("cargo:rustc-cfg=libcoap_version=\"{}\"", version.as_str()); - - // Uncomment and adjust this in order to create version-dependent cfg-flags. - // Note: In most cases, you probably want to check for the presence of a given feature instead. - // Matching based on the libcoap version usually only makes sense in order to either - // enable optional optimizations possible with newer versions, or to add struct fields - // that were added into existing structs without breaking backward compatibility. - /*if version > Version::from("4.3.5").unwrap() { - println!("cargo:rustc-cfg=[INSERT FLAG NAME HERE]") - }*/ +/// The minimal version of libcoap that is expected to work with libcoap-rs. +/// +/// Does not necessarily match the minimum supported version of libcoap-sys, and should be increased +/// whenever we make changes to the safe wrapper that can not feasibly be supported on older +/// versions of libcoap. +const MINIMUM_LIBCOAP_VERSION: &str = "4.3.5"; + +fn main() -> Result<()> { + println!("cargo::rustc-check-cfg=cfg(dtls_backend, values(\"gnutls\", \"mbedtls\", \"tinydtls\", \"openssl\", \"wolfssl\"))"); + println!("cargo::rustc-check-cfg=cfg(libcoap_version, values(any()))"); + + // If at all possible, you should not write code that is conditional on the DTLS backend (use + // cargo features instead). + // If there is no other way (e.g., if there is no way to determine feature support), you must + // at least write code that can also deal with the variable not being there. + match std::env::var("DEP_COAP_3_DTLS_BACKEND") { + Ok(dtls_backend) => { + println!("cargo::rustc-cfg=dtls_backend=\"{}\"", dtls_backend) + }, + Err(VarError::NotUnicode(_)) => { + panic!("DEP_COAP_3_DTLS_BACKEND is not valid unicode") + }, + Err(VarError::NotPresent) => {}, } + + match std::env::var("DEP_COAP_3_LIBCOAP_VERSION") { + Ok(libcoap_version) => { + let version = Version::from(libcoap_version.as_ref()).expect("invalid libcoap version"); + println!("cargo::rustc-cfg=libcoap_version=\"{}\"", version.as_str()); + + if version < Version::from(MINIMUM_LIBCOAP_VERSION).unwrap() { + // Unlike libcoap-sys, we do return an error here and not just a warning, as + // unsupported libcoap versions might have semantic differences that break the + // safety guarantees this wrapper is supposed to provide. + + bail!("The linked version of libcoap is lower than the minimal version required for libcoap-rs ({}), can not build.", MINIMUM_LIBCOAP_VERSION); + } + + // Uncomment and adjust this in order to create version-dependent cfg-flags. + // When updating the minimum supported libcoap version, one should also remove all + // cfg-flags that are only relevant for versions lower than the new minimum + // supported one, and rewrite code gated on these flags to assume that the minimum + // version . + // Note: In most cases, you probably want to check for the presence of a given feature instead. + // Matching based on the libcoap version usually only makes sense in order to either + // enable optional optimizations possible with newer versions, or to add struct fields + // that were added into existing structs without breaking backward compatibility. + + /*if version > Version::from("4.3.5").unwrap() { + println!("cargo:rustc-cfg=[INSERT FLAG NAME HERE]") + }*/ + }, + Err(VarError::NotUnicode(_)) => { + panic!("DEP_COAP_3_LIBCOAP_VERSION is not valid unicode") + }, + Err(VarError::NotPresent) => { + println!("cargo:warning=Unable to automatically detect the linked version of libcoap, please manually ensure that the used version is at least {} for libcoap-rs to work as expected.", MINIMUM_LIBCOAP_VERSION); + }, + } + + Ok(()) } diff --git a/libcoap/tests/dtls_pki_client_server_test.rs b/libcoap/tests/dtls_pki_client_server_test.rs index bc97761f..eec324a6 100644 --- a/libcoap/tests/dtls_pki_client_server_test.rs +++ b/libcoap/tests/dtls_pki_client_server_test.rs @@ -75,9 +75,9 @@ pub fn dtls_pki_asn1_file_client_server_request() { // For some inexplicable reason, setting the CA cert fails _only_ with ASN1 files using the // OpenSSL library. // I'm pretty sure this is a libcoap issue, so we'll not set the CA cert there for now. - #[cfg(not(feature = "dtls_openssl"))] + #[cfg(not(dtls_backend = "openssl"))] Some(key_storage.join("./ca/ca.crt.der")), - #[cfg(feature = "dtls_openssl")] + #[cfg(dtls_backend = "openssl")] None::, key_storage.join("./server/server.crt.der"), key_storage.join("./server/server.key.der"), @@ -93,7 +93,7 @@ pub fn dtls_pki_asn1_file_client_server_request() { #[test] // GnuTLS does not like DER-encoded EC keys from memory (for some reason. Loading them from files as // done in the test above works fine). -#[cfg(not(feature = "dtls_gnutls"))] +#[cfg_attr(dtls_backend = "gnutls", ignore)] pub fn dtls_pki_asn1_memory_client_server_request() { const DER_CA_CERT: &[u8] = include_bytes!("../resources/test-keys/ca/ca.crt.der"); const DER_CLIENT_PUBLIC_CERT: &[u8] = include_bytes!("../resources/test-keys/client/client.crt.der"); From 6d2a339bbd661dfbb7cc334db2bfe7921d6a8d28 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Tue, 21 Jan 2025 15:03:08 +0100 Subject: [PATCH 13/26] refactor(sys): improve error output and coap_startup_with_feature_checks When building for the ESP-IDF without the libcoap component being enabled, all compile errors will now be silenced except for the one regarding the missing component, which should make it easier to identify the underlying issue. Additionally, the coap_startup_with_feature_checks() function now also ensures that the compile-time detected DTLS library matches the one that libcoap was actually linked against, as a mismatch would indicate a more serious issue (libcoap-sys being linked against a different libcoap than the headers the bindings were generated for) and might break downstream libraries that made assumptions about the underlying DTLS library. --- libcoap-sys/Cargo.toml | 3 +- libcoap-sys/build/build_system/esp_idf.rs | 14 +- libcoap-sys/build/main.rs | 13 +- libcoap-sys/build/metadata.rs | 2 + libcoap-sys/src/lib.rs | 223 +++++++++++----------- 5 files changed, 127 insertions(+), 128 deletions(-) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index ed626327..1c0a78d8 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -147,11 +147,10 @@ tinydtls-sys = { version = "^0.2.0", default-features = false, optional = true } esp-idf-sys = { version = "0.36.1" } [build-dependencies] -bindgen = { version = "0.71.1", features = ["experimental"] } +bindgen = { version = "0.71.1" } autotools = "^0.2.3" fs_extra = "^1.2" pkg-config = "^0.3.24" -regex = "1.10.5" version-compare = "0.2.0" anyhow = { version = "1.0.94", features = ["backtrace"] } enumset = "1.1.5" diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs index cf4aa3bb..d9994486 100644 --- a/libcoap-sys/build/build_system/esp_idf.rs +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -120,16 +120,6 @@ impl BuildSystem for EspIdfBuildSystem { } } - // Add check whether the libcoap component is enabled in ESP-IDF to generated bindings file. - println!("cargo::rustc-check-cfg=cfg(esp_idf_comp_espressif__coap_enabled)"); - writeln!( - &mut libcoap_bindings_file, - "#[cfg(not(esp_idf_comp_espressif__coap_enabled))]" - ) - .context("unable to write to bindings file")?; - writeln!(&mut libcoap_bindings_file, "compile_error!(\"You are building libcoap-sys for an ESP-IDF target, but have not added the espressif/coap remote component (see the libcoap-sys documentation for more information)\");") - .context("unable to write to bindings file")?; - for (feature_name, feature_flag) in self .requested_features .iter() @@ -140,7 +130,9 @@ impl BuildSystem for EspIdfBuildSystem { writeln!( &mut libcoap_bindings_file, - "#[cfg(not(esp_idf_{}))]", + // Only show these errors if the coap component is enabled at all (in order to only + // show the relevant compilation error). + "#[cfg(all(esp_idf_comp_espressif__coap_enabled, not(esp_idf_{})))]", feature_flag.to_lowercase() ) .context("unable to write to bindings file")?; diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index b4bf73c4..9404e47b 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -19,7 +19,16 @@ fn main() -> Result<()> { println!("cargo:rerun-if-env-changed=LIBCOAP_RS_DTLS_BACKEND"); println!("cargo:rerun-if-env-changed=LIBCOAP_RS_BUILD_SYSTEM"); println!("cargo:rerun-if-env-changed=LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS"); + // On ESP-IDF builds, this indicates whether the libcoap component has been enabled. + println!("cargo::rustc-check-cfg=cfg(esp_idf_comp_espressif__coap_enabled)"); + // Indicates the DTLS library crate that was linked against, if a library version vendored by + // another crate was used. println!("cargo:rustc-check-cfg=cfg(used_dtls_crate, values(\"mbedtls\", \"tinydtls\", \"openssl\"))"); + // Indicates the DTLS backend used, if any. + println!("cargo:rustc-check-cfg=cfg(dtls_backend, values(\"mbedtls\", \"tinydtls\", \"openssl\", \"gnutls\", \"wolfssl\"))"); + // The detected libcoap version, if any. + println!("cargo::rustc-check-cfg=cfg(libcoap_version, values(any()))"); + let out_dir = PathBuf::from( env::var_os("OUT_DIR").expect("no OUT_DIR was provided (are we not running as a cargo build script?)"), ); @@ -72,13 +81,15 @@ fn main() -> Result<()> { if version < Version::from(MINIMUM_LIBCOAP_VERSION).unwrap() { println!("cargo:warning=The linked version of libcoap is lower than the minimal version required for libcoap-sys ({}), this will most likely cause errors.", MINIMUM_LIBCOAP_VERSION); } - println!("cargo::metadata=libcoap_version={}", version.as_str()) + println!("cargo::metadata=libcoap_version={}", version.as_str()); + println!("cargo::rustc-cfg=libcoap_version=\"{}\"", version.as_str()); } else { println!("cargo:warning=Unable to automatically detect the linked version of libcoap, please manually ensure that the used version is at least {} for libcoap-sys to work as expected.", MINIMUM_LIBCOAP_VERSION); } if let Some(dtls_backend) = build_system.detected_dtls_backend() { println!("cargo::metadata=dtls_backend={}", dtls_backend.as_str()); + println!("cargo::rustc-cfg=dtls_backend=\"{}\"", dtls_backend.as_str()); } match build_system.detected_features() { diff --git a/libcoap-sys/build/metadata.rs b/libcoap-sys/build/metadata.rs index e716d3f8..2427ab47 100644 --- a/libcoap-sys/build/metadata.rs +++ b/libcoap-sys/build/metadata.rs @@ -140,6 +140,8 @@ impl LibcoapFeature { match self { LibcoapFeature::DtlsPsk => Some("COAP_MBEDTLS_PSK"), LibcoapFeature::DtlsPki => Some("COAP_MBEDTLS_PKI"), + // Should be implied by mbedtls being enabled. + LibcoapFeature::DtlsCid => Some("COAP_MBEDTLS_PSK"), LibcoapFeature::Tcp => Some("COAP_TCP_SUPPORT"), LibcoapFeature::Oscore => Some("COAP_OSCORE_SUPPORT"), LibcoapFeature::ObservePersist => Some("COAP_OBSERVE_PERSIST"), diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 5231ba15..6507a234 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -105,6 +105,13 @@ use openssl_sys as _; #[cfg(used_dtls_crate = "tinydtls")] use tinydtls_sys as _; +// Add check whether the libcoap component is enabled when building for the ESP-IDF. +#[cfg(all(target_os = "espidf", not(esp_idf_comp_espressif__coap_enabled)))] +compile_error!(concat!( + "You are building libcoap-sys for an ESP-IDF target, but have not added the\n", + "espressif/coap remote component (see the libcoap-sys documentation for more information)" +)); + include!(env!("BINDINGS_FILE")); /// Compares instances of coap_str_const_t and/or coap_string_t. @@ -142,134 +149,121 @@ pub unsafe fn coap_string_equal_internal( && memcmp(str1_ptr as *const c_void, str2_ptr as *const c_void, str1_len) == 0) } +/// Execute feature check function and panic with an error message if the feature is not supported. +/// +/// SAFETY: check_fn will be called, make sure to wrap this macro in unsafe if the function is +/// unsafe. +#[cfg(any(not(target_os = "espidf"), esp_idf_comp_espressif__coap_enabled))] +macro_rules! feature_check { + ($feature:literal, $check_fn:ident) => { + #[cfg(feature = $feature)] + // SAFETY: Function is always safe to call. + if $check_fn() != 1 { + panic!("Required feature \"{}\" is not supported by libcoap", $feature) + } + }; + ( $(($feature:literal, $check_fn:ident)),* ) => { + $( + feature_check!($feature, $check_fn); + )* + } +} + +#[cfg(any(not(target_os = "espidf"), esp_idf_comp_espressif__coap_enabled))] +macro_rules! dtls_backend_string { + ($backend:ident) => { + match $backend { + coap_tls_library_t::COAP_TLS_LIBRARY_OPENSSL => "openssl", + coap_tls_library_t::COAP_TLS_LIBRARY_GNUTLS => "gnutls", + coap_tls_library_t::COAP_TLS_LIBRARY_MBEDTLS => "mbedtls", + coap_tls_library_t::COAP_TLS_LIBRARY_TINYDTLS => "tinydtls", + coap_tls_library_t::COAP_TLS_LIBRARY_WOLFSSL => "wolfssl", + coap_tls_library_t::COAP_TLS_LIBRARY_NOTLS => "notls", + } + }; +} + +#[cfg(any(not(target_os = "espidf"), esp_idf_comp_espressif__coap_enabled))] +macro_rules! panic_wrong_dtls { + ($presumed_backend:ident, $detected_backend:ident) => { + panic!( + concat!( + "compile-time detected DTLS backend \"{}\" does not match run-time detected DTLS backend \"{}\".\n", + "This almost certainly means that libcoap-sys was linked against a different version of \n", + "libcoap than the one whose headers were used for binding generation." + ), + dtls_backend_string!($presumed_backend), + dtls_backend_string!($detected_backend) + ) + }; +} + /// Initialize the CoAP library and additionally perform runtime checks to ensure that required /// features (as enabled in `Cargo.toml`) are available. /// /// You *should* prefer using this function over [coap_startup], as without calling this function /// some of the features enabled using the Cargo features may not actually be available. -/// (NOTE: However, see the below section on safety). /// /// Either this function or [coap_startup] must be run once before any libcoap function is called. /// /// If you are absolutely 100% certain that all features you require are always available (or are /// prepared to deal with error return values/different behavior on your own if they aren't), you /// may use [coap_startup] instead. -/// -/// # SAFETY WARNING -/// In order to preserve backwards compatibility, this method may (for now) skip the feature checks -/// altogether if they aren't provided by libcoap (which may be the case for libcoap < 4.3.5rc3). -/// -/// If you want to be absolutely certain that a given feature is available, you *must* also check -/// the libcoap version to ensure that it is at least 4.3.5rc3. -/// -/// This behavior will be changed as soon as libcoap 4.3.5 is released, after which libcoap 4.3.5 -/// will become the minimum supported version and these checks will be mandatory. +// Make sure that if we're compiling for the ESP-IDF, this function is only compiled if the +// libcoap component is installed in the ESP-IDF. +// This way, these function calls will not cause missing function or struct definition errors that +// clutter up the error log, and the only error message will be the way more descriptive +// compile_error macro invocation at the start of this file. +#[cfg(any(not(target_os = "espidf"), esp_idf_comp_espressif__coap_enabled))] pub fn coap_startup_with_feature_checks() { - #[cfg(feature = "af-unix")] - // SAFETY: Function is always safe to call. - if unsafe { coap_af_unix_is_supported() != 1 } { - panic!("Required feature \"af-unix\" is not supported by libcoap") - } - #[cfg(feature = "async")] - // SAFETY: Function is always safe to call. - if unsafe { coap_async_is_supported() != 1 } { - panic!("Required feature \"async\" is not supported by libcoap") - } - #[cfg(feature = "client")] - // SAFETY: Function is always safe to call. - if unsafe { coap_client_is_supported() != 1 } { - panic!("Required feature \"ipv4\" is not supported by libcoap") - } - #[cfg(feature = "dtls")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_is_supported() != 1 } { - panic!("Required feature \"dtls\" is not supported by libcoap") - } - #[cfg(feature = "dtls-cid")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_cid_is_supported() != 1 } { - panic!("Required feature \"dtls-cid\" is not supported by libcoap") - } - #[cfg(feature = "dtls-psk")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_psk_is_supported() != 1 } { - panic!("Required feature \"dtls-psk\" is not supported by libcoap") - } - #[cfg(feature = "dtls-pki")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_pki_is_supported() != 1 } { - panic!("Required feature \"dtls-pki\" is not supported by libcoap") - } - #[cfg(feature = "dtls-pkcs11")] - // SAFETY: Function is always safe to call. - if !unsafe { coap_dtls_pkcs11_is_supported() == 1 } { - panic!("Required feature \"dtls-pkcs11\" is not supported by libcoap") - } - #[cfg(feature = "dtls-rpk")] - // SAFETY: Function is always safe to call. - if unsafe { coap_dtls_rpk_is_supported() != 1 } { - panic!("Required feature \"dtls-rpk\" is not supported by libcoap") - } - #[cfg(feature = "epoll")] - // SAFETY: Function is always safe to call. - if unsafe { coap_epoll_is_supported() != 1 } { - panic!("Required feature \"epoll\" is not supported by libcoap") - } - #[cfg(feature = "ipv4")] - // SAFETY: Function is always safe to call. - if !unsafe { coap_ipv4_is_supported() == 1 } { - panic!("Required feature \"ipv4\" is not supported by libcoap") - } - #[cfg(feature = "ipv6")] - // SAFETY: Function is always safe to call. - if !unsafe { coap_ipv6_is_supported() == 1 } { - panic!("Required feature \"ipv6\" is not supported by libcoap") - } - #[cfg(feature = "observe-persist")] - // SAFETY: Function is always safe to call. - if unsafe { coap_observe_persist_is_supported() != 1 } { - panic!("Required feature \"ipv4\" is not supported by libcoap") - } - #[cfg(feature = "oscore")] - // SAFETY: Function is always safe to call. - if unsafe { coap_oscore_is_supported() != 1 } { - panic!("Required feature \"oscore\" is not supported by libcoap") - } - #[cfg(feature = "q-block")] - // SAFETY: Function is always safe to call. - if unsafe { coap_q_block_is_supported() != 1 } { - panic!("Required feature \"q-block\" is not supported by libcoap") - } - #[cfg(feature = "server")] - // SAFETY: Function is always safe to call. - if unsafe { coap_server_is_supported() != 1 } { - panic!("Required feature \"ipv4\" is not supported by libcoap") - } - #[cfg(feature = "tcp")] - // SAFETY: Function is always safe to call. - if unsafe { coap_tcp_is_supported() != 1 } { - panic!("Required feature \"tcp\" is not supported by libcoap") - } - #[cfg(feature = "thread-safe")] - // SAFETY: Function is always safe to call. - if unsafe { coap_threadsafe_is_supported() != 1 } { - panic!("Required feature \"thread-safe\" is not supported by libcoap") - } - #[cfg(feature = "tls")] - // SAFETY: Function is always safe to call. - if unsafe { coap_tls_is_supported() != 1 } { - panic!("Required feature \"tls\" is not supported by libcoap") - } - #[cfg(feature = "websockets")] - // SAFETY: Function is always safe to call. - if unsafe { coap_ws_is_supported() != 1 } { - panic!("Required feature \"websockets\" is not supported by libcoap") + unsafe { + feature_check!( + ("af-unix", coap_af_unix_is_supported), + ("async", coap_async_is_supported), + ("client", coap_client_is_supported), + ("dtls", coap_dtls_is_supported), + ("dtls-cid", coap_dtls_cid_is_supported), + ("dtls-psk", coap_dtls_psk_is_supported), + ("dtls-pki", coap_dtls_pki_is_supported), + ("dtls-pkcs11", coap_dtls_pkcs11_is_supported), + ("dtls-rpk", coap_dtls_rpk_is_supported), + ("epoll", coap_epoll_is_supported), + ("ipv4", coap_ipv4_is_supported), + ("ipv6", coap_ipv6_is_supported), + ("observe-persist", coap_observe_persist_is_supported), + ("oscore", coap_oscore_is_supported), + ("q-block", coap_q_block_is_supported), + ("server", coap_server_is_supported), + ("tcp", coap_tcp_is_supported), + ("thread-safe", coap_threadsafe_is_supported), + ("tls", coap_tls_is_supported), + ("websockets", coap_ws_is_supported), + ("secure-websockets", coap_wss_is_supported) + ); } - #[cfg(feature = "secure-websockets")] - // SAFETY: Function is always safe to call. - if unsafe { coap_wss_is_supported() != 1 } { - panic!("Required feature \"websockets\" is not supported by libcoap") + + let presumed_dtls_backend = if cfg!(dtls_backend = "openssl") { + coap_tls_library_t::COAP_TLS_LIBRARY_OPENSSL + } else if cfg!(dtls_backend = "gnutls") { + coap_tls_library_t::COAP_TLS_LIBRARY_GNUTLS + } else if cfg!(dtls_backend = "mbedtls") { + coap_tls_library_t::COAP_TLS_LIBRARY_MBEDTLS + } else if cfg!(dtls_backend = "tinydtls") { + coap_tls_library_t::COAP_TLS_LIBRARY_TINYDTLS + } else if cfg!(dtls_backend = "wolfssl") { + coap_tls_library_t::COAP_TLS_LIBRARY_WOLFSSL + } else { + coap_tls_library_t::COAP_TLS_LIBRARY_NOTLS + }; + + let actual_dtls_backend = unsafe { *coap_get_tls_library_version() }.type_; + + if presumed_dtls_backend != coap_tls_library_t::COAP_TLS_LIBRARY_NOTLS + && actual_dtls_backend != presumed_dtls_backend + { + panic_wrong_dtls!(presumed_dtls_backend, actual_dtls_backend); } + // SAFETY: Function is always safe to call. unsafe { coap_startup() } } @@ -453,6 +447,7 @@ mod tests { /// Test case that creates a basic coap server and makes a request to it from a separate context #[test] fn test_coap_client_server_basic() { + coap_startup_with_feature_checks(); // This will give us a SocketAddress with a port in the local port range automatically // assigned by the operating system. // Because the UdpSocket goes out of scope, the Port will be free for usage by libcoap. From 7a702bf75c79820af7164a798cf0762be2ab7ac8 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Tue, 21 Jan 2025 15:15:36 +0100 Subject: [PATCH 14/26] fix(sys): respect LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS on ESP-IDF targets --- libcoap-sys/build/build_system/esp_idf.rs | 49 ++++++++++++++--------- libcoap-sys/build/main.rs | 31 +++++++++++--- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs index d9994486..b93ac5d4 100644 --- a/libcoap-sys/build/build_system/esp_idf.rs +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -14,6 +14,7 @@ pub struct EspIdfBuildSystem { out_dir: PathBuf, esp_idf_bindings_file: PathBuf, requested_features: EnumSet, + bypass_compile_time_feature_checks: bool, } impl EspIdfBuildSystem { @@ -21,6 +22,7 @@ impl EspIdfBuildSystem { out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option, + bypass_compile_time_feature_checks: bool, ) -> Result { embuild::espidf::sysenv::output(); let esp_idf_bindings_file = env::var_os("DEP_ESP_IDF_ROOT") @@ -38,6 +40,7 @@ impl EspIdfBuildSystem { out_dir, esp_idf_bindings_file, requested_features, + bypass_compile_time_feature_checks, }) } } @@ -80,7 +83,7 @@ impl BuildSystem for EspIdfBuildSystem { let esp_bindings_file = std::fs::read_to_string(&self.esp_idf_bindings_file).context("unable to read ESP-IDF bindings file")?; let parsed_esp_bindings_file = - syn::parse_file(&esp_bindings_file).context("unable to parse ESP-IDF bidnings file")?; + syn::parse_file(&esp_bindings_file).context("unable to parse ESP-IDF bindings file")?; // Create file for our own bindings. let bindings_file_path = self.out_dir.join("bindings.rs"); @@ -120,24 +123,34 @@ impl BuildSystem for EspIdfBuildSystem { } } - for (feature_name, feature_flag) in self - .requested_features - .iter() - .filter_map(|v| v.sdkconfig_flag_name().map(|flag| (v.as_str(), flag))) - { - // For some reason, embuild adds expected cfg flags for some, but not all feature-related sdkconfig flags, causing warnings if we don't do this. - println!("cargo::rustc-check-cfg=cfg(esp_idf_{})", feature_flag.to_lowercase()); - - writeln!( - &mut libcoap_bindings_file, - // Only show these errors if the coap component is enabled at all (in order to only - // show the relevant compilation error). - "#[cfg(all(esp_idf_comp_espressif__coap_enabled, not(esp_idf_{})))]", - feature_flag.to_lowercase() - ) - .context("unable to write to bindings file")?; - writeln!(&mut libcoap_bindings_file, "compile_error!(\"Requested feature \\\"{feature_name}\\\" is not enabled in ESP-IDF sdkconfig.defaults (set `CONFIG_{feature_flag}=y` to fix this)\");") + if !self.bypass_compile_time_feature_checks { + for (feature_name, feature_flag) in self + .requested_features + .iter() + .filter_map(|v| v.sdkconfig_flag_name().map(|flag| (v.as_str(), flag))) + { + // For some reason, embuild adds expected cfg flags for some, but not all + // feature-related sdkconfig flags, causing warnings if we don't do this. + println!("cargo::rustc-check-cfg=cfg(esp_idf_{})", feature_flag.to_lowercase()); + + writeln!( + &mut libcoap_bindings_file, + // Only show these errors if the coap component is enabled at all (in order to + // only show the relevant compilation error). + "#[cfg(all(esp_idf_comp_espressif__coap_enabled, not(esp_idf_{})))]", + feature_flag.to_lowercase() + ) .context("unable to write to bindings file")?; + writeln!( + &mut libcoap_bindings_file, + concat!( + "compile_error!(\"Requested feature \\\"{}\\\" is not enabled\n", + "in ESP-IDF sdkconfig.defaults (set `CONFIG_{}=y` to fix this)\");" + ), + feature_flag, feature_name + ) + .context("unable to write to bindings file")?; + } } Ok(bindings_file_path) diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 9404e47b..eb3a6db9 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -67,8 +67,15 @@ fn main() -> Result<()> { out_dir, requested_features, requested_dtls_backend, + bypass_compile_time_feature_checks, + ), + None => link_libcoap_auto( + &target_os, + out_dir, + requested_features, + requested_dtls_backend, + bypass_compile_time_feature_checks, ), - None => link_libcoap_auto(&target_os, out_dir, requested_features, requested_dtls_backend), }?; let bindings_file = build_system.generate_bindings()?; @@ -134,10 +141,11 @@ fn link_libcoap_explicit( out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option, + bypass_compile_time_feature_checks: bool, ) -> Result> { match requested_build_system { "vendored" if target_os == "espidf" => { - EspIdfBuildSystem::new(out_dir, requested_features, requested_dtls_backend).map(|v| Box::::from(Box::new(v))) + EspIdfBuildSystem::new(out_dir, requested_features, requested_dtls_backend, bypass_compile_time_feature_checks).map(|v| Box::::from(Box::new(v))) }, "vendored" if cfg!(not(feature = "vendored")) => Err(anyhow!("LIBCOAP_RS_BUILD_SYSTEM has been set to \"vendored\", but the corresponding crate feature \"vendored\" has not been enabled.")), "vendored" => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) @@ -159,14 +167,20 @@ fn vendored_libcoap_build( out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option, + bypass_compile_time_feature_checks: bool, ) -> Result> { // TODO: Later on, we'll probably want to use the CMake based build system for any host+target // combination that the libcoap build documentation recommends CMake for (most notably: // Windows). // See: https://github.com/obgm/libcoap/blob/develop/BUILDING match target_os { - "espidf" => EspIdfBuildSystem::new(out_dir, requested_features, requested_dtls_backend) - .map(|v| Box::::from(Box::new(v))), + "espidf" => EspIdfBuildSystem::new( + out_dir, + requested_features, + requested_dtls_backend, + bypass_compile_time_feature_checks, + ) + .map(|v| Box::::from(Box::new(v))), _ => VendoredBuildSystem::build_libcoap(out_dir, requested_features, requested_dtls_backend) .map(|v| Box::::from(Box::new(v))), } @@ -177,13 +191,20 @@ fn link_libcoap_auto( out_dir: PathBuf, requested_features: EnumSet, requested_dtls_backend: Option, + bypass_compile_time_feature_checks: bool, ) -> Result> { let mut errors = Vec::<(&'static str, anyhow::Error)>::new(); // Try vendored build first if the feature is enabled and supported by the host. // If the vendored build fails on a supported target, do not try anything else (we assume that // the user wanted to use the vendored library for a reason). if cfg!(feature = "vendored") || target_os == "espidf" { - return vendored_libcoap_build(target_os, out_dir, requested_features, requested_dtls_backend); + return vendored_libcoap_build( + target_os, + out_dir, + requested_features, + requested_dtls_backend, + bypass_compile_time_feature_checks, + ); } PkgConfigBuildSystem::link_with_libcoap(out_dir.clone(), requested_dtls_backend) .map(|v| Box::::from(Box::new(v))) From 63a57964aa8c4df49c4c7c79034563ad7888e0c3 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Tue, 21 Jan 2025 15:34:44 +0100 Subject: [PATCH 15/26] fix(lib): specify the ESP-IDF libcoap component in libcoap-rs --- libcoap/Cargo.toml | 11 +++++++++++ libcoap/src/wrapper.h | 1 + 2 files changed, 12 insertions(+) create mode 120000 libcoap/src/wrapper.h diff --git a/libcoap/Cargo.toml b/libcoap/Cargo.toml index 56a70808..377622d8 100644 --- a/libcoap/Cargo.toml +++ b/libcoap/Cargo.toml @@ -52,3 +52,14 @@ anyhow = "1.0.95" [package.metadata.docs.rs] features = ["dtls", "dtls_openssl", "vendored", "url"] + +[target.'cfg(target_os="espidf")'.dependencies] +esp-idf-sys = { version = "0.36.1" } + +# For ESP-IDF builds, we need to add the espressif/coap component ourselves here, +# as esp-idf-sys only inspects the metadata for direct dependencies. +# Otherwise, users of this library would always also have to depend on libcoap-sys +# or add this snippet themselves. +[[package.metadata.esp-idf-sys.extra_components]] +remote_component = { name = "espressif/coap", version = "4.3.5~3" } +bindings_header = "src/wrapper.h" \ No newline at end of file diff --git a/libcoap/src/wrapper.h b/libcoap/src/wrapper.h new file mode 120000 index 00000000..762e29fb --- /dev/null +++ b/libcoap/src/wrapper.h @@ -0,0 +1 @@ +../../libcoap-sys/src/wrapper.h \ No newline at end of file From 8e10554957e7924d0ccc7aa8cfa17b7137315442 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Tue, 21 Jan 2025 18:57:13 +0100 Subject: [PATCH 16/26] refactor(sys|lib): change bindgen enums+unions, re-export libc The bindings generated by libcoap-sys now use a different default enum style, which avoids potential soundness issues if libcoap ever decides to provide a non-existing enum variant. As a consequence, all C enums in libcoap-sys are now represented by a type alias to the underlying representational type + an appropriately (if verbosely) named constant for each different variant. The code in libcoap-rs has been rewritten to account for these changes where necessary. Additionally, libcoap-sys now re-exports the crate used for C standard library types, which allows downstream crates (like libcoap-rs) to use the appropriate types without needing to determine the right crate themselves. Typically, the re-exported crate will be libc, but some targets may have their own alternative crates (such as the ESP-IDF with the esp-idf-sys). --- libcoap-sys/Cargo.toml | 4 +- libcoap-sys/build/bindings.rs | 62 ++++- libcoap-sys/build/build_system/manual.rs | 25 +- libcoap-sys/build/build_system/pkgconfig.rs | 35 +-- libcoap-sys/build/build_system/vendored.rs | 35 ++- libcoap-sys/src/lib.rs | 110 +++++---- libcoap/Cargo.toml | 3 +- libcoap/src/context.rs | 92 ++++--- libcoap/src/crypto/pki_rpk/key.rs | 70 +++--- libcoap/src/crypto/pki_rpk/pki.rs | 19 +- libcoap/src/crypto/pki_rpk/rpk.rs | 13 +- libcoap/src/event.rs | 14 +- libcoap/src/protocol.rs | 251 ++++++++++---------- libcoap/src/resource.rs | 14 +- libcoap/src/session/client.rs | 27 ++- libcoap/src/session/mod.rs | 59 +++-- libcoap/src/session/server.rs | 24 +- libcoap/src/types.rs | 93 ++++---- libcoap/tests/common/dtls.rs | 22 +- libcoap/tests/common/mod.rs | 6 +- 20 files changed, 569 insertions(+), 409 deletions(-) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 1c0a78d8..2734cfeb 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -140,12 +140,14 @@ dtls-rpk = ["dtls"] [dependencies] openssl-sys = { version = "^0.9.74", optional = true } mbedtls-sys-auto = { version = "^2.26", optional = true } -libc = "^0.2.126" tinydtls-sys = { version = "^0.2.0", default-features = false, optional = true } [target.'cfg(target_os="espidf")'.dependencies] esp-idf-sys = { version = "0.36.1" } +[target.'cfg(not(target_os="espidf"))'.dependencies] +libc = "0.2.126" + [build-dependencies] bindgen = { version = "0.71.1" } autotools = "^0.2.3" diff --git a/libcoap-sys/build/bindings.rs b/libcoap-sys/build/bindings.rs index 07102e90..dc60a01f 100644 --- a/libcoap-sys/build/bindings.rs +++ b/libcoap-sys/build/bindings.rs @@ -1,12 +1,8 @@ use std::{cell::RefCell, fmt::Debug, path::PathBuf, rc::Rc}; -use anyhow::{Context, Result}; -use bindgen::{ - callbacks::{IntKind, ParseCallbacks}, - EnumVariation, -}; - use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}; +use anyhow::{Context, Result}; +use bindgen::callbacks::{DeriveTrait, ImplementsTrait, IntKind, ParseCallbacks}; /// Implementation of bindgen's [ParseCallbacks] that allow reading some meta-information about the /// used libcoap version from its defines (package version, supported features, ...) @@ -49,8 +45,47 @@ impl ParseCallbacks for LibcoapDefineParser { } } +#[derive(Debug, Default)] +struct LibcoapBindingHelper; + +impl ParseCallbacks for LibcoapBindingHelper { + // Even if we don't use CargoCallbacks, we want to rebuild if relevant environment variables + // change. + fn read_env_var(&self, key: &str) { + println!("cargo:rerun-if-env-changed={key}") + } + + fn blocklisted_type_implements_trait(&self, name: &str, derive_trait: DeriveTrait) -> Option { + // This is based on what libc reports for Unix-based OSes + #[cfg(unix)] + match (name, derive_trait) { + ( + "struct epoll_event" | "fd_set" | "struct sockaddr_in" | "struct sockaddr_in6" | "struct sockaddr", + DeriveTrait::Debug | DeriveTrait::Hash | DeriveTrait::PartialEqOrPartialOrd | DeriveTrait::Copy, + ) => Some(ImplementsTrait::Yes), + ( + "struct epoll_event" | "fd_set" | "struct sockaddr_in" | "struct sockaddr_in6" | "struct sockaddr", + DeriveTrait::Default, + ) => Some(ImplementsTrait::No), + ( + "time_t" | "socklen_t" | "sa_family_t", + DeriveTrait::Debug + | DeriveTrait::Hash + | DeriveTrait::PartialEqOrPartialOrd + | DeriveTrait::Copy + | DeriveTrait::Default, + ) => Some(ImplementsTrait::Yes), + (_, _) => None, + } + #[cfg(not(unix))] + // Let's just assume that bindgen's default behavior is fine. + None + } +} + pub fn generate_libcoap_bindings( bindgen_builder_configurator: impl FnOnce(bindgen::Builder) -> Result, + rerun_on_header_file_changes: bool, ) -> Result { let source_root = PathBuf::from( std::env::var_os("CARGO_MANIFEST_DIR") @@ -65,11 +100,9 @@ pub fn generate_libcoap_bindings( .context("unable to convert header path to &str")? .to_string(), ) - .default_enum_style(EnumVariation::Rust { non_exhaustive: true }) - // Causes invalid syntax for some reason, so we have to disable it. + .parse_callbacks(Box::new(LibcoapBindingHelper)) .generate_comments(true) .generate_cstr(true) - .dynamic_link_require_all(true) .allowlist_function("(oscore|coap)_.*") .allowlist_type("(oscore|coap)_.*") .allowlist_var("(oscore|coap)_.*") @@ -92,6 +125,17 @@ pub fn generate_libcoap_bindings( // this problem. .blocklist_type("__(u)?int(8|16|32|64|128)_t") .size_t_is_usize(true); + + // The `rerun_on_header_files()` method only applies to the top level header (in our case + // src/wrapper.h, so we must not add CargoCallbacks at all if we want to get our desired + // effect). + // To still handle environment variable changes properly, LibcoapBindingHeader already includes + // the relevant parts of CargoCallbacks. + if rerun_on_header_file_changes { + builder = builder.parse_callbacks(Box::new( + bindgen::CargoCallbacks::new().rerun_on_header_files(rerun_on_header_file_changes), + )) + } builder = bindgen_builder_configurator(builder)?; builder.generate().context("unable to generate bindings") diff --git a/libcoap-sys/build/build_system/manual.rs b/libcoap-sys/build/build_system/manual.rs index b531e0a9..48cf6ceb 100644 --- a/libcoap-sys/build/build_system/manual.rs +++ b/libcoap-sys/build/build_system/manual.rs @@ -102,17 +102,20 @@ impl BuildSystem for ManualBuildSystem { fn generate_bindings(&mut self) -> anyhow::Result { let (define_info, define_parser) = LibcoapDefineParser::new(); - let bindings = generate_libcoap_bindings(|builder| { - Ok(builder - .parse_callbacks(Box::new(define_parser)) - // If the pkg-config provided include path coincides with a system include directory, - // setting the "-I{}" command line argument will not do anything, potentially resulting - // in clang using different CoAP headers than provided by pkg-config, e.g., if there - // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. - // Therefore, we use `-isystem` instead. - // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir - .clang_args(self.include_dirs.iter().map(|v| format!("-isystem{}", v.display())))) - })?; + let bindings = generate_libcoap_bindings( + |builder| { + Ok(builder + .parse_callbacks(Box::new(define_parser)) + // If the pkg-config provided include path coincides with a system include directory, + // setting the "-I{}" command line argument will not do anything, potentially resulting + // in clang using different CoAP headers than provided by pkg-config, e.g., if there + // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. + // Therefore, we use `-isystem` instead. + // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir + .clang_args(self.include_dirs.iter().map(|v| format!("-isystem{}", v.display())))) + }, + true, + )?; self.define_info = Some(RefCell::take(&define_info)); diff --git a/libcoap-sys/build/build_system/pkgconfig.rs b/libcoap-sys/build/build_system/pkgconfig.rs index 1b0fba6e..b7b4fe0b 100644 --- a/libcoap-sys/build/build_system/pkgconfig.rs +++ b/libcoap-sys/build/build_system/pkgconfig.rs @@ -61,22 +61,25 @@ impl BuildSystem for PkgConfigBuildSystem { fn generate_bindings(&mut self) -> anyhow::Result { let (define_info, define_parser) = LibcoapDefineParser::new(); - let bindings = generate_libcoap_bindings(|builder| { - Ok(builder - .parse_callbacks(Box::new(define_parser)) - // If the pkg-config provided include path coincides with a system include directory, - // setting the "-I{}" command line argument will not do anything, potentially resulting - // in clang using different CoAP headers than provided by pkg-config, e.g., if there - // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. - // Therefore, we use `-isystem` instead. - // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir - .clang_args( - self.library - .include_paths - .iter() - .map(|v| format!("-isystem{}", v.display())), - )) - })?; + let bindings = generate_libcoap_bindings( + |builder| { + Ok(builder + .parse_callbacks(Box::new(define_parser)) + // If the pkg-config provided include path coincides with a system include directory, + // setting the "-I{}" command line argument will not do anything, potentially resulting + // in clang using different CoAP headers than provided by pkg-config, e.g., if there + // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. + // Therefore, we use `-isystem` instead. + // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir + .clang_args( + self.library + .include_paths + .iter() + .map(|v| format!("-isystem{}", v.display())), + )) + }, + true, + )?; self.define_info = Some(RefCell::take(&define_info)); diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index 2fc8ee18..14713973 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -385,17 +385,30 @@ impl BuildSystem for VendoredBuildSystem { fn generate_bindings(&mut self) -> anyhow::Result { let (define_info, define_parser) = LibcoapDefineParser::new(); - let bindings = generate_libcoap_bindings(|builder| { - Ok(builder - .parse_callbacks(Box::new(define_parser)) - // If the pkg-config provided include path coincides with a system include directory, - // setting the "-I{}" command line argument will not do anything, potentially resulting - // in clang using different CoAP headers than provided by pkg-config, e.g., if there - // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. - // Therefore, we use `-isystem` instead. - // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir - .clang_args(self.include_paths.iter().map(|v| format!("-isystem{}", v.display())))) - })?; + let bindings = generate_libcoap_bindings( + |builder| { + Ok(builder + .parse_callbacks(Box::new(define_parser)) + // If the pkg-config provided include path coincides with a system include directory, + // setting the "-I{}" command line argument will not do anything, potentially resulting + // in clang using different CoAP headers than provided by pkg-config, e.g., if there + // is an old libcoap in /usr/local/include, but the desired one has its headers in /usr/include. + // Therefore, we use `-isystem` instead. + // See also: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-I-dir + .clang_args(self.include_paths.iter().map(|v| format!("-isystem{}", v.display())))) + }, + // Do not run on header file changes, as this will cause cargo to __always__ rebuild, + // no matter what. + // This is probably related to the fact that the headers are only generated during + // build. + // Without changes to the actual source code in src, crate features or environment + // variables that influence this build script (which are already covered by + // cargo:rerun-if-env-changed), the headers will never change and necessitate a rebuild, + // so this should be fine. + false, + )?; + // Just to make sure we do actually run if the libcoap source code changed. + println!("cargo:rerun-if-changed=src/libcoap"); self.define_info = Some(RefCell::take(&define_info)); diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 6507a234..783439cd 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -85,14 +85,24 @@ #![allow(non_camel_case_types)] #![allow(deref_nullptr)] #![allow(non_snake_case)] +#![allow(non_upper_case_globals)] -use std::ffi::c_void; +use core::ffi::c_void; -#[allow(unused_imports)] +/// Re-export of the crate that provides libc data types used by libcoap. +/// +/// In most cases, this will be libc, but on the ESP-IDF, it will be esp_idf_sys. +#[cfg(target_os = "espidf")] +pub use esp_idf_sys as c_stdlib; +/// Re-export of the crate that provides libc data types used by libcoap. +/// +/// In most cases, this will be libc, but on the ESP-IDF, it will be esp_idf_sys. #[cfg(not(target_os = "espidf"))] -use libc::epoll_event; -#[allow(unused_imports)] -use libc::{fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t}; +pub use libc as c_stdlib; + + +use c_stdlib::{epoll_event, fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t}; + // use dtls backend libraries in cases where they set our linker flags, otherwise rustc will // optimize them out, resulting in missing symbols. #[allow(unused_imports)] @@ -146,7 +156,7 @@ pub unsafe fn coap_string_equal_internal( && (str1_len == 0 || !str1_ptr.is_null() && !str2_ptr.is_null() - && memcmp(str1_ptr as *const c_void, str2_ptr as *const c_void, str1_len) == 0) + && memcmp(str1_ptr as *const c_void, str2_ptr as *const c_void, str1_len as _) == 0) } /// Execute feature check function and panic with an error message if the feature is not supported. @@ -173,12 +183,13 @@ macro_rules! feature_check { macro_rules! dtls_backend_string { ($backend:ident) => { match $backend { - coap_tls_library_t::COAP_TLS_LIBRARY_OPENSSL => "openssl", - coap_tls_library_t::COAP_TLS_LIBRARY_GNUTLS => "gnutls", - coap_tls_library_t::COAP_TLS_LIBRARY_MBEDTLS => "mbedtls", - coap_tls_library_t::COAP_TLS_LIBRARY_TINYDTLS => "tinydtls", - coap_tls_library_t::COAP_TLS_LIBRARY_WOLFSSL => "wolfssl", - coap_tls_library_t::COAP_TLS_LIBRARY_NOTLS => "notls", + coap_tls_library_t_COAP_TLS_LIBRARY_OPENSSL => "openssl", + coap_tls_library_t_COAP_TLS_LIBRARY_GNUTLS => "gnutls", + coap_tls_library_t_COAP_TLS_LIBRARY_MBEDTLS => "mbedtls", + coap_tls_library_t_COAP_TLS_LIBRARY_TINYDTLS => "tinydtls", + coap_tls_library_t_COAP_TLS_LIBRARY_WOLFSSL => "wolfssl", + coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS => "notls", + _ => "unknown", } }; } @@ -242,26 +253,30 @@ pub fn coap_startup_with_feature_checks() { ); } - let presumed_dtls_backend = if cfg!(dtls_backend = "openssl") { - coap_tls_library_t::COAP_TLS_LIBRARY_OPENSSL - } else if cfg!(dtls_backend = "gnutls") { - coap_tls_library_t::COAP_TLS_LIBRARY_GNUTLS - } else if cfg!(dtls_backend = "mbedtls") { - coap_tls_library_t::COAP_TLS_LIBRARY_MBEDTLS - } else if cfg!(dtls_backend = "tinydtls") { - coap_tls_library_t::COAP_TLS_LIBRARY_TINYDTLS - } else if cfg!(dtls_backend = "wolfssl") { - coap_tls_library_t::COAP_TLS_LIBRARY_WOLFSSL - } else { - coap_tls_library_t::COAP_TLS_LIBRARY_NOTLS - }; - - let actual_dtls_backend = unsafe { *coap_get_tls_library_version() }.type_; - - if presumed_dtls_backend != coap_tls_library_t::COAP_TLS_LIBRARY_NOTLS - && actual_dtls_backend != presumed_dtls_backend + // ESP-IDF is missing the coap_tls_library_t type. + #[cfg(not(target_os = "espidf"))] { - panic_wrong_dtls!(presumed_dtls_backend, actual_dtls_backend); + let presumed_dtls_backend = if cfg!(dtls_backend = "openssl") { + coap_tls_library_t_COAP_TLS_LIBRARY_OPENSSL + } else if cfg!(dtls_backend = "gnutls") { + coap_tls_library_t_COAP_TLS_LIBRARY_GNUTLS + } else if cfg!(dtls_backend = "mbedtls") { + coap_tls_library_t_COAP_TLS_LIBRARY_MBEDTLS + } else if cfg!(dtls_backend = "tinydtls") { + coap_tls_library_t_COAP_TLS_LIBRARY_TINYDTLS + } else if cfg!(dtls_backend = "wolfssl") { + coap_tls_library_t_COAP_TLS_LIBRARY_WOLFSSL + } else { + coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS + }; + + let actual_dtls_backend = unsafe { *coap_get_tls_library_version() }.type_; + + if presumed_dtls_backend != coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS + && actual_dtls_backend != presumed_dtls_backend + { + panic_wrong_dtls!(presumed_dtls_backend, actual_dtls_backend); + } } // SAFETY: Function is always safe to call. @@ -280,12 +295,6 @@ mod tests { use libc::{in6_addr, in_addr, sa_family_t, size_t, AF_INET, AF_INET6}; use super::*; - use crate::{ - coap_pdu_code_t::{COAP_REQUEST_CODE_GET, COAP_RESPONSE_CODE_CONTENT}, - coap_proto_t::COAP_PROTO_UDP, - coap_request_t::COAP_REQUEST_GET, - coap_response_t::COAP_RESPONSE_OK, - }; const COAP_TEST_RESOURCE_URI: &str = "test"; const COAP_TEST_RESOURCE_RESPONSE: &str = "Hello World!"; @@ -302,7 +311,7 @@ mod tests { size: std::mem::size_of::() as socklen_t, addr: std::mem::zeroed(), }; - *coap_addr.addr.sin.as_mut() = sockaddr_in { + coap_addr.addr.sin = sockaddr_in { sin_family: AF_INET as sa_family_t, sin_port: addr.port().to_be(), sin_addr: in_addr { @@ -322,7 +331,7 @@ mod tests { size: std::mem::size_of::() as socklen_t, addr: std::mem::zeroed(), }; - *coap_addr.addr.sin6.as_mut() = sockaddr_in6 { + coap_addr.addr.sin6 = sockaddr_in6 { sin6_family: AF_INET6 as sa_family_t, sin6_port: addr.port().to_be(), sin6_addr: in6_addr { @@ -364,7 +373,7 @@ mod tests { COAP_TEST_RESOURCE_RESPONSE.as_ptr(), ); coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void); - coap_pdu_set_code(response_pdu, COAP_RESPONSE_CODE_CONTENT); + coap_pdu_set_code(response_pdu, coap_pdu_code_t_COAP_RESPONSE_CODE_CONTENT); } /// Response handler for the CoAP client/server test (client-side) @@ -380,7 +389,7 @@ mod tests { received: *const coap_pdu_t, _mid: coap_mid_t, ) -> coap_response_t { - assert_eq!(coap_pdu_get_code(received), COAP_RESPONSE_CODE_CONTENT); + assert_eq!(coap_pdu_get_code(received), coap_pdu_code_t_COAP_RESPONSE_CODE_CONTENT); let mut len: size_t = 0; let mut data: *const u8 = std::ptr::null(); assert_ne!(coap_get_data(received, &mut len, &mut data), 0); @@ -388,7 +397,7 @@ mod tests { assert_eq!(data, COAP_TEST_RESOURCE_RESPONSE.as_bytes()); coap_set_app_data(coap_session_get_context(session), (&true) as *const bool as *mut c_void); - return COAP_RESPONSE_OK; + return coap_response_t_COAP_RESPONSE_OK; } /// Creates a CoAP server that provides a single resource under COAP_TEST_RESOURCE_URI over the @@ -401,7 +410,7 @@ mod tests { let address: coap_address_t = coap_address_from_socketaddr(addr); // SAFETY: We asserted that context != null, listen_addr is a reference and can therefore not be null. - let endpoint = unsafe { coap_new_endpoint(context, &address, coap_proto_t::COAP_PROTO_UDP) }; + let endpoint = unsafe { coap_new_endpoint(context, &address, coap_proto_t_COAP_PROTO_UDP) }; assert!(!endpoint.is_null()); // SAFETY: Since we use a string constant here, the arguments to the function are all valid. @@ -418,7 +427,11 @@ mod tests { // struct allows for mutable pointers to be set there, so that applications can use this to // modify some application specific state. unsafe { - coap_register_request_handler(test_resource, COAP_REQUEST_GET, Some(test_resource_handler)); + coap_register_request_handler( + test_resource, + coap_request_t_COAP_REQUEST_GET, + Some(test_resource_handler), + ); coap_add_resource(context, test_resource); coap_set_app_data(context, (&false) as *const bool as *mut c_void); } @@ -481,7 +494,7 @@ mod tests { // SAFETY: null pointer is valid argument for local_if, server_address is guaranteed to be // a correct value (conversion cannot fail), validity of context was asserted before. let client_session = - unsafe { coap_new_client_session(context, std::ptr::null(), &server_address, COAP_PROTO_UDP) }; + unsafe { coap_new_client_session(context, std::ptr::null(), &server_address, coap_proto_t_COAP_PROTO_UDP) }; // SAFETY: context and client_session were asserted to be valid. // Casting *const to *mut is fine because we don't mutate the value pointed to and the @@ -492,8 +505,11 @@ mod tests { unsafe { coap_register_response_handler(context, Some(test_response_handler)); coap_set_app_data(context, (&false) as *const bool as *mut c_void); - let coap_request_pdu = - coap_new_pdu(coap_pdu_type_t::COAP_MESSAGE_NON, COAP_REQUEST_CODE_GET, client_session); + let coap_request_pdu = coap_new_pdu( + coap_pdu_type_t_COAP_MESSAGE_NON, + coap_pdu_code_t_COAP_REQUEST_CODE_GET, + client_session, + ); assert!(!coap_request_pdu.is_null()); assert_ne!( coap_add_option( diff --git a/libcoap/Cargo.toml b/libcoap/Cargo.toml index 377622d8..dbee783e 100644 --- a/libcoap/Cargo.toml +++ b/libcoap/Cargo.toml @@ -20,7 +20,7 @@ resolver = "2" rust-version = "1.81.0" [features] -default = ["dtls-psk", "tcp", "vendored", "libcoap-sys/default"] +default = ["dtls-psk", "tcp"] dtls = ["libcoap-sys/dtls"] dtls-psk = ["dtls", "libcoap-sys/dtls-psk"] dtls-pki = ["dtls", "libcoap-sys/dtls-pki"] @@ -38,7 +38,6 @@ dtls-tinydtls-sys-vendored = ["libcoap-sys/dtls-tinydtls-sys-vendored"] [dependencies] libcoap-sys = { version = "^0.2.2", path = "../libcoap-sys", default-features = false, features = ["client", "server"] } -libc = { version = "^0.2.95" } num-derive = { version = "^0.3.3" } num-traits = { version = "^0.2.14" } url = { version = "^2.2", optional = true } diff --git a/libcoap/src/context.rs b/libcoap/src/context.rs index 0f35f390..b4b26b92 100644 --- a/libcoap/src/context.rs +++ b/libcoap/src/context.rs @@ -17,7 +17,8 @@ use std::{any::Any, ffi::c_void, fmt::Debug, net::SocketAddr, ops::Sub, sync::On #[cfg(all(feature = "dtls-pki", unix))] use std::{os::unix::ffi::OsStrExt, path::Path}; -use libc::c_uint; +use core::ffi::c_uint; + #[cfg(feature = "dtls-pki")] use libcoap_sys::coap_context_set_pki_root_cas; use libcoap_sys::{ @@ -25,9 +26,22 @@ use libcoap_sys::{ coap_context_get_max_handshake_sessions, coap_context_get_max_idle_sessions, coap_context_get_session_timeout, coap_context_set_block_mode, coap_context_set_csm_max_message_size, coap_context_set_csm_timeout, coap_context_set_keepalive, coap_context_set_max_handshake_sessions, coap_context_set_max_idle_sessions, - coap_context_set_session_timeout, coap_context_t, coap_event_t, coap_free_context, coap_get_app_data, - coap_io_process, coap_new_context, coap_proto_t, coap_register_event_handler, coap_register_response_handler, - coap_set_app_data, coap_startup_with_feature_checks, COAP_BLOCK_SINGLE_BODY, COAP_BLOCK_USE_LIBCOAP, COAP_IO_WAIT, + coap_context_set_session_timeout, coap_context_t, coap_event_t, coap_event_t_COAP_EVENT_BAD_PACKET, + coap_event_t_COAP_EVENT_DTLS_CLOSED, coap_event_t_COAP_EVENT_DTLS_CONNECTED, coap_event_t_COAP_EVENT_DTLS_ERROR, + coap_event_t_COAP_EVENT_DTLS_RENEGOTIATE, coap_event_t_COAP_EVENT_KEEPALIVE_FAILURE, + coap_event_t_COAP_EVENT_MSG_RETRANSMITTED, coap_event_t_COAP_EVENT_OSCORE_DECODE_ERROR, + coap_event_t_COAP_EVENT_OSCORE_DECRYPTION_FAILURE, coap_event_t_COAP_EVENT_OSCORE_INTERNAL_ERROR, + coap_event_t_COAP_EVENT_OSCORE_NOT_ENABLED, coap_event_t_COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD, + coap_event_t_COAP_EVENT_OSCORE_NO_SECURITY, coap_event_t_COAP_EVENT_PARTIAL_BLOCK, + coap_event_t_COAP_EVENT_SERVER_SESSION_DEL, coap_event_t_COAP_EVENT_SERVER_SESSION_NEW, + coap_event_t_COAP_EVENT_SESSION_CLOSED, coap_event_t_COAP_EVENT_SESSION_CONNECTED, + coap_event_t_COAP_EVENT_SESSION_FAILED, coap_event_t_COAP_EVENT_TCP_CLOSED, coap_event_t_COAP_EVENT_TCP_CONNECTED, + coap_event_t_COAP_EVENT_TCP_FAILED, coap_event_t_COAP_EVENT_WS_CLOSED, coap_event_t_COAP_EVENT_WS_CONNECTED, + coap_event_t_COAP_EVENT_WS_PACKET_SIZE, coap_event_t_COAP_EVENT_XMIT_BLOCK_FAIL, coap_free_context, + coap_get_app_data, coap_io_process, coap_new_context, coap_proto_t, coap_proto_t_COAP_PROTO_DTLS, + coap_proto_t_COAP_PROTO_TCP, coap_proto_t_COAP_PROTO_UDP, coap_register_event_handler, + coap_register_response_handler, coap_set_app_data, coap_startup_with_feature_checks, COAP_BLOCK_SINGLE_BODY, + COAP_BLOCK_USE_LIBCOAP, COAP_IO_WAIT, }; #[cfg(any(feature = "dtls-rpk", feature = "dtls-pki"))] @@ -148,49 +162,52 @@ impl<'a> CoapContext<'a> { let inner_ref = &mut *self.inner.borrow_mut(); // Call event handler for event. if let Some(handler) = &mut inner_ref.event_handler { + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] match event { - coap_event_t::COAP_EVENT_DTLS_CLOSED => handler.handle_dtls_closed(&mut session), - coap_event_t::COAP_EVENT_DTLS_CONNECTED => handler.handle_dtls_connected(&mut session), - coap_event_t::COAP_EVENT_DTLS_RENEGOTIATE => handler.handle_dtls_renegotiate(&mut session), - coap_event_t::COAP_EVENT_DTLS_ERROR => handler.handle_dtls_error(&mut session), - coap_event_t::COAP_EVENT_TCP_CONNECTED => handler.handle_tcp_connected(&mut session), - coap_event_t::COAP_EVENT_TCP_CLOSED => handler.handle_tcp_closed(&mut session), - coap_event_t::COAP_EVENT_TCP_FAILED => handler.handle_tcp_failed(&mut session), - coap_event_t::COAP_EVENT_SESSION_CONNECTED => handler.handle_session_connected(&mut session), - coap_event_t::COAP_EVENT_SESSION_CLOSED => handler.handle_session_closed(&mut session), - coap_event_t::COAP_EVENT_SESSION_FAILED => handler.handle_session_failed(&mut session), - coap_event_t::COAP_EVENT_PARTIAL_BLOCK => handler.handle_partial_block(&mut session), - coap_event_t::COAP_EVENT_SERVER_SESSION_NEW => { + coap_event_t_COAP_EVENT_DTLS_CLOSED => handler.handle_dtls_closed(&mut session), + coap_event_t_COAP_EVENT_DTLS_CONNECTED => handler.handle_dtls_connected(&mut session), + coap_event_t_COAP_EVENT_DTLS_RENEGOTIATE => handler.handle_dtls_renegotiate(&mut session), + coap_event_t_COAP_EVENT_DTLS_ERROR => handler.handle_dtls_error(&mut session), + coap_event_t_COAP_EVENT_TCP_CONNECTED => handler.handle_tcp_connected(&mut session), + coap_event_t_COAP_EVENT_TCP_CLOSED => handler.handle_tcp_closed(&mut session), + coap_event_t_COAP_EVENT_TCP_FAILED => handler.handle_tcp_failed(&mut session), + coap_event_t_COAP_EVENT_SESSION_CONNECTED => handler.handle_session_connected(&mut session), + coap_event_t_COAP_EVENT_SESSION_CLOSED => handler.handle_session_closed(&mut session), + coap_event_t_COAP_EVENT_SESSION_FAILED => handler.handle_session_failed(&mut session), + coap_event_t_COAP_EVENT_PARTIAL_BLOCK => handler.handle_partial_block(&mut session), + coap_event_t_COAP_EVENT_SERVER_SESSION_NEW => { if let CoapSession::Server(server_session) = &mut session { handler.handle_server_session_new(server_session) } else { panic!("server-side session event fired for non-server-side session"); } }, - coap_event_t::COAP_EVENT_SERVER_SESSION_DEL => { + coap_event_t_COAP_EVENT_SERVER_SESSION_DEL => { if let CoapSession::Server(server_session) = &mut session { handler.handle_server_session_del(server_session) } else { panic!("server-side session event fired for non-server-side session"); } }, - coap_event_t::COAP_EVENT_XMIT_BLOCK_FAIL => handler.handle_xmit_block_fail(&mut session), - coap_event_t::COAP_EVENT_BAD_PACKET => handler.handle_bad_packet(&mut session), - coap_event_t::COAP_EVENT_MSG_RETRANSMITTED => handler.handle_msg_retransmitted(&mut session), - coap_event_t::COAP_EVENT_OSCORE_DECRYPTION_FAILURE => { + coap_event_t_COAP_EVENT_XMIT_BLOCK_FAIL => handler.handle_xmit_block_fail(&mut session), + coap_event_t_COAP_EVENT_BAD_PACKET => handler.handle_bad_packet(&mut session), + coap_event_t_COAP_EVENT_MSG_RETRANSMITTED => handler.handle_msg_retransmitted(&mut session), + coap_event_t_COAP_EVENT_OSCORE_DECRYPTION_FAILURE => { handler.handle_oscore_decryption_failure(&mut session) }, - coap_event_t::COAP_EVENT_OSCORE_NOT_ENABLED => handler.handle_oscore_not_enabled(&mut session), - coap_event_t::COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD => { + coap_event_t_COAP_EVENT_OSCORE_NOT_ENABLED => handler.handle_oscore_not_enabled(&mut session), + coap_event_t_COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD => { handler.handle_oscore_no_protected_payload(&mut session) }, - coap_event_t::COAP_EVENT_OSCORE_NO_SECURITY => handler.handle_oscore_no_security(&mut session), - coap_event_t::COAP_EVENT_OSCORE_INTERNAL_ERROR => handler.handle_oscore_internal_error(&mut session), - coap_event_t::COAP_EVENT_OSCORE_DECODE_ERROR => handler.handle_oscore_decode_error(&mut session), - coap_event_t::COAP_EVENT_WS_PACKET_SIZE => handler.handle_ws_packet_size(&mut session), - coap_event_t::COAP_EVENT_WS_CONNECTED => handler.handle_ws_connected(&mut session), - coap_event_t::COAP_EVENT_WS_CLOSED => handler.handle_ws_closed(&mut session), - coap_event_t::COAP_EVENT_KEEPALIVE_FAILURE => handler.handle_keepalive_failure(&mut session), + coap_event_t_COAP_EVENT_OSCORE_NO_SECURITY => handler.handle_oscore_no_security(&mut session), + coap_event_t_COAP_EVENT_OSCORE_INTERNAL_ERROR => handler.handle_oscore_internal_error(&mut session), + coap_event_t_COAP_EVENT_OSCORE_DECODE_ERROR => handler.handle_oscore_decode_error(&mut session), + coap_event_t_COAP_EVENT_WS_PACKET_SIZE => handler.handle_ws_packet_size(&mut session), + coap_event_t_COAP_EVENT_WS_CONNECTED => handler.handle_ws_connected(&mut session), + coap_event_t_COAP_EVENT_WS_CLOSED => handler.handle_ws_closed(&mut session), + coap_event_t_COAP_EVENT_KEEPALIVE_FAILURE => handler.handle_keepalive_failure(&mut session), _ => { // TODO probably a log message is justified here. }, @@ -198,9 +215,12 @@ impl<'a> CoapContext<'a> { } // For server-side sessions: Ensure that server-side session wrappers are either kept in memory or dropped when needed. if let CoapSession::Server(serv_sess) = session { + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] match event { - coap_event_t::COAP_EVENT_SERVER_SESSION_NEW => inner_ref.server_sessions.push(serv_sess), - coap_event_t::COAP_EVENT_SERVER_SESSION_DEL => { + coap_event_t_COAP_EVENT_SERVER_SESSION_NEW => inner_ref.server_sessions.push(serv_sess), + coap_event_t_COAP_EVENT_SERVER_SESSION_DEL => { std::mem::drop(inner_ref.server_sessions.remove( inner_ref.server_sessions.iter().position(|v| v.eq(&serv_sess)).expect( "attempted to remove session wrapper from context that was never associated with it", @@ -369,13 +389,13 @@ impl CoapContext<'_> { /// Creates a new UDP endpoint that is bound to the given address. pub fn add_endpoint_udp(&mut self, addr: SocketAddr) -> Result<(), EndpointCreationError> { - self.add_endpoint(addr, coap_proto_t::COAP_PROTO_UDP) + self.add_endpoint(addr, coap_proto_t_COAP_PROTO_UDP) } /// Creates a new TCP endpoint that is bound to the given address. #[cfg(feature = "tcp")] pub fn add_endpoint_tcp(&mut self, addr: SocketAddr) -> Result<(), EndpointCreationError> { - self.add_endpoint(addr, coap_proto_t::COAP_PROTO_TCP) + self.add_endpoint(addr, coap_proto_t_COAP_PROTO_TCP) } /// Creates a new DTLS endpoint that is bound to the given address. @@ -384,14 +404,14 @@ impl CoapContext<'_> { /// using [CoapContext::set_psk_context] and/or [CoapContext::set_pki_rpk_context]. #[cfg(feature = "dtls")] pub fn add_endpoint_dtls(&mut self, addr: SocketAddr) -> Result<(), EndpointCreationError> { - self.add_endpoint(addr, coap_proto_t::COAP_PROTO_DTLS) + self.add_endpoint(addr, coap_proto_t_COAP_PROTO_DTLS) } // /// TODO // #[cfg(all(feature = "tcp", dtls))] // pub fn add_endpoint_tls(&mut self, _addr: SocketAddr) -> Result<(), EndpointCreationError> { // todo!() - // // TODO: self.add_endpoint(addr, coap_proto_t::COAP_PROTO_TLS) + // // TODO: self.add_endpoint(addr, coap_proto_t_COAP_PROTO_TLS) // } /// Adds the given resource to the resource pool of this context. diff --git a/libcoap/src/crypto/pki_rpk/key.rs b/libcoap/src/crypto/pki_rpk/key.rs index 036200d5..5f8f9a6f 100644 --- a/libcoap/src/crypto/pki_rpk/key.rs +++ b/libcoap/src/crypto/pki_rpk/key.rs @@ -8,7 +8,15 @@ */ use libcoap_sys::{ - coap_asn1_privatekey_type_t, coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_pki_t, coap_pki_define_t, + coap_asn1_privatekey_type_t, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_CMAC, + coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DH, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DHX, + coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA1, + coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA2, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA3, + coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA4, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_EC, + coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_HKDF, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_HMAC, + coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_NONE, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_RSA, + coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_RSA2, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_TLS1_PRF, + coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_pki_t, coap_pki_define_t, }; use num_derive::FromPrimitive; use num_traits::FromPrimitive; @@ -210,41 +218,41 @@ impl> From for EngineKeyComponent { #[derive(Copy, Clone, FromPrimitive, Debug, PartialEq, Eq, Hash, Default)] pub enum Asn1PrivateKeyType { #[default] - None = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_NONE as isize, - Rsa = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_RSA as isize, - Rsa2 = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_RSA2 as isize, - Dsa = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA as isize, - Dsa1 = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA1 as isize, - Dsa2 = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA2 as isize, - Dsa3 = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA3 as isize, - Dsa4 = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA4 as isize, - Dh = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DH as isize, - Dhx = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DHX as isize, - Ec = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_EC as isize, - Hmac = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_HMAC as isize, - Cmac = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_CMAC as isize, - Tls1Prf = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_TLS1_PRF as isize, - Hkdf = coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_HKDF as isize, + None = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_NONE as isize, + Rsa = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_RSA as isize, + Rsa2 = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_RSA2 as isize, + Dsa = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA as isize, + Dsa1 = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA1 as isize, + Dsa2 = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA2 as isize, + Dsa3 = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA3 as isize, + Dsa4 = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA4 as isize, + Dh = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DH as isize, + Dhx = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DHX as isize, + Ec = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_EC as isize, + Hmac = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_HMAC as isize, + Cmac = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_CMAC as isize, + Tls1Prf = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_TLS1_PRF as isize, + Hkdf = coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_HKDF as isize, } impl From for coap_asn1_privatekey_type_t { fn from(value: Asn1PrivateKeyType) -> Self { match value { - Asn1PrivateKeyType::None => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_NONE, - Asn1PrivateKeyType::Rsa => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_RSA, - Asn1PrivateKeyType::Rsa2 => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_RSA2, - Asn1PrivateKeyType::Dsa => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA, - Asn1PrivateKeyType::Dsa1 => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA1, - Asn1PrivateKeyType::Dsa2 => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA2, - Asn1PrivateKeyType::Dsa3 => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA3, - Asn1PrivateKeyType::Dsa4 => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DSA4, - Asn1PrivateKeyType::Dh => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DH, - Asn1PrivateKeyType::Dhx => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_DHX, - Asn1PrivateKeyType::Ec => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_EC, - Asn1PrivateKeyType::Hmac => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_HMAC, - Asn1PrivateKeyType::Cmac => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_CMAC, - Asn1PrivateKeyType::Tls1Prf => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_TLS1_PRF, - Asn1PrivateKeyType::Hkdf => coap_asn1_privatekey_type_t::COAP_ASN1_PKEY_HKDF, + Asn1PrivateKeyType::None => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_NONE, + Asn1PrivateKeyType::Rsa => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_RSA, + Asn1PrivateKeyType::Rsa2 => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_RSA2, + Asn1PrivateKeyType::Dsa => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA, + Asn1PrivateKeyType::Dsa1 => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA1, + Asn1PrivateKeyType::Dsa2 => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA2, + Asn1PrivateKeyType::Dsa3 => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA3, + Asn1PrivateKeyType::Dsa4 => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DSA4, + Asn1PrivateKeyType::Dh => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DH, + Asn1PrivateKeyType::Dhx => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DHX, + Asn1PrivateKeyType::Ec => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_EC, + Asn1PrivateKeyType::Hmac => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_HMAC, + Asn1PrivateKeyType::Cmac => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_CMAC, + Asn1PrivateKeyType::Tls1Prf => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_TLS1_PRF, + Asn1PrivateKeyType::Hkdf => coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_HKDF, } } } diff --git a/libcoap/src/crypto/pki_rpk/pki.rs b/libcoap/src/crypto/pki_rpk/pki.rs index bebf05f7..bc441de1 100644 --- a/libcoap/src/crypto/pki_rpk/pki.rs +++ b/libcoap/src/crypto/pki_rpk/pki.rs @@ -18,7 +18,10 @@ use crate::crypto::ClientCryptoContext; use crate::session::CoapSession; use libcoap_sys::{ coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_key_t__bindgen_ty_1, coap_dtls_pki_t, coap_pki_define_t, - coap_pki_key_define_t, coap_pki_key_t, + coap_pki_define_t_COAP_PKI_KEY_DEF_DER, coap_pki_define_t_COAP_PKI_KEY_DEF_DER_BUF, + coap_pki_define_t_COAP_PKI_KEY_DEF_ENGINE, coap_pki_define_t_COAP_PKI_KEY_DEF_PEM, + coap_pki_define_t_COAP_PKI_KEY_DEF_PEM_BUF, coap_pki_define_t_COAP_PKI_KEY_DEF_PKCS11, coap_pki_key_define_t + , coap_pki_key_t_COAP_PKI_KEY_DEFINE, }; use std::ffi::{c_uint, CStr, CString}; use std::fmt::Debug; @@ -314,7 +317,7 @@ impl, PK: KeyComponent, SK: KeyComponent> KeyDef let (private_key, private_key_len) = self.private_key.as_raw_key_component(); coap_dtls_key_t { - key_type: coap_pki_key_t::COAP_PKI_KEY_DEFINE, + key_type: coap_pki_key_t_COAP_PKI_KEY_DEFINE, key: coap_dtls_key_t__bindgen_ty_1 { define: coap_pki_key_define_t { ca, @@ -339,25 +342,25 @@ impl, PK: KeyComponent, SK: KeyComponent> KeyDef } impl KeyComponentSealed for PemFileKeyComponent { - const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_PEM; + const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t_COAP_PKI_KEY_DEF_PEM; } impl KeyComponentSealed for PemMemoryKeyComponent { - const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_PEM_BUF; + const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t_COAP_PKI_KEY_DEF_PEM_BUF; } impl KeyComponentSealed for DerFileKeyComponent { - const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_DER; + const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t_COAP_PKI_KEY_DEF_DER; } impl KeyComponentSealed for DerMemoryKeyComponent { - const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_DER_BUF; + const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t_COAP_PKI_KEY_DEF_DER_BUF; } impl KeyComponentSealed for Pkcs11KeyComponent { - const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_PKCS11; + const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t_COAP_PKI_KEY_DEF_PKCS11; } impl KeyComponentSealed for EngineKeyComponent { - const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_ENGINE; + const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t_COAP_PKI_KEY_DEF_ENGINE; } diff --git a/libcoap/src/crypto/pki_rpk/rpk.rs b/libcoap/src/crypto/pki_rpk/rpk.rs index 1642bc75..8693ec64 100644 --- a/libcoap/src/crypto/pki_rpk/rpk.rs +++ b/libcoap/src/crypto/pki_rpk/rpk.rs @@ -15,10 +15,7 @@ use crate::crypto::pki_rpk::{ }; use crate::crypto::ClientCryptoContext; use crate::session::CoapSession; -use libcoap_sys::{ - coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_key_t__bindgen_ty_1, coap_dtls_pki_t, coap_pki_define_t, - coap_pki_key_define_t, coap_pki_key_t, -}; +use libcoap_sys::{coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_key_t__bindgen_ty_1, coap_dtls_pki_t, coap_pki_define_t, coap_pki_define_t_COAP_PKI_KEY_DEF_PEM, coap_pki_define_t_COAP_PKI_KEY_DEF_PKCS11_RPK, coap_pki_define_t_COAP_PKI_KEY_DEF_RPK_BUF, coap_pki_key_define_t, coap_pki_key_t, coap_pki_key_t_COAP_PKI_KEY_DEFINE}; use std::ffi::CString; use std::fmt::Debug; @@ -170,7 +167,7 @@ impl, SK: KeyComponent> KeyDefSealed for RpkKeyDef, SK: KeyComponent> KeyDefSealed for RpkKeyDef>::DEFINE_TYPE, private_key_def: >::DEFINE_TYPE, private_key_type: self.asn1_private_key_type.into(), @@ -197,9 +194,9 @@ impl, SK: KeyComponent> KeyDef for RpkKeyDef } impl KeyComponentSealed for PemMemoryKeyComponent { - const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_RPK_BUF; + const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t_COAP_PKI_KEY_DEF_RPK_BUF; } impl KeyComponentSealed for Pkcs11KeyComponent { - const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t::COAP_PKI_KEY_DEF_PKCS11_RPK; + const DEFINE_TYPE: coap_pki_define_t = coap_pki_define_t_COAP_PKI_KEY_DEF_PKCS11_RPK; } diff --git a/libcoap/src/event.rs b/libcoap/src/event.rs index 07573119..e3a8743d 100644 --- a/libcoap/src/event.rs +++ b/libcoap/src/event.rs @@ -11,8 +11,11 @@ use std::fmt::Debug; -use libcoap_sys::{coap_event_t, coap_session_get_context, coap_session_t}; -use libcoap_sys::{coap_session_get_type, coap_session_type_t}; +use libcoap_sys::{ + coap_event_t, coap_event_t_COAP_EVENT_SERVER_SESSION_NEW, coap_event_t_COAP_EVENT_TCP_CONNECTED, + coap_session_get_context, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_SERVER, +}; +use libcoap_sys::coap_session_get_type; use crate::context::CoapContext; use crate::session::CoapSession; @@ -147,7 +150,6 @@ pub trait CoapEventHandler: Debug { #[allow(unused_variables)] fn handle_oscore_decode_error(&mut self, session: &mut CoapSession) {} - /// Handle an oversized WebSocket packet event. #[allow(unused_variables)] fn handle_ws_packet_size(&mut self, session: &mut CoapSession) {} @@ -171,9 +173,9 @@ pub trait CoapEventHandler: Debug { pub(crate) unsafe extern "C" fn event_handler_callback(raw_session: *mut coap_session_t, event: coap_event_t) -> i32 { let raw_session_type = coap_session_get_type(raw_session); - let session: CoapSession = if event == coap_event_t::COAP_EVENT_SERVER_SESSION_NEW - || (event == coap_event_t::COAP_EVENT_TCP_CONNECTED - && raw_session_type == coap_session_type_t::COAP_SESSION_TYPE_SERVER) + let session: CoapSession = if event == coap_event_t_COAP_EVENT_SERVER_SESSION_NEW + || (event == coap_event_t_COAP_EVENT_TCP_CONNECTED + && raw_session_type == coap_session_type_t_COAP_SESSION_TYPE_SERVER) { CoapServerSession::initialize_raw(raw_session).into() } else { diff --git a/libcoap/src/protocol.rs b/libcoap/src/protocol.rs index 6bb8f926..45d2ff28 100644 --- a/libcoap/src/protocol.rs +++ b/libcoap/src/protocol.rs @@ -18,26 +18,45 @@ use num_derive::FromPrimitive; use num_traits::FromPrimitive; use libcoap_sys::{ + coap_option_num_t, coap_pdu_code_t, coap_pdu_code_t_COAP_EMPTY_CODE, coap_pdu_code_t_COAP_REQUEST_CODE_DELETE, + coap_pdu_code_t_COAP_REQUEST_CODE_FETCH, coap_pdu_code_t_COAP_REQUEST_CODE_GET, + coap_pdu_code_t_COAP_REQUEST_CODE_IPATCH, coap_pdu_code_t_COAP_REQUEST_CODE_PATCH, + coap_pdu_code_t_COAP_REQUEST_CODE_POST, coap_pdu_code_t_COAP_REQUEST_CODE_PUT, + coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_GATEWAY, coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_OPTION, + coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_REQUEST, coap_pdu_code_t_COAP_RESPONSE_CODE_CHANGED, + coap_pdu_code_t_COAP_RESPONSE_CODE_CONFLICT, coap_pdu_code_t_COAP_RESPONSE_CODE_CONTENT, + coap_pdu_code_t_COAP_RESPONSE_CODE_CONTINUE, coap_pdu_code_t_COAP_RESPONSE_CODE_CREATED, + coap_pdu_code_t_COAP_RESPONSE_CODE_DELETED, coap_pdu_code_t_COAP_RESPONSE_CODE_FORBIDDEN, + coap_pdu_code_t_COAP_RESPONSE_CODE_GATEWAY_TIMEOUT, coap_pdu_code_t_COAP_RESPONSE_CODE_HOP_LIMIT_REACHED, + coap_pdu_code_t_COAP_RESPONSE_CODE_INCOMPLETE, coap_pdu_code_t_COAP_RESPONSE_CODE_INTERNAL_ERROR, + coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_ACCEPTABLE, coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_ALLOWED, + coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_FOUND, coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_IMPLEMENTED, + coap_pdu_code_t_COAP_RESPONSE_CODE_PRECONDITION_FAILED, coap_pdu_code_t_COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED, + coap_pdu_code_t_COAP_RESPONSE_CODE_REQUEST_TOO_LARGE, coap_pdu_code_t_COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE, + coap_pdu_code_t_COAP_RESPONSE_CODE_TOO_MANY_REQUESTS, coap_pdu_code_t_COAP_RESPONSE_CODE_UNAUTHORIZED, + coap_pdu_code_t_COAP_RESPONSE_CODE_UNPROCESSABLE, coap_pdu_code_t_COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT, + coap_pdu_code_t_COAP_RESPONSE_CODE_VALID, coap_pdu_type_t, coap_pdu_type_t_COAP_MESSAGE_ACK, + coap_pdu_type_t_COAP_MESSAGE_CON, coap_pdu_type_t_COAP_MESSAGE_NON, coap_pdu_type_t_COAP_MESSAGE_RST, + coap_request_t, coap_request_t_COAP_REQUEST_DELETE, coap_request_t_COAP_REQUEST_FETCH, + coap_request_t_COAP_REQUEST_GET, coap_request_t_COAP_REQUEST_IPATCH, coap_request_t_COAP_REQUEST_PATCH, + coap_request_t_COAP_REQUEST_POST, coap_request_t_COAP_REQUEST_PUT, coap_response_phrase, COAP_MEDIATYPE_APPLICATION_ACE_CBOR, COAP_MEDIATYPE_APPLICATION_CBOR, COAP_MEDIATYPE_APPLICATION_COAP_GROUP_JSON, - COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, - COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, COAP_MEDIATYPE_APPLICATION_COSE_KEY, COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, COAP_MEDIATYPE_APPLICATION_COSE_MAC, - COAP_MEDIATYPE_APPLICATION_COSE_MAC0, COAP_MEDIATYPE_APPLICATION_COSE_SIGN, - COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, COAP_MEDIATYPE_APPLICATION_CWT, - COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, COAP_MEDIATYPE_APPLICATION_EXI, COAP_MEDIATYPE_APPLICATION_JSON, - COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ, COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, - COAP_MEDIATYPE_APPLICATION_OSCORE, COAP_MEDIATYPE_APPLICATION_RDF_XML, COAP_MEDIATYPE_APPLICATION_SENML_CBOR, - COAP_MEDIATYPE_APPLICATION_SENML_EXI, COAP_MEDIATYPE_APPLICATION_SENML_JSON, - COAP_MEDIATYPE_APPLICATION_SENML_XML, COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, COAP_MEDIATYPE_APPLICATION_SENSML_EXI, - COAP_MEDIATYPE_APPLICATION_SENSML_JSON, COAP_MEDIATYPE_APPLICATION_SENSML_XML, COAP_MEDIATYPE_APPLICATION_XML, - COAP_MEDIATYPE_TEXT_PLAIN, COAP_OPTION_ACCEPT, - COAP_OPTION_BLOCK1, COAP_OPTION_BLOCK2, - COAP_OPTION_CONTENT_FORMAT, COAP_OPTION_ECHO, COAP_OPTION_ETAG, - COAP_OPTION_HOP_LIMIT, COAP_OPTION_IF_MATCH, COAP_OPTION_IF_NONE_MATCH, COAP_OPTION_LOCATION_PATH, COAP_OPTION_LOCATION_QUERY, - COAP_OPTION_MAXAGE, COAP_OPTION_NORESPONSE, coap_option_num_t, COAP_OPTION_OBSERVE, - COAP_OPTION_OSCORE, COAP_OPTION_PROXY_SCHEME, COAP_OPTION_PROXY_URI, COAP_OPTION_Q_BLOCK1, + COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, + COAP_MEDIATYPE_APPLICATION_COSE_KEY, COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, COAP_MEDIATYPE_APPLICATION_COSE_MAC, + COAP_MEDIATYPE_APPLICATION_COSE_MAC0, COAP_MEDIATYPE_APPLICATION_COSE_SIGN, COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, + COAP_MEDIATYPE_APPLICATION_CWT, COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, COAP_MEDIATYPE_APPLICATION_EXI, + COAP_MEDIATYPE_APPLICATION_JSON, COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ, + COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, COAP_MEDIATYPE_APPLICATION_OSCORE, COAP_MEDIATYPE_APPLICATION_RDF_XML, + COAP_MEDIATYPE_APPLICATION_SENML_CBOR, COAP_MEDIATYPE_APPLICATION_SENML_EXI, COAP_MEDIATYPE_APPLICATION_SENML_JSON, + COAP_MEDIATYPE_APPLICATION_SENML_XML, COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, + COAP_MEDIATYPE_APPLICATION_SENSML_EXI, COAP_MEDIATYPE_APPLICATION_SENSML_JSON, + COAP_MEDIATYPE_APPLICATION_SENSML_XML, COAP_MEDIATYPE_APPLICATION_XML, COAP_MEDIATYPE_TEXT_PLAIN, + COAP_OPTION_ACCEPT, COAP_OPTION_BLOCK1, COAP_OPTION_BLOCK2, COAP_OPTION_CONTENT_FORMAT, COAP_OPTION_ECHO, + COAP_OPTION_ETAG, COAP_OPTION_HOP_LIMIT, COAP_OPTION_IF_MATCH, COAP_OPTION_IF_NONE_MATCH, + COAP_OPTION_LOCATION_PATH, COAP_OPTION_LOCATION_QUERY, COAP_OPTION_MAXAGE, COAP_OPTION_NORESPONSE, + COAP_OPTION_OBSERVE, COAP_OPTION_OSCORE, COAP_OPTION_PROXY_SCHEME, COAP_OPTION_PROXY_URI, COAP_OPTION_Q_BLOCK1, COAP_OPTION_Q_BLOCK2, COAP_OPTION_RTAG, COAP_OPTION_SIZE1, COAP_OPTION_SIZE2, COAP_OPTION_URI_HOST, - COAP_OPTION_URI_PATH, COAP_OPTION_URI_PORT, COAP_OPTION_URI_QUERY, coap_pdu_code_t, coap_pdu_type_t, - coap_pdu_type_t::{COAP_MESSAGE_ACK, COAP_MESSAGE_CON, COAP_MESSAGE_NON, COAP_MESSAGE_RST}, coap_request_t, coap_response_phrase, + COAP_OPTION_URI_PATH, COAP_OPTION_URI_PORT, COAP_OPTION_URI_QUERY, }; use crate::error::{MessageCodeError, UnknownOptionError}; @@ -286,7 +305,7 @@ impl CoapMessageCode { /// [coap_pdu_t](libcoap_sys::coap_pdu_t). pub fn to_raw_pdu_code(self) -> coap_pdu_code_t { match self { - CoapMessageCode::Empty => coap_pdu_code_t::COAP_EMPTY_CODE, + CoapMessageCode::Empty => coap_pdu_code_t_COAP_EMPTY_CODE, CoapMessageCode::Request(req) => req.to_raw_pdu_code(), CoapMessageCode::Response(rsp) => rsp.to_raw_pdu_code(), } @@ -309,8 +328,11 @@ impl TryFrom for CoapMessageCode { type Error = MessageCodeError; fn try_from(code: coap_pdu_code_t) -> Result { + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] match code { - coap_pdu_code_t::COAP_EMPTY_CODE => Ok(CoapMessageCode::Empty), + coap_pdu_code_t_COAP_EMPTY_CODE => Ok(CoapMessageCode::Empty), code => CoapRequestCode::try_from(code) .map(CoapMessageCode::Request) .or_else(|_| CoapResponseCode::try_from(code).map(CoapMessageCode::Response)), @@ -326,30 +348,30 @@ impl TryFrom for CoapMessageCode { #[non_exhaustive] #[derive(FromPrimitive, Clone, Copy, Eq, PartialEq, Hash, Debug)] pub enum CoapRequestCode { - Get = coap_pdu_code_t::COAP_REQUEST_CODE_GET as u8, - Put = coap_pdu_code_t::COAP_REQUEST_CODE_PUT as u8, - Delete = coap_pdu_code_t::COAP_REQUEST_CODE_DELETE as u8, - Post = coap_pdu_code_t::COAP_REQUEST_CODE_POST as u8, - Fetch = coap_pdu_code_t::COAP_REQUEST_CODE_FETCH as u8, - IPatch = coap_pdu_code_t::COAP_REQUEST_CODE_IPATCH as u8, - Patch = coap_pdu_code_t::COAP_REQUEST_CODE_PATCH as u8, + Get = coap_pdu_code_t_COAP_REQUEST_CODE_GET as u8, + Put = coap_pdu_code_t_COAP_REQUEST_CODE_PUT as u8, + Delete = coap_pdu_code_t_COAP_REQUEST_CODE_DELETE as u8, + Post = coap_pdu_code_t_COAP_REQUEST_CODE_POST as u8, + Fetch = coap_pdu_code_t_COAP_REQUEST_CODE_FETCH as u8, + IPatch = coap_pdu_code_t_COAP_REQUEST_CODE_IPATCH as u8, + Patch = coap_pdu_code_t_COAP_REQUEST_CODE_PATCH as u8, } impl CoapRequestCode { /// Returns the [coap_request_t](coap_request_t) corresponding to this request code. /// - /// Note that this is *not* the code that should be set inside of a [coap_pdu_t](libcoap_sys::coap_pdu_t), + /// Note that this is *not* the code that should be set inside a [coap_pdu_t](libcoap_sys::coap_pdu_t), /// but a value used internally by the libcoap C library. See [to_raw_pdu_code()](CoapRequestCode::to_raw_pdu_code()) /// for the standardized value used in messages. pub fn to_raw_request(self) -> coap_request_t { match self { - CoapRequestCode::Get => coap_request_t::COAP_REQUEST_GET, - CoapRequestCode::Put => coap_request_t::COAP_REQUEST_PUT, - CoapRequestCode::Delete => coap_request_t::COAP_REQUEST_FETCH, - CoapRequestCode::Post => coap_request_t::COAP_REQUEST_POST, - CoapRequestCode::Fetch => coap_request_t::COAP_REQUEST_FETCH, - CoapRequestCode::IPatch => coap_request_t::COAP_REQUEST_IPATCH, - CoapRequestCode::Patch => coap_request_t::COAP_REQUEST_PATCH, + CoapRequestCode::Get => coap_request_t_COAP_REQUEST_GET, + CoapRequestCode::Put => coap_request_t_COAP_REQUEST_PUT, + CoapRequestCode::Delete => coap_request_t_COAP_REQUEST_DELETE, + CoapRequestCode::Post => coap_request_t_COAP_REQUEST_POST, + CoapRequestCode::Fetch => coap_request_t_COAP_REQUEST_FETCH, + CoapRequestCode::IPatch => coap_request_t_COAP_REQUEST_IPATCH, + CoapRequestCode::Patch => coap_request_t_COAP_REQUEST_PATCH, } } @@ -357,28 +379,13 @@ impl CoapRequestCode { /// request code. pub fn to_raw_pdu_code(self) -> coap_pdu_code_t { match self { - CoapRequestCode::Get => coap_pdu_code_t::COAP_REQUEST_CODE_GET, - CoapRequestCode::Put => coap_pdu_code_t::COAP_REQUEST_CODE_PUT, - CoapRequestCode::Delete => coap_pdu_code_t::COAP_REQUEST_CODE_FETCH, - CoapRequestCode::Post => coap_pdu_code_t::COAP_REQUEST_CODE_POST, - CoapRequestCode::Fetch => coap_pdu_code_t::COAP_REQUEST_CODE_FETCH, - CoapRequestCode::IPatch => coap_pdu_code_t::COAP_REQUEST_CODE_IPATCH, - CoapRequestCode::Patch => coap_pdu_code_t::COAP_REQUEST_CODE_PATCH, - } - } -} - -impl From for CoapRequestCode { - fn from(req: coap_request_t) -> Self { - match req { - coap_request_t::COAP_REQUEST_GET => CoapRequestCode::Get, - coap_request_t::COAP_REQUEST_POST => CoapRequestCode::Post, - coap_request_t::COAP_REQUEST_PUT => CoapRequestCode::Put, - coap_request_t::COAP_REQUEST_DELETE => CoapRequestCode::Delete, - coap_request_t::COAP_REQUEST_FETCH => CoapRequestCode::Fetch, - coap_request_t::COAP_REQUEST_PATCH => CoapRequestCode::Patch, - coap_request_t::COAP_REQUEST_IPATCH => CoapRequestCode::IPatch, - _ => panic!("unknown request type"), + CoapRequestCode::Get => coap_pdu_code_t_COAP_REQUEST_CODE_GET, + CoapRequestCode::Put => coap_pdu_code_t_COAP_REQUEST_CODE_PUT, + CoapRequestCode::Delete => coap_pdu_code_t_COAP_REQUEST_CODE_FETCH, + CoapRequestCode::Post => coap_pdu_code_t_COAP_REQUEST_CODE_POST, + CoapRequestCode::Fetch => coap_pdu_code_t_COAP_REQUEST_CODE_FETCH, + CoapRequestCode::IPatch => coap_pdu_code_t_COAP_REQUEST_CODE_IPATCH, + CoapRequestCode::Patch => coap_pdu_code_t_COAP_REQUEST_CODE_PATCH, } } } @@ -386,8 +393,8 @@ impl From for CoapRequestCode { impl TryFrom for CoapRequestCode { type Error = MessageCodeError; - fn try_from(req: coap_pdu_code_t) -> Result { - ::from_u32(req as u32).ok_or(MessageCodeError::NotARequestCode) + fn try_from(value: coap_pdu_code_t) -> Result { + ::from_u32(value as u32).ok_or(MessageCodeError::NotARequestCode) } } @@ -399,33 +406,33 @@ impl TryFrom for CoapRequestCode { #[non_exhaustive] #[derive(Clone, Copy, FromPrimitive, Debug, Eq, PartialEq, Hash)] pub enum CoapResponseCode { - Content = coap_pdu_code_t::COAP_RESPONSE_CODE_CONTENT as u8, - BadGateway = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_GATEWAY as u8, - Continue = coap_pdu_code_t::COAP_RESPONSE_CODE_CONTINUE as u8, - Conflict = coap_pdu_code_t::COAP_RESPONSE_CODE_CONFLICT as u8, - BadRequest = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_REQUEST as u8, - BadOption = coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_OPTION as u8, - Changed = coap_pdu_code_t::COAP_RESPONSE_CODE_CHANGED as u8, - Created = coap_pdu_code_t::COAP_RESPONSE_CODE_CREATED as u8, - Deleted = coap_pdu_code_t::COAP_RESPONSE_CODE_DELETED as u8, - Forbidden = coap_pdu_code_t::COAP_RESPONSE_CODE_FORBIDDEN as u8, - GatewayTimeout = coap_pdu_code_t::COAP_RESPONSE_CODE_GATEWAY_TIMEOUT as u8, - HopLimitReached = coap_pdu_code_t::COAP_RESPONSE_CODE_HOP_LIMIT_REACHED as u8, - Incomplete = coap_pdu_code_t::COAP_RESPONSE_CODE_INCOMPLETE as u8, - InternalError = coap_pdu_code_t::COAP_RESPONSE_CODE_INTERNAL_ERROR as u8, - NotAcceptable = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ACCEPTABLE as u8, - NotAllowed = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ALLOWED as u8, - NotFound = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_FOUND as u8, - NotImplemented = coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_IMPLEMENTED as u8, - PreconditionFailed = coap_pdu_code_t::COAP_RESPONSE_CODE_PRECONDITION_FAILED as u8, - ProxyingNotSupported = coap_pdu_code_t::COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED as u8, - RequestTooLarge = coap_pdu_code_t::COAP_RESPONSE_CODE_REQUEST_TOO_LARGE as u8, - ServiceUnavailable = coap_pdu_code_t::COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE as u8, - TooManyRequests = coap_pdu_code_t::COAP_RESPONSE_CODE_TOO_MANY_REQUESTS as u8, - Unauthorized = coap_pdu_code_t::COAP_RESPONSE_CODE_UNAUTHORIZED as u8, - Unprocessable = coap_pdu_code_t::COAP_RESPONSE_CODE_UNPROCESSABLE as u8, - UnsupportedContentFormat = coap_pdu_code_t::COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT as u8, - Valid = coap_pdu_code_t::COAP_RESPONSE_CODE_VALID as u8, + Content = coap_pdu_code_t_COAP_RESPONSE_CODE_CONTENT as u8, + BadGateway = coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_GATEWAY as u8, + Continue = coap_pdu_code_t_COAP_RESPONSE_CODE_CONTINUE as u8, + Conflict = coap_pdu_code_t_COAP_RESPONSE_CODE_CONFLICT as u8, + BadRequest = coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_REQUEST as u8, + BadOption = coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_OPTION as u8, + Changed = coap_pdu_code_t_COAP_RESPONSE_CODE_CHANGED as u8, + Created = coap_pdu_code_t_COAP_RESPONSE_CODE_CREATED as u8, + Deleted = coap_pdu_code_t_COAP_RESPONSE_CODE_DELETED as u8, + Forbidden = coap_pdu_code_t_COAP_RESPONSE_CODE_FORBIDDEN as u8, + GatewayTimeout = coap_pdu_code_t_COAP_RESPONSE_CODE_GATEWAY_TIMEOUT as u8, + HopLimitReached = coap_pdu_code_t_COAP_RESPONSE_CODE_HOP_LIMIT_REACHED as u8, + Incomplete = coap_pdu_code_t_COAP_RESPONSE_CODE_INCOMPLETE as u8, + InternalError = coap_pdu_code_t_COAP_RESPONSE_CODE_INTERNAL_ERROR as u8, + NotAcceptable = coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_ACCEPTABLE as u8, + NotAllowed = coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_ALLOWED as u8, + NotFound = coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_FOUND as u8, + NotImplemented = coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_IMPLEMENTED as u8, + PreconditionFailed = coap_pdu_code_t_COAP_RESPONSE_CODE_PRECONDITION_FAILED as u8, + ProxyingNotSupported = coap_pdu_code_t_COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED as u8, + RequestTooLarge = coap_pdu_code_t_COAP_RESPONSE_CODE_REQUEST_TOO_LARGE as u8, + ServiceUnavailable = coap_pdu_code_t_COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE as u8, + TooManyRequests = coap_pdu_code_t_COAP_RESPONSE_CODE_TOO_MANY_REQUESTS as u8, + Unauthorized = coap_pdu_code_t_COAP_RESPONSE_CODE_UNAUTHORIZED as u8, + Unprocessable = coap_pdu_code_t_COAP_RESPONSE_CODE_UNPROCESSABLE as u8, + UnsupportedContentFormat = coap_pdu_code_t_COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT as u8, + Valid = coap_pdu_code_t_COAP_RESPONSE_CODE_VALID as u8, } impl CoapResponseCode { @@ -433,35 +440,33 @@ impl CoapResponseCode { /// request code. pub fn to_raw_pdu_code(self) -> coap_pdu_code_t { match self { - CoapResponseCode::Content => coap_pdu_code_t::COAP_RESPONSE_CODE_CONTENT, - CoapResponseCode::BadGateway => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_GATEWAY, - CoapResponseCode::Continue => coap_pdu_code_t::COAP_RESPONSE_CODE_CONTINUE, - CoapResponseCode::Conflict => coap_pdu_code_t::COAP_RESPONSE_CODE_CONFLICT, - CoapResponseCode::BadRequest => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_REQUEST, - CoapResponseCode::BadOption => coap_pdu_code_t::COAP_RESPONSE_CODE_BAD_OPTION, - CoapResponseCode::Changed => coap_pdu_code_t::COAP_RESPONSE_CODE_CHANGED, - CoapResponseCode::Created => coap_pdu_code_t::COAP_RESPONSE_CODE_CREATED, - CoapResponseCode::Deleted => coap_pdu_code_t::COAP_RESPONSE_CODE_DELETED, - CoapResponseCode::Forbidden => coap_pdu_code_t::COAP_RESPONSE_CODE_FORBIDDEN, - CoapResponseCode::GatewayTimeout => coap_pdu_code_t::COAP_RESPONSE_CODE_GATEWAY_TIMEOUT, - CoapResponseCode::HopLimitReached => coap_pdu_code_t::COAP_RESPONSE_CODE_HOP_LIMIT_REACHED, - CoapResponseCode::Incomplete => coap_pdu_code_t::COAP_RESPONSE_CODE_INCOMPLETE, - CoapResponseCode::InternalError => coap_pdu_code_t::COAP_RESPONSE_CODE_INTERNAL_ERROR, - CoapResponseCode::NotAcceptable => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ACCEPTABLE, - CoapResponseCode::NotAllowed => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_ALLOWED, - CoapResponseCode::NotFound => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_FOUND, - CoapResponseCode::NotImplemented => coap_pdu_code_t::COAP_RESPONSE_CODE_NOT_IMPLEMENTED, - CoapResponseCode::PreconditionFailed => coap_pdu_code_t::COAP_RESPONSE_CODE_PRECONDITION_FAILED, - CoapResponseCode::ProxyingNotSupported => coap_pdu_code_t::COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED, - CoapResponseCode::RequestTooLarge => coap_pdu_code_t::COAP_RESPONSE_CODE_REQUEST_TOO_LARGE, - CoapResponseCode::ServiceUnavailable => coap_pdu_code_t::COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE, - CoapResponseCode::TooManyRequests => coap_pdu_code_t::COAP_RESPONSE_CODE_TOO_MANY_REQUESTS, - CoapResponseCode::Unauthorized => coap_pdu_code_t::COAP_RESPONSE_CODE_UNAUTHORIZED, - CoapResponseCode::Unprocessable => coap_pdu_code_t::COAP_RESPONSE_CODE_UNPROCESSABLE, - CoapResponseCode::UnsupportedContentFormat => { - coap_pdu_code_t::COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT - }, - CoapResponseCode::Valid => coap_pdu_code_t::COAP_RESPONSE_CODE_VALID, + CoapResponseCode::Content => coap_pdu_code_t_COAP_RESPONSE_CODE_CONTENT, + CoapResponseCode::BadGateway => coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_GATEWAY, + CoapResponseCode::Continue => coap_pdu_code_t_COAP_RESPONSE_CODE_CONTINUE, + CoapResponseCode::Conflict => coap_pdu_code_t_COAP_RESPONSE_CODE_CONFLICT, + CoapResponseCode::BadRequest => coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_REQUEST, + CoapResponseCode::BadOption => coap_pdu_code_t_COAP_RESPONSE_CODE_BAD_OPTION, + CoapResponseCode::Changed => coap_pdu_code_t_COAP_RESPONSE_CODE_CHANGED, + CoapResponseCode::Created => coap_pdu_code_t_COAP_RESPONSE_CODE_CREATED, + CoapResponseCode::Deleted => coap_pdu_code_t_COAP_RESPONSE_CODE_DELETED, + CoapResponseCode::Forbidden => coap_pdu_code_t_COAP_RESPONSE_CODE_FORBIDDEN, + CoapResponseCode::GatewayTimeout => coap_pdu_code_t_COAP_RESPONSE_CODE_GATEWAY_TIMEOUT, + CoapResponseCode::HopLimitReached => coap_pdu_code_t_COAP_RESPONSE_CODE_HOP_LIMIT_REACHED, + CoapResponseCode::Incomplete => coap_pdu_code_t_COAP_RESPONSE_CODE_INCOMPLETE, + CoapResponseCode::InternalError => coap_pdu_code_t_COAP_RESPONSE_CODE_INTERNAL_ERROR, + CoapResponseCode::NotAcceptable => coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_ACCEPTABLE, + CoapResponseCode::NotAllowed => coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_ALLOWED, + CoapResponseCode::NotFound => coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_FOUND, + CoapResponseCode::NotImplemented => coap_pdu_code_t_COAP_RESPONSE_CODE_NOT_IMPLEMENTED, + CoapResponseCode::PreconditionFailed => coap_pdu_code_t_COAP_RESPONSE_CODE_PRECONDITION_FAILED, + CoapResponseCode::ProxyingNotSupported => coap_pdu_code_t_COAP_RESPONSE_CODE_PROXYING_NOT_SUPPORTED, + CoapResponseCode::RequestTooLarge => coap_pdu_code_t_COAP_RESPONSE_CODE_REQUEST_TOO_LARGE, + CoapResponseCode::ServiceUnavailable => coap_pdu_code_t_COAP_RESPONSE_CODE_SERVICE_UNAVAILABLE, + CoapResponseCode::TooManyRequests => coap_pdu_code_t_COAP_RESPONSE_CODE_TOO_MANY_REQUESTS, + CoapResponseCode::Unauthorized => coap_pdu_code_t_COAP_RESPONSE_CODE_UNAUTHORIZED, + CoapResponseCode::Unprocessable => coap_pdu_code_t_COAP_RESPONSE_CODE_UNPROCESSABLE, + CoapResponseCode::UnsupportedContentFormat => coap_pdu_code_t_COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT, + CoapResponseCode::Valid => coap_pdu_code_t_COAP_RESPONSE_CODE_VALID, } } } @@ -497,13 +502,13 @@ impl TryFrom for CoapResponseCode { #[derive(Copy, Clone, Hash, Eq, PartialEq, FromPrimitive, Debug)] pub enum CoapMessageType { /// Confirmable message, i.e. a message whose reception should be confirmed by the peer. - Con = COAP_MESSAGE_CON as u8, + Con = coap_pdu_type_t_COAP_MESSAGE_CON as u8, /// Non-confirmable message, i.e. a message whose reception should not be confirmed by the peer. - Non = COAP_MESSAGE_NON as u8, + Non = coap_pdu_type_t_COAP_MESSAGE_NON as u8, /// Acknowledgement for a previous message. - Ack = COAP_MESSAGE_ACK as u8, + Ack = coap_pdu_type_t_COAP_MESSAGE_ACK as u8, /// Non-acknowledgement for a previous message. - Rst = COAP_MESSAGE_RST as u8, + Rst = coap_pdu_type_t_COAP_MESSAGE_RST as u8, } impl CoapMessageType { @@ -511,10 +516,10 @@ impl CoapMessageType { /// this message type. pub fn to_raw_pdu_type(&self) -> coap_pdu_type_t { match self { - CoapMessageType::Con => COAP_MESSAGE_CON, - CoapMessageType::Non => COAP_MESSAGE_NON, - CoapMessageType::Ack => COAP_MESSAGE_ACK, - CoapMessageType::Rst => COAP_MESSAGE_RST, + CoapMessageType::Con => coap_pdu_type_t_COAP_MESSAGE_CON, + CoapMessageType::Non => coap_pdu_type_t_COAP_MESSAGE_NON, + CoapMessageType::Ack => coap_pdu_type_t_COAP_MESSAGE_ACK, + CoapMessageType::Rst => coap_pdu_type_t_COAP_MESSAGE_RST, } } } diff --git a/libcoap/src/resource.rs b/libcoap/src/resource.rs index 6a752b9f..4319d148 100644 --- a/libcoap/src/resource.rs +++ b/libcoap/src/resource.rs @@ -17,25 +17,25 @@ use std::{ marker::PhantomData, }; -use libc::c_int; +use core::ffi::c_int; use libcoap_sys::{ - coap_delete_resource, coap_new_str_const, coap_pdu_t, coap_register_request_handler, COAP_RESOURCE_FLAGS_NOTIFY_CON, - COAP_RESOURCE_FLAGS_NOTIFY_NON, COAP_RESOURCE_FLAGS_RELEASE_URI, coap_resource_get_uri_path, coap_resource_get_userdata, - coap_resource_init, coap_resource_notify_observers, coap_resource_set_get_observable, coap_resource_set_mode, coap_resource_set_userdata, coap_resource_t, - coap_send_rst, coap_session_t, coap_string_t, + coap_delete_resource, coap_new_str_const, coap_pdu_t, coap_register_request_handler, coap_resource_get_uri_path, + coap_resource_get_userdata, coap_resource_init, coap_resource_notify_observers, coap_resource_set_get_observable, + coap_resource_set_mode, coap_resource_set_userdata, coap_resource_t, coap_send_rst, coap_session_t, coap_string_t, + COAP_RESOURCE_FLAGS_NOTIFY_CON, COAP_RESOURCE_FLAGS_NOTIFY_NON, COAP_RESOURCE_FLAGS_RELEASE_URI, }; -use crate::{error::MessageConversionError, message::CoapMessage, protocol::CoapRequestCode}; use crate::context::ensure_coap_started; use crate::mem::{CoapFfiRcCell, DropInnerExclusively}; -use crate::message::CoapMessageCommon; use crate::message::request::CoapRequest; use crate::message::response::CoapResponse; +use crate::message::CoapMessageCommon; use crate::protocol::CoapMessageCode; use crate::protocol::CoapMessageType; use crate::session::CoapServerSession; use crate::session::CoapSessionCommon; +use crate::{error::MessageConversionError, message::CoapMessage, protocol::CoapRequestCode}; // Trait aliases are experimental //trait CoapMethodHandlerFn = FnMut(&D, &mut CoapSession, &CoapRequestMessage, &mut CoapResponseMessage); diff --git a/libcoap/src/session/client.rs b/libcoap/src/session/client.rs index 4e1660fb..e229221f 100644 --- a/libcoap/src/session/client.rs +++ b/libcoap/src/session/client.rs @@ -11,9 +11,11 @@ use std::cell::{Ref, RefMut}; use std::net::SocketAddr; use libcoap_sys::{ - coap_new_client_session, coap_proto_t, coap_register_event_handler, coap_session_get_app_data, - coap_session_get_context, coap_session_get_type, coap_session_init_token, coap_session_release, - coap_session_set_app_data, coap_session_t, coap_session_type_t, COAP_TOKEN_DEFAULT_MAX, + coap_new_client_session, coap_proto_t_COAP_PROTO_DTLS, coap_proto_t_COAP_PROTO_TCP, coap_proto_t_COAP_PROTO_UDP, + coap_register_event_handler, coap_session_get_app_data, coap_session_get_context, coap_session_get_type, + coap_session_init_token, coap_session_release, coap_session_set_app_data, coap_session_t, + coap_session_type_t_COAP_SESSION_TYPE_CLIENT, coap_session_type_t_COAP_SESSION_TYPE_HELLO, + coap_session_type_t_COAP_SESSION_TYPE_NONE, coap_session_type_t_COAP_SESSION_TYPE_SERVER, COAP_TOKEN_DEFAULT_MAX, }; use super::{CoapSessionCommon, CoapSessionInner, CoapSessionInnerProvider}; @@ -115,15 +117,15 @@ impl CoapClientSession<'_> { match &crypto_ctx { #[cfg(feature = "dtls-psk")] ClientCryptoContext::Psk(psk_ctx) => { - psk_ctx.create_raw_session(ctx, &addr.into(), coap_proto_t::COAP_PROTO_DTLS)? + psk_ctx.create_raw_session(ctx, &addr.into(), coap_proto_t_COAP_PROTO_DTLS)? }, #[cfg(feature = "dtls-pki")] ClientCryptoContext::Pki(pki_ctx) => { - pki_ctx.create_raw_session(ctx, &addr.into(), coap_proto_t::COAP_PROTO_DTLS)? + pki_ctx.create_raw_session(ctx, &addr.into(), coap_proto_t_COAP_PROTO_DTLS)? }, #[cfg(feature = "dtls-rpk")] ClientCryptoContext::Rpk(rpk_ctx) => { - rpk_ctx.create_raw_session(ctx, &addr.into(), coap_proto_t::COAP_PROTO_DTLS)? + rpk_ctx.create_raw_session(ctx, &addr.into(), coap_proto_t_COAP_PROTO_DTLS)? }, } }; @@ -149,7 +151,7 @@ impl CoapClientSession<'_> { ctx.as_mut_raw_context(), std::ptr::null(), CoapAddress::from(addr).as_raw_address(), - coap_proto_t::COAP_PROTO_UDP, + coap_proto_t_COAP_PROTO_UDP, ) }; if session.is_null() { @@ -176,7 +178,7 @@ impl CoapClientSession<'_> { ctx.as_mut_raw_context(), std::ptr::null(), CoapAddress::from(addr).as_raw_address(), - coap_proto_t::COAP_PROTO_TCP, + coap_proto_t_COAP_PROTO_TCP, ) }; if session.is_null() { @@ -208,15 +210,18 @@ impl CoapClientSession<'_> { pub(crate) unsafe fn from_raw<'a>(raw_session: *mut coap_session_t) -> CoapClientSession<'a> { assert!(!raw_session.is_null(), "provided raw session was null"); let raw_session_type = coap_session_get_type(raw_session); + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] match raw_session_type { - coap_session_type_t::COAP_SESSION_TYPE_NONE => panic!("provided session has no type"), - coap_session_type_t::COAP_SESSION_TYPE_CLIENT => { + coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"), + coap_session_type_t_COAP_SESSION_TYPE_CLIENT => { let raw_app_data_ptr = coap_session_get_app_data(raw_session); assert!(!raw_app_data_ptr.is_null(), "provided raw session has no app data"); let inner = CoapFfiRcCell::clone_raw_rc(raw_app_data_ptr); CoapClientSession { inner } }, - coap_session_type_t::COAP_SESSION_TYPE_SERVER | coap_session_type_t::COAP_SESSION_TYPE_HELLO => { + coap_session_type_t_COAP_SESSION_TYPE_SERVER | coap_session_type_t_COAP_SESSION_TYPE_HELLO => { panic!("attempted to create CoapClientSession from raw server session") }, _ => unreachable!("unknown session type"), diff --git a/libcoap/src/session/mod.rs b/libcoap/src/session/mod.rs index 13016e3d..ffb0ac3f 100644 --- a/libcoap/src/session/mod.rs +++ b/libcoap/src/session/mod.rs @@ -19,12 +19,17 @@ use std::{ use libcoap_sys::{ coap_context_t, coap_fixed_point_t, coap_mid_t, coap_new_message_id, coap_pdu_get_token, coap_pdu_t, - coap_response_t, coap_send, coap_session_get_ack_random_factor, coap_session_get_ack_timeout, - coap_session_get_addr_local, coap_session_get_addr_remote, coap_session_get_ifindex, - coap_session_get_max_retransmit, coap_session_get_proto, coap_session_get_state, coap_session_get_type, - coap_session_init_token, coap_session_max_pdu_size, coap_session_new_token, coap_session_send_ping, - coap_session_set_ack_random_factor, coap_session_set_ack_timeout, coap_session_set_max_retransmit, - coap_session_set_mtu, coap_session_state_t, coap_session_t, coap_session_type_t, + coap_response_t, coap_response_t_COAP_RESPONSE_FAIL, coap_response_t_COAP_RESPONSE_OK, coap_send, + coap_session_get_ack_random_factor, coap_session_get_ack_timeout, coap_session_get_addr_local, + coap_session_get_addr_remote, coap_session_get_ifindex, coap_session_get_max_retransmit, coap_session_get_proto, + coap_session_get_state, coap_session_get_type, coap_session_init_token, coap_session_max_pdu_size, + coap_session_new_token, coap_session_send_ping, coap_session_set_ack_random_factor, coap_session_set_ack_timeout, + coap_session_set_max_retransmit, coap_session_set_mtu, coap_session_state_t, + coap_session_state_t_COAP_SESSION_STATE_CONNECTING, coap_session_state_t_COAP_SESSION_STATE_CSM, + coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED, coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE, + coap_session_state_t_COAP_SESSION_STATE_NONE, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_CLIENT, + coap_session_type_t_COAP_SESSION_TYPE_HELLO, coap_session_type_t_COAP_SESSION_TYPE_NONE, + coap_session_type_t_COAP_SESSION_TYPE_SERVER, }; #[cfg(feature = "dtls-psk")] use libcoap_sys::{coap_session_get_psk_hint, coap_session_get_psk_identity, coap_session_get_psk_key}; @@ -45,21 +50,24 @@ pub mod server; /// Representation of the states that a session can be in. #[repr(u32)] pub enum CoapSessionState { - None = coap_session_state_t::COAP_SESSION_STATE_NONE as u32, - Connecting = coap_session_state_t::COAP_SESSION_STATE_CONNECTING as u32, - Handshake = coap_session_state_t::COAP_SESSION_STATE_HANDSHAKE as u32, - Csm = coap_session_state_t::COAP_SESSION_STATE_CSM as u32, - Established = coap_session_state_t::COAP_SESSION_STATE_ESTABLISHED as u32, + None = coap_session_state_t_COAP_SESSION_STATE_NONE as u32, + Connecting = coap_session_state_t_COAP_SESSION_STATE_CONNECTING as u32, + Handshake = coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE as u32, + Csm = coap_session_state_t_COAP_SESSION_STATE_CSM as u32, + Established = coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED as u32, } impl From for CoapSessionState { fn from(raw_state: coap_session_state_t) -> Self { + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] match raw_state { - coap_session_state_t::COAP_SESSION_STATE_NONE => CoapSessionState::None, - coap_session_state_t::COAP_SESSION_STATE_CONNECTING => CoapSessionState::Connecting, - coap_session_state_t::COAP_SESSION_STATE_HANDSHAKE => CoapSessionState::Handshake, - coap_session_state_t::COAP_SESSION_STATE_CSM => CoapSessionState::Csm, - coap_session_state_t::COAP_SESSION_STATE_ESTABLISHED => CoapSessionState::Established, + coap_session_state_t_COAP_SESSION_STATE_NONE => CoapSessionState::None, + coap_session_state_t_COAP_SESSION_STATE_CONNECTING => CoapSessionState::Connecting, + coap_session_state_t_COAP_SESSION_STATE_HANDSHAKE => CoapSessionState::Handshake, + coap_session_state_t_COAP_SESSION_STATE_CSM => CoapSessionState::Csm, + coap_session_state_t_COAP_SESSION_STATE_ESTABLISHED => CoapSessionState::Established, _ => unreachable!("unknown session state added"), } } @@ -112,7 +120,7 @@ pub trait CoapSessionCommon<'a>: CoapSessionCommonInternal<'a> { } /// Sets the application-specific data stored alongside this session. - fn set_app_data(&self, value: Option) { + fn set_app_data(&self, value: Option) { let mut inner = self.inner_mut(); let new_box: Option> = value.map(|v| Rc::new(v) as Rc); inner.app_data = new_box; @@ -447,12 +455,16 @@ impl<'a> CoapSession<'a> { pub(crate) unsafe fn from_raw(raw_session: *mut coap_session_t) -> CoapSession<'a> { assert!(!raw_session.is_null(), "provided raw session was null"); let raw_session_type = coap_session_get_type(raw_session); + + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] match raw_session_type { - coap_session_type_t::COAP_SESSION_TYPE_NONE => panic!("provided session has no type"), + coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"), - coap_session_type_t::COAP_SESSION_TYPE_CLIENT => CoapClientSession::from_raw(raw_session).into(), + coap_session_type_t_COAP_SESSION_TYPE_CLIENT => CoapClientSession::from_raw(raw_session).into(), - coap_session_type_t::COAP_SESSION_TYPE_SERVER | coap_session_type_t::COAP_SESSION_TYPE_HELLO => { + coap_session_type_t_COAP_SESSION_TYPE_SERVER | coap_session_type_t_COAP_SESSION_TYPE_HELLO => { CoapServerSession::from_raw(raw_session).into() }, _ => unreachable!("unknown session type"), @@ -542,7 +554,6 @@ impl CoapRequestHandle { } // This is fine, we don't read the C-type struct, we return it. -#[allow(improper_ctypes_definitions)] pub(crate) unsafe extern "C" fn session_response_handler( session: *mut coap_session_t, _sent: *const coap_pdu_t, @@ -555,12 +566,12 @@ pub(crate) unsafe extern "C" fn session_response_handler( let raw_token = coap_pdu_get_token(received); let token: CoapToken = CoapToken::from(std::slice::from_raw_parts(raw_token.s, raw_token.length)); if !client.is_waiting_for_token(&token) { - return coap_response_t::COAP_RESPONSE_FAIL; + return coap_response_t_COAP_RESPONSE_FAIL; } if let Ok(message) = CoapMessage::from_raw_pdu(received).and_then(CoapResponse::from_message) { client.add_response(message); - coap_response_t::COAP_RESPONSE_OK + coap_response_t_COAP_RESPONSE_OK } else { - coap_response_t::COAP_RESPONSE_FAIL + coap_response_t_COAP_RESPONSE_FAIL } } diff --git a/libcoap/src/session/server.rs b/libcoap/src/session/server.rs index 29335e58..9177648a 100644 --- a/libcoap/src/session/server.rs +++ b/libcoap/src/session/server.rs @@ -11,7 +11,9 @@ use std::cell::{Ref, RefMut}; use libcoap_sys::{ coap_session_get_app_data, coap_session_get_type, coap_session_reference, coap_session_release, - coap_session_set_app_data, coap_session_t, coap_session_type_t, + coap_session_set_app_data, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_CLIENT, + coap_session_type_t_COAP_SESSION_TYPE_HELLO, coap_session_type_t_COAP_SESSION_TYPE_NONE, + coap_session_type_t_COAP_SESSION_TYPE_SERVER, }; use super::{CoapSessionCommon, CoapSessionInner, CoapSessionInnerProvider}; @@ -65,13 +67,16 @@ impl CoapServerSession<'_> { assert!(!raw_session.is_null(), "provided raw session was null"); let raw_session_type = coap_session_get_type(raw_session); let inner = CoapSessionInner::new(raw_session); + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] let session_inner = match raw_session_type { - coap_session_type_t::COAP_SESSION_TYPE_NONE => panic!("provided session has no type"), - coap_session_type_t::COAP_SESSION_TYPE_CLIENT => { + coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"), + coap_session_type_t_COAP_SESSION_TYPE_CLIENT => { panic!("attempted to create server session from raw client session") }, - coap_session_type_t::COAP_SESSION_TYPE_SERVER => CoapServerSessionInner { inner }, - coap_session_type_t::COAP_SESSION_TYPE_HELLO => CoapServerSessionInner { inner }, + coap_session_type_t_COAP_SESSION_TYPE_SERVER => CoapServerSessionInner { inner }, + coap_session_type_t_COAP_SESSION_TYPE_HELLO => CoapServerSessionInner { inner }, _ => unreachable!("unknown session type"), }; let session_ref = CoapFfiRcCell::new(session_inner); @@ -139,9 +144,12 @@ impl CoapServerSession<'_> { pub(crate) unsafe fn from_raw_without_refcount<'a>(raw_session: *mut coap_session_t) -> CoapServerSession<'a> { assert!(!raw_session.is_null(), "provided raw session was null"); let raw_session_type = coap_session_get_type(raw_session); + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] match raw_session_type { - coap_session_type_t::COAP_SESSION_TYPE_NONE => panic!("provided session has no type"), - coap_session_type_t::COAP_SESSION_TYPE_SERVER | coap_session_type_t::COAP_SESSION_TYPE_HELLO => { + coap_session_type_t_COAP_SESSION_TYPE_NONE => panic!("provided session has no type"), + coap_session_type_t_COAP_SESSION_TYPE_SERVER | coap_session_type_t_COAP_SESSION_TYPE_HELLO => { let raw_app_data_ptr = coap_session_get_app_data(raw_session); assert!(!raw_app_data_ptr.is_null(), "provided raw session has no app data"); CoapServerSession { @@ -149,7 +157,7 @@ impl CoapServerSession<'_> { ref_counted: false, } }, - coap_session_type_t::COAP_SESSION_TYPE_CLIENT => { + coap_session_type_t_COAP_SESSION_TYPE_CLIENT => { panic!("attempted to create CoapServerSession from raw client session") }, _ => unreachable!("unknown session type"), diff --git a/libcoap/src/types.rs b/libcoap/src/types.rs index ad7703e3..f240d96b 100644 --- a/libcoap/src/types.rs +++ b/libcoap/src/types.rs @@ -9,7 +9,11 @@ //! Types required for conversion between libcoap C library abstractions and Rust types. -use std::ffi::CString; +use core::ffi::c_ushort; +use libcoap_sys::c_stdlib::{in6_addr, in_addr, sa_family_t, sockaddr_in, sockaddr_in6, socklen_t, AF_INET, AF_INET6}; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; +use std::ffi::{CStr, CString}; use std::fmt::{Display, Formatter}; use std::marker::PhantomPinned; use std::pin::Pin; @@ -20,24 +24,18 @@ use std::{ os::raw::c_int, str::FromStr, }; - -use libc::{c_ushort, in6_addr, in_addr, sa_family_t, sockaddr_in, sockaddr_in6, socklen_t, AF_INET, AF_INET6}; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; #[cfg(feature = "url")] use url::Url; -use libcoap_sys::coap_uri_scheme_t::{COAP_URI_SCHEME_COAPS_WS, COAP_URI_SCHEME_COAP_WS}; use libcoap_sys::{ - coap_address_t, coap_delete_optlist, coap_mid_t, coap_proto_t, - coap_proto_t::{COAP_PROTO_DTLS, COAP_PROTO_NONE, COAP_PROTO_TCP, COAP_PROTO_TLS, COAP_PROTO_UDP}, - coap_split_proxy_uri, coap_split_uri, coap_str_const_t, coap_string_equal, coap_uri_into_optlist, - coap_uri_scheme_t, - coap_uri_scheme_t::{ - COAP_URI_SCHEME_COAP, COAP_URI_SCHEME_COAPS, COAP_URI_SCHEME_COAPS_TCP, COAP_URI_SCHEME_COAP_TCP, - COAP_URI_SCHEME_HTTP, COAP_URI_SCHEME_HTTPS, - }, - coap_uri_t, COAP_URI_SCHEME_SECURE_MASK, + coap_address_t, coap_delete_optlist, coap_mid_t, coap_proto_t, coap_proto_t_COAP_PROTO_DTLS, + coap_proto_t_COAP_PROTO_NONE, coap_proto_t_COAP_PROTO_TCP, coap_proto_t_COAP_PROTO_TLS, + coap_proto_t_COAP_PROTO_UDP, coap_split_proxy_uri, coap_split_uri, coap_str_const_t, coap_string_equal, + coap_uri_into_optlist, coap_uri_scheme_t, coap_uri_scheme_t_COAP_URI_SCHEME_COAP, + coap_uri_scheme_t_COAP_URI_SCHEME_COAPS, coap_uri_scheme_t_COAP_URI_SCHEME_COAPS_TCP, + coap_uri_scheme_t_COAP_URI_SCHEME_COAPS_WS, coap_uri_scheme_t_COAP_URI_SCHEME_COAP_TCP, + coap_uri_scheme_t_COAP_URI_SCHEME_COAP_WS, coap_uri_scheme_t_COAP_URI_SCHEME_HTTP, + coap_uri_scheme_t_COAP_URI_SCHEME_HTTPS, coap_uri_t, COAP_URI_SCHEME_SECURE_MASK, }; use crate::context::ensure_coap_started; @@ -92,11 +90,11 @@ impl ToSocketAddrs for CoapAddress { // SAFETY: That the underlying value of addr is a valid sockaddr is an invariant, the only // way the value could be invalid is if as_mut_coap_address_t() (an unsafe function) is used // incorrectly. - let socketaddr = match unsafe { self.0.addr.sa.as_ref().sa_family } as i32 { + let socketaddr = match unsafe { self.0.addr.sa.sa_family as _ } { AF_INET => { // SAFETY: Validity of addr is an invariant, and we checked that the type of the // underlying sockaddr is actually sockaddr_in. - let raw_addr = unsafe { self.0.addr.sin.as_ref() }; + let raw_addr = unsafe { self.0.addr.sin }; SocketAddrV4::new( Ipv4Addr::from(raw_addr.sin_addr.s_addr.to_ne_bytes()), u16::from_be(raw_addr.sin_port), @@ -106,9 +104,17 @@ impl ToSocketAddrs for CoapAddress { AF_INET6 => { // SAFETY: Validity of addr is an invariant, and we checked that the type of the // underlying sockaddr is actually sockaddr_in6. - let raw_addr = unsafe { self.0.addr.sin6.as_ref() }; + let raw_addr = unsafe { self.0.addr.sin6 }; + + // The esp_idf_sys definition of sockaddr_in6 differs slightly. + #[cfg(not(target_os = "espidf"))] + let raw_addr_bytes = raw_addr.sin6_addr.s6_addr; + #[cfg(target_os = "espidf")] + // SAFETY: Both representations are valid. + let raw_addr_bytes = unsafe { raw_addr.sin6_addr.un.u8_addr }; + SocketAddrV6::new( - Ipv6Addr::from(raw_addr.sin6_addr.s6_addr), + Ipv6Addr::from(raw_addr_bytes), u16::from_be(raw_addr.sin6_port), raw_addr.sin6_flowinfo, raw_addr.sin6_scope_id, @@ -135,7 +141,7 @@ impl From for CoapAddress { addr: std::mem::zeroed(), }; - *coap_addr.addr.sin.as_mut() = sockaddr_in { + coap_addr.addr.sin = sockaddr_in { #[cfg(any( target_os = "freebsd", target_os = "dragonfly", @@ -167,7 +173,9 @@ impl From for CoapAddress { addr: std::mem::zeroed(), }; - *coap_addr.addr.sin6.as_mut() = sockaddr_in6 { + // Representation of sockaddr_in6 differs depending on the used OS, therefore + // some fields are a bit different. + coap_addr.addr.sin6 = sockaddr_in6 { #[cfg(any( target_os = "freebsd", target_os = "dragonfly", @@ -182,7 +190,12 @@ impl From for CoapAddress { sin6_family: AF_INET6 as sa_family_t, sin6_port: addr.port().to_be(), sin6_addr: in6_addr { + #[cfg(not(target_os = "espidf"))] s6_addr: addr.ip().octets(), + #[cfg(target_os = "espidf")] + un: libcoap_sys::c_stdlib::in6_addr__bindgen_ty_1 { + u8_addr: addr.ip().octets(), + }, }, sin6_flowinfo: addr.flowinfo(), sin6_scope_id: addr.scope_id(), @@ -217,14 +230,14 @@ impl From<&coap_address_t> for CoapAddress { #[repr(u32)] #[derive(Copy, Clone, FromPrimitive, Debug, PartialEq, Eq, Hash)] pub enum CoapUriScheme { - Coap = COAP_URI_SCHEME_COAP as u32, - Coaps = COAP_URI_SCHEME_COAPS as u32, - CoapTcp = COAP_URI_SCHEME_COAP_TCP as u32, - CoapsTcp = COAP_URI_SCHEME_COAPS_TCP as u32, - Http = COAP_URI_SCHEME_HTTP as u32, - Https = COAP_URI_SCHEME_HTTPS as u32, - CoapWs = COAP_URI_SCHEME_COAP_WS as u32, - CoapsWs = COAP_URI_SCHEME_COAPS_WS as u32, + Coap = coap_uri_scheme_t_COAP_URI_SCHEME_COAP as u32, + Coaps = coap_uri_scheme_t_COAP_URI_SCHEME_COAPS as u32, + CoapTcp = coap_uri_scheme_t_COAP_URI_SCHEME_COAP_TCP as u32, + CoapsTcp = coap_uri_scheme_t_COAP_URI_SCHEME_COAPS_TCP as u32, + Http = coap_uri_scheme_t_COAP_URI_SCHEME_HTTP as u32, + Https = coap_uri_scheme_t_COAP_URI_SCHEME_HTTPS as u32, + CoapWs = coap_uri_scheme_t_COAP_URI_SCHEME_COAP_WS as u32, + CoapsWs = coap_uri_scheme_t_COAP_URI_SCHEME_COAPS_WS as u32, } impl CoapUriScheme { @@ -713,7 +726,7 @@ impl CoapUri { length: 0, s: std::ptr::null(), }, - scheme: coap_uri_scheme_t::COAP_URI_SCHEME_COAP, + scheme: coap_uri_scheme_t_COAP_URI_SCHEME_COAP, }, uri_str, is_proxy, @@ -740,7 +753,7 @@ impl CoapUri { if unsafe { parsing_fn( uri.uri_str.0.as_ptr() as *const u8, - libc::strlen(uri.uri_str.0.as_ptr()), + CStr::from_ptr(uri.uri_str.0.as_ptr()).count_bytes(), std::ptr::from_mut(&mut uri.raw_uri), ) } < 0 @@ -797,10 +810,10 @@ impl PartialEq for CoapUri { // corresponding parts of the underlying string, which is pinned. Therefore, the // pointer and length are valid for the lifetime of this struct. && unsafe { - coap_string_equal!(&self.raw_uri.host, &other.raw_uri.host) - && coap_string_equal!(&self.raw_uri.path, &other.raw_uri.path) - && coap_string_equal!(&self.raw_uri.query, &other.raw_uri.query) - } + coap_string_equal!(&self.raw_uri.host, &other.raw_uri.host) + && coap_string_equal!(&self.raw_uri.path, &other.raw_uri.path) + && coap_string_equal!(&self.raw_uri.query, &other.raw_uri.query) + } } } @@ -842,11 +855,11 @@ impl Display for CoapUri { #[non_exhaustive] #[derive(Copy, Clone, FromPrimitive, PartialEq, Eq, Hash)] pub enum CoapProtocol { - None = COAP_PROTO_NONE as u32, - Udp = COAP_PROTO_UDP as u32, - Dtls = COAP_PROTO_DTLS as u32, - Tcp = COAP_PROTO_TCP as u32, - Tls = COAP_PROTO_TLS as u32, + None = coap_proto_t_COAP_PROTO_NONE as u32, + Udp = coap_proto_t_COAP_PROTO_UDP as u32, + Dtls = coap_proto_t_COAP_PROTO_DTLS as u32, + Tcp = coap_proto_t_COAP_PROTO_TCP as u32, + Tls = coap_proto_t_COAP_PROTO_TLS as u32, } impl CoapProtocol { diff --git a/libcoap/tests/common/dtls.rs b/libcoap/tests/common/dtls.rs index 63220103..00bed47e 100644 --- a/libcoap/tests/common/dtls.rs +++ b/libcoap/tests/common/dtls.rs @@ -7,7 +7,12 @@ use libcoap_rs::message::CoapMessageCommon; use libcoap_rs::protocol::{CoapMessageCode, CoapResponseCode}; use libcoap_rs::session::{CoapClientSession, CoapSessionCommon}; use libcoap_rs::CoapContext; -use libcoap_sys::{coap_get_tls_library_version, coap_package_version, coap_tls_library_t}; +use libcoap_sys::{ + coap_get_tls_library_version, coap_package_version, coap_tls_library_t_COAP_TLS_LIBRARY_GNUTLS, + coap_tls_library_t_COAP_TLS_LIBRARY_MBEDTLS, coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS, + coap_tls_library_t_COAP_TLS_LIBRARY_OPENSSL, coap_tls_library_t_COAP_TLS_LIBRARY_TINYDTLS, + coap_tls_library_t_COAP_TLS_LIBRARY_WOLFSSL, +}; use std::ffi::CStr; use std::path::PathBuf; use std::time::Duration; @@ -25,13 +30,16 @@ pub fn dtls_client_server_request_common( ServerPkiRpkCryptoContext<'static>: From>, ClientCryptoContext<'static>: From>, { + // Variant names are named by bindgen, we have no influence on this. + // Ref: https://github.com/rust-lang/rust/issues/39371 + #[allow(non_upper_case_globals)] let tls_library = match unsafe { *coap_get_tls_library_version() }.type_ { - coap_tls_library_t::COAP_TLS_LIBRARY_NOTLS => "notls", - coap_tls_library_t::COAP_TLS_LIBRARY_TINYDTLS => "tinydtls", - coap_tls_library_t::COAP_TLS_LIBRARY_OPENSSL => "openssl", - coap_tls_library_t::COAP_TLS_LIBRARY_GNUTLS => "gnutls", - coap_tls_library_t::COAP_TLS_LIBRARY_MBEDTLS => "mbedtls", - coap_tls_library_t::COAP_TLS_LIBRARY_WOLFSSL => "wolfssl", + coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS => "notls", + coap_tls_library_t_COAP_TLS_LIBRARY_TINYDTLS => "tinydtls", + coap_tls_library_t_COAP_TLS_LIBRARY_OPENSSL => "openssl", + coap_tls_library_t_COAP_TLS_LIBRARY_GNUTLS => "gnutls", + coap_tls_library_t_COAP_TLS_LIBRARY_MBEDTLS => "mbedtls", + coap_tls_library_t_COAP_TLS_LIBRARY_WOLFSSL => "wolfssl", _ => "unknown", }; println!( diff --git a/libcoap/tests/common/mod.rs b/libcoap/tests/common/mod.rs index dab19994..28a146a6 100644 --- a/libcoap/tests/common/mod.rs +++ b/libcoap/tests/common/mod.rs @@ -21,7 +21,7 @@ use libcoap_rs::message::{CoapMessageCommon, CoapRequest, CoapResponse}; use libcoap_rs::protocol::{CoapMessageCode, CoapMessageType, CoapRequestCode, CoapResponseCode}; use libcoap_rs::session::CoapSessionCommon; use libcoap_rs::{CoapContext, CoapRequestHandler, CoapResource}; -use libcoap_sys::{coap_dtls_set_log_level, coap_log_t, coap_set_log_level}; +use libcoap_sys::{coap_dtls_set_log_level, coap_log_t_COAP_LOG_DEBUG, coap_set_log_level}; pub(crate) fn get_unused_server_addr() -> SocketAddr { // This will give us a SocketAddress with a port in the local port range automatically @@ -91,8 +91,8 @@ pub(crate) fn spawn_test_server) -> CoapContext<' pub(crate) fn run_test_server) -> CoapContext<'static>>(context_configurator: F) { unsafe { libcoap_sys::coap_startup_with_feature_checks(); - coap_dtls_set_log_level(coap_log_t::COAP_LOG_DEBUG); - coap_set_log_level(coap_log_t::COAP_LOG_DEBUG); + coap_dtls_set_log_level(coap_log_t_COAP_LOG_DEBUG); + coap_set_log_level(coap_log_t_COAP_LOG_DEBUG); } let mut context = CoapContext::new().unwrap(); context = context_configurator(context); From 9441e02e314ba83aef557e3126edb92e7b8df19a Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Tue, 21 Jan 2025 19:54:50 +0100 Subject: [PATCH 17/26] feat(test): Add example for libcoap-rs+ESP-IDF, update libcoap-sys+ESP-IDF example --- libcoap-sys/examples/esp-idf/Cargo.toml | 4 +- libcoap-sys/examples/esp-idf/src/main.rs | 1 - libcoap/examples/esp-idf/.cargo/config.toml | 16 ++++++++ .../esp-idf/.github/workflows/rust_ci.yml | 41 +++++++++++++++++++ libcoap/examples/esp-idf/.gitignore | 4 ++ libcoap/examples/esp-idf/Cargo.toml | 34 +++++++++++++++ libcoap/examples/esp-idf/build.rs | 3 ++ .../examples/esp-idf/components_esp32c3.lock | 20 +++++++++ libcoap/examples/esp-idf/rust-toolchain.toml | 3 ++ libcoap/examples/esp-idf/sdkconfig.defaults | 21 ++++++++++ libcoap/examples/esp-idf/src/main.rs | 14 +++++++ libcoap/examples/esp-idf/vars.log | 0 12 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 libcoap/examples/esp-idf/.cargo/config.toml create mode 100644 libcoap/examples/esp-idf/.github/workflows/rust_ci.yml create mode 100644 libcoap/examples/esp-idf/.gitignore create mode 100644 libcoap/examples/esp-idf/Cargo.toml create mode 100644 libcoap/examples/esp-idf/build.rs create mode 100644 libcoap/examples/esp-idf/components_esp32c3.lock create mode 100644 libcoap/examples/esp-idf/rust-toolchain.toml create mode 100644 libcoap/examples/esp-idf/sdkconfig.defaults create mode 100644 libcoap/examples/esp-idf/src/main.rs create mode 100644 libcoap/examples/esp-idf/vars.log diff --git a/libcoap-sys/examples/esp-idf/Cargo.toml b/libcoap-sys/examples/esp-idf/Cargo.toml index 12c6fa4e..8f1b550c 100644 --- a/libcoap-sys/examples/esp-idf/Cargo.toml +++ b/libcoap-sys/examples/esp-idf/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "esp-idf" +name = "libcoap-sys-esp-idf-example" version = "0.1.0" authors = ["Hugo Hakim Damer "] edition = "2021" @@ -7,7 +7,7 @@ resolver = "2" rust-version = "1.77" [[bin]] -name = "esp-idf" +name = "libcoap-sys-esp-idf-example" harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors [profile.release] diff --git a/libcoap-sys/examples/esp-idf/src/main.rs b/libcoap-sys/examples/esp-idf/src/main.rs index f60f82b5..42fa7063 100644 --- a/libcoap-sys/examples/esp-idf/src/main.rs +++ b/libcoap-sys/examples/esp-idf/src/main.rs @@ -1,4 +1,3 @@ - use libcoap_sys as _; fn main() { diff --git a/libcoap/examples/esp-idf/.cargo/config.toml b/libcoap/examples/esp-idf/.cargo/config.toml new file mode 100644 index 00000000..11494290 --- /dev/null +++ b/libcoap/examples/esp-idf/.cargo/config.toml @@ -0,0 +1,16 @@ +[build] +target = "riscv32imc-esp-espidf" + +[target.riscv32imc-esp-espidf] +linker = "ldproxy" +runner = "espflash flash --monitor" +rustflags = [ "--cfg", "espidf_time64"] + +[unstable] +build-std = ["std", "panic_abort"] + +[env] +MCU="esp32c3" +# Note: this variable is not used by the pio builder (`cargo build --features pio`) +ESP_IDF_VERSION = "v5.2.3" + diff --git a/libcoap/examples/esp-idf/.github/workflows/rust_ci.yml b/libcoap/examples/esp-idf/.github/workflows/rust_ci.yml new file mode 100644 index 00000000..e7e9b1ec --- /dev/null +++ b/libcoap/examples/esp-idf/.github/workflows/rust_ci.yml @@ -0,0 +1,41 @@ +name: Continuous Integration + +on: + push: + paths-ignore: + - "**/README.md" + pull_request: + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + rust-checks: + name: Rust Checks + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + action: + - command: build + args: --release + - command: fmt + args: --all -- --check --color always + - command: clippy + args: --all-targets --all-features --workspace -- -D warnings + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Rust + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: nightly + components: rust-src rustfmt clippy + - name: Enable caching + uses: Swatinem/rust-cache@v2 + - name: Install ldproxy + run: cargo install ldproxy + - name: Run command + run: cargo ${{ matrix.action.command }} ${{ matrix.action.args }} diff --git a/libcoap/examples/esp-idf/.gitignore b/libcoap/examples/esp-idf/.gitignore new file mode 100644 index 00000000..73a638b5 --- /dev/null +++ b/libcoap/examples/esp-idf/.gitignore @@ -0,0 +1,4 @@ +/.vscode +/.embuild +/target +/Cargo.lock diff --git a/libcoap/examples/esp-idf/Cargo.toml b/libcoap/examples/esp-idf/Cargo.toml new file mode 100644 index 00000000..92046ac0 --- /dev/null +++ b/libcoap/examples/esp-idf/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "libcoap-esp-idf-example" +version = "0.1.0" +authors = ["Hugo Hakim Damer "] +edition = "2021" +resolver = "2" +rust-version = "1.77" + +[[bin]] +name = "libcoap-esp-idf-example" +harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors + +[profile.release] +opt-level = "s" + +[profile.dev] +debug = true # Symbols are nice and they don't increase the size on Flash +opt-level = "z" + +[features] +default = [] + +experimental = ["esp-idf-svc/experimental"] + +[dependencies] +log = "0.4" +esp-idf-svc = { version = "0.51", features = ["critical-section", "embassy-time-driver", "embassy-sync"] } +esp-idf-sys = { version = "0.36.1" } +libcoap-rs = { version = "*", path = "../../" } + +[build-dependencies] +embuild = "0.33" + +[workspace] diff --git a/libcoap/examples/esp-idf/build.rs b/libcoap/examples/esp-idf/build.rs new file mode 100644 index 00000000..112ec3f7 --- /dev/null +++ b/libcoap/examples/esp-idf/build.rs @@ -0,0 +1,3 @@ +fn main() { + embuild::espidf::sysenv::output(); +} diff --git a/libcoap/examples/esp-idf/components_esp32c3.lock b/libcoap/examples/esp-idf/components_esp32c3.lock new file mode 100644 index 00000000..b6391610 --- /dev/null +++ b/libcoap/examples/esp-idf/components_esp32c3.lock @@ -0,0 +1,20 @@ +dependencies: + espressif/coap: + component_hash: a5d1b781b15980d9af136b5e63315dd5f5ec00215cc755f395ad2fb4977c7668 + dependencies: + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com/ + type: service + version: 4.3.5~3 + idf: + source: + type: idf + version: 5.2.3 +direct_dependencies: +- espressif/coap +manifest_hash: 13fcf89b865b0f073a765a0dfdcf062a0063b4af7a631d3d37ef94c920538a25 +target: esp32c3 +version: 2.0.0 diff --git a/libcoap/examples/esp-idf/rust-toolchain.toml b/libcoap/examples/esp-idf/rust-toolchain.toml new file mode 100644 index 00000000..f70d2254 --- /dev/null +++ b/libcoap/examples/esp-idf/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "nightly" +components = ["rust-src"] diff --git a/libcoap/examples/esp-idf/sdkconfig.defaults b/libcoap/examples/esp-idf/sdkconfig.defaults new file mode 100644 index 00000000..81361c8b --- /dev/null +++ b/libcoap/examples/esp-idf/sdkconfig.defaults @@ -0,0 +1,21 @@ +# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K) +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000 + +# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default). +# This allows to use 1 ms granularity for thread sleeps (10 ms by default). +#CONFIG_FREERTOS_HZ=1000 + +# Workaround for https://github.com/espressif/esp-idf/issues/7631 +#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n +#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n + +CONFIG_COAP_CLIENT_SUPPORT=y +CONFIG_COAP_SERVER_SUPPORT=y +CONFIG_COAP_TCP_SUPPORT=y +CONFIG_COAP_OSCORE_SUPPORT=y +CONFIG_COAP_Q_BLOCK=y +CONFIG_COAP_SERVER_SUPPORT=y +CONFIG_COAP_THREAD_RECURSIVE_CHECK=y +CONFIG_COAP_THREAD_SAFE=y +CONFIG_COAP_OBSERVE_PERSIST=y +CONFIG_COAP_WEBSOCKETS=y \ No newline at end of file diff --git a/libcoap/examples/esp-idf/src/main.rs b/libcoap/examples/esp-idf/src/main.rs new file mode 100644 index 00000000..c5f1bcc6 --- /dev/null +++ b/libcoap/examples/esp-idf/src/main.rs @@ -0,0 +1,14 @@ +use libcoap_rs::CoapContext; + +fn main() { + // It is necessary to call this function once. Otherwise some patches to the runtime + // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 + esp_idf_svc::sys::link_patches(); + + // Bind the log crate to the ESP Logging facilities + esp_idf_svc::log::EspLogger::initialize_default(); + + log::info!("Hello, world!"); + + let context = CoapContext::new().unwrap(); +} diff --git a/libcoap/examples/esp-idf/vars.log b/libcoap/examples/esp-idf/vars.log new file mode 100644 index 00000000..e69de29b From cb3ef1969e276d691d0c8d55e4d46b96a402186a Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Tue, 21 Jan 2025 19:55:18 +0100 Subject: [PATCH 18/26] docs(sys): Start rewriting the libcoap-sys documentation --- libcoap-sys/Cargo.toml | 23 ++++++-- libcoap-sys/src/lib.rs | 119 ++++++++++++++++++++++------------------- 2 files changed, 83 insertions(+), 59 deletions(-) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 2734cfeb..16f0d8e3 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -23,9 +23,26 @@ build = "build/main.rs" rust-version = "1.81.0" [features] -# The default features match those of libcoaps configure script, except for dtls, which is disabled here because it -# requires a backend to be set manually. -default = ["oscore", "ipv4", "ipv6", "af-unix", "tcp", "websockets", "async", "observe-persist", "q-block", "thread-safe", "thread-recursive-lock-detection", "server", "client", "epoll", "vendored"] +# The default features match those of libcoap's configure script for +# the minimum supported version. +default = [ + "dtls", + "oscore", + "ipv4", + "ipv6", + "af-unix", + "tcp", + "websockets", + "async", + "observe-persist", + "q-block", + "thread-safe", + "thread-recursive-lock-detection", + "server", + "client", + "epoll" + # TODO add proxy +] # While not specified here due to limitations in Cargo's syntax, the DTLS feature depends on one of the DTLS backends # being enabled. # If you are developing a library based on libcoap-sys and do not care about the DTLS backend, enable the dtls feature diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 783439cd..5f60cdcb 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -14,71 +14,78 @@ //! made in this library are generated automatically using bindgen, for further documentation on how //! to use them, refer to the [libcoap documentation](https://libcoap.net/documentation.html). //! -//! In most cases you probably want to use the safe wrapper provided by the libcoap crate (or -//! another coap library written in pure rust such as [coap-rs](https://github.com/covertness/coap-rs)) +//! In most cases you probably want to use the safe wrapper provided by the libcoap-rs crate or +//! another coap library written in pure Rust such as [coap-rs](https://github.com/covertness/coap-rs) //! instead. //! -//! Cargo Features -//! -------------- -//! We currently define a number of features that affect the functionality provided by this wrapper -//! and required by the linked libcoap library. +//! The TLDR for building libcoap-sys (and resolving the most common Build Issues) +//! ------------------------------------------------------------------------------ +//! It is strongly recommended that you read the remainder of this page in order to fully understand +//! the build process and possible causes of errors, especially if you're cross-compiling or +//! building for embedded targets. +//! +//! However, if you lack the time to do so, the following instructions should work in most cases: +//! +//! 1. Add a dependency to this crate and add all features you need for your crate to work. +//! Call [`coap_startup_with_feature_checks()`] instead of [`coap_startup()`] during +//! initialization to ensure that all of these features are actually available in the linked +//! version of `libcoap`. //! -//! Features affecting functionality: -//! - `dtls`: Enable usage of DTLS for transport security. Supports a number of different backends. +//! 2. If you require DTLS support and run into `Required feature "dtls-(psk|pki|rpk|...)" is not +//! supported by libcoap` errors, manually select a DTLS library that supports all of your +//! required DTLS features by setting the `LIBCOAP_RS_DTLS_BACKEND` environment variable to your +//! desired choice (the library name in all-lowercase should work). //! -//! Note that while not specified here due to limitations in Cargo's syntax, the DTLS feature -//! depends on one of the DTLS backends being enabled, and failing to enable a DTLS backend will -//! result in a build failure. -//! -//! If you are developing a library based on libcoap-sys and do not care about the DTLS backend, -//! enable the dtls feature and let the user decide on the backend to use, either by -//! re-exporting these features (see [the Cargo Book](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features)) -//! or by assuming that the user will use libcoap-sys as a dependency and enable the -//! corresponding backend feature themselves, relying on Cargo's feature unification to enable -//! it for your crate as well. -//! -//! Also note that the backends are **mutually exclusive** due to the C library having these -//! backends as mutually exclusive features. If multiple backends are enabled (e.g. because -//! multiple dependencies use libcoap-sys and use different backends), we select one based on -//! the auto-detection order specified in [the libcoap configure script](https://github.com/obgm/libcoap/blob/develop/configure.ac#L494) -//! (gnutls > openssl > mbedtls > tinydtls). -//! - `dtls_backend_(openssl|gnutls|mbedtls|tinydtls)`: Enable the corresponding DTLS backend. -//! -//! Note that enabling the OpenSSL, GnuTLS, TinyDTLS or MbedTLS backend will also require the -//! appropriate library to be available (hence the dependency on the corresponding sys-crate). -//! The TinyDTLS backend is built using a vendored (and statically linked) version of TinyDTLS -//! by default, see the tinydtls-sys crate for more info. -//! Choosing a DTLS backend also means that the license terms of these libraries may apply to -//! you. See the relevant parts of the [libcoap license file](https://github.com/obgm/libcoap/blob/develop/LICENSE) -//! for more information. -//! - `tcp` (default): Enable CoAP over TCP support -//! - `async` (default): Enable async functionality. -//! -//! Note that this async functionality is not translated to Rust's async language functionality, -//! but instead adds functionality to the underlying C library to allow for making asynchronous -//! requests (i.e. function calls that return before the response has arrived). +//! 3. If you're building a binary crate (or tests, examples, ...) and are getting non-DTLS-related +//! `Required feature "" is not supported by libcoap` errors, enable the `vendored` +//! feature to build and statically link a version of libcoap that supports exactly the features +//! you requested. +//! +//! 4. Inspect your dependency tree to determine whether you already have a DTLS library's sys-crate +//! (`openssl-sys`, `tinydtls-sys` or `mbedtls-sys-auto`) in your dependency tree. +//! If this is the case, enable the `dtls--sys` feature for all of them. +//! This may resolve issues related to linking multiple versions of the same library at once, and +//! could also help in reducing binary size. +//! +//! If you're still unable to compile `libcoap-sys`, refer to the documentation below. +//! If the documentation below does not solve your issue, feel free to open an issue +//! [on GitHub](https://github.com/namib-project/libcoap-rs/) and ask for help. +//! +//! Cargo Features +//! -------------- +//! Most features specified in this crate's Cargo.toml directly correspond to a feature that can be +//! enabled or disabled in libcoap's configure-script and/or CMake configuration, refer to the +//! libcoap documentation for more details on these features. //! -//! Integrating libcoap into Rusts async language features is out of scope for this crate, but -//! might be implemented later on in the libcoap (safe abstraction) crate. -//! - `server` (default): Enable code related to server functionality -//! - `client` (default): Enable code related to client functionality -//! - `epoll` (default): Allow the underlying C library to perform IO operations using epoll. +//! The `default` feature should match the default features enabled in the configure script of the +//! minimum supported version of libcoap. //! -//! Other features: -//! - `vendored` (default): Use a vendored version of libcoap instead of the system-provided one. -//! Note that `vendored` implies `static`. -//! - `static` (default): Perform static linking to the libcoap C library. +//! Depending on the build system and linked version of libcoap, the features actually provided may +//! differ from the ones indicated by the crate features. +//! If you want to ensure that all features that are enabled for this crate are actually supported +//! by the linked version of libcoap, you may call [coap_startup_with_feature_checks]. //! -//! ### Note on features affecting functionality -//! The features that add or remove functionality do not change the generated bindings as libcoap's -//! headers (unlike the source files themselves) are not affected by the corresponding `#define`s. +//! Aside from the features relating to libcoap functionality, the following features may also be +//! enabled for this crate: +//! - `vendored`: Build and statically link against a version of libcoap bundled with this crate +//! instead of using a system-provided one[^1]. +//! - `dtls--sys`: Allows the [vendored](#TODO) libcoap version to link against the +//! same version of a DTLS library that is used by the corresponding -sys +//! crate[^2]. +//! Note, however, that this does not imply that this DTLS library *will* be used, it +//! should +//! - `dtls--sys-vendored` instructs the sys-crate of the DTLS library corresponding +//! to the feature name to use a vendored version of the underlying library (implies +//! `dtls--sys`). //! -//! For library users that link to a shared version of libcoap, this means that the feature flags -//! do not have any effect and the supported features will correspond directly to the features -//! enabled during the build of the shared libcoap instance (using the configure-script). +//! [^1]: Note that when building for the ESP-IDF, this feature will be a no-op, as the version +//! provided by the ESP-IDF will always be used. +//! [^2]: In the case of `mbedtls`, `mbedtls-sys-auto` is used instead, as `mbedtls-sys` is +//! unmaintained. //! -//! For users of the vendored version of libcoap (see the `vendored` feature), the supported -//! features of the vendored libcoap will be set to match the cargo features during build. +//! Build Process +//! ------------- +//! [TODO] // Bindgen translates the C headers, clippy's and rustfmt's recommendations are not applicable here. #![allow(clippy::all)] From cdd49d194dd9ecd1b2c8abb5d173901db003750a Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Wed, 22 Jan 2025 02:53:14 +0100 Subject: [PATCH 19/26] docs(sys|lib): Finish rewrite of build instructions --- .idea/libcoap-rs.iml | 4 + libcoap-sys/Cargo.toml | 22 +- libcoap-sys/build/build_system/manual.rs | 6 +- libcoap-sys/build/build_system/mod.rs | 30 +++ libcoap-sys/build/build_system/vendored.rs | 14 +- libcoap-sys/build/main.rs | 21 +- libcoap-sys/build/metadata.rs | 4 + libcoap-sys/src/lib.rs | 236 ++++++++++++++++++++- libcoap/src/lib.rs | 20 +- libcoap/src/prng.rs | 4 +- 10 files changed, 306 insertions(+), 55 deletions(-) diff --git a/.idea/libcoap-rs.iml b/.idea/libcoap-rs.iml index 84e5d2c4..6c1d6fb4 100644 --- a/.idea/libcoap-rs.iml +++ b/.idea/libcoap-rs.iml @@ -5,6 +5,10 @@ + + + + diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 16f0d8e3..ce9c0df1 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -43,18 +43,6 @@ default = [ "epoll" # TODO add proxy ] -# While not specified here due to limitations in Cargo's syntax, the DTLS feature depends on one of the DTLS backends -# being enabled. -# If you are developing a library based on libcoap-sys and do not care about the DTLS backend, enable the dtls feature -# and let the user decide on the backend to use, either by re-exporting these features (see -# https://doc.rust-lang.org/cargo/reference/features.html#dependency-features) or by assuming that the user will use -# libcoap-sys as a dependency and enable the corresponding backend feature themselves, relying on Cargo's feature -# unification to enable it for your crate as well. -# Also note that the backends are **mutually exclusive** due to the C library having these backends as mutually -# exclusive features. If multiple backends are enabled (e.g. because multiple dependencies use libcoap-sys and use -# different backends), we select one based on the auto-detection order specified in -# https://github.com/obgm/libcoap/blob/develop/configure.ac#L494 (gnutls > openssl > mbedtls > tinydtls). - # Allows using the version of OpenSSL provided by openssl-sys instead of a system-provided one. # Note that this does not enforce the use of OpenSSL in libcoap, see the crate-level documentation for more info. @@ -70,22 +58,14 @@ dtls-tinydtls-sys = ["dep:tinydtls-sys", "tinydtls-sys/ecc", "tinydtls-sys/psk"] # Tell the tinydtls-sys version that is possibly used by libcoap-sys to use the vendored version of its library. dtls-tinydtls-sys-vendored = ["dtls-tinydtls-sys", "tinydtls-sys/vendored"] -# Enabling this feature will force libcoap-sys to be built with and statically linked to a vendored version of libcoap, -# which will be built by the build-script before building libcoap-sys. -# which will be built by the build-script before building libcoap-sys. +# Enabling this feature will allow libcoap-sys to be built with and statically linked to a vendored version of libcoap, # This way, it is no longer required to have libcoap installed to use this crate. vendored = [] # --- FEATURE FLAGS --- -# Note that setting the feature flags currently has no effect on the generated Rust code, because the libcoap headers do -# not use these feature flags. They only affect the features built into the vendored C library (if enabled). - # Enable this feature to enable/require CoAP over DTLS support in the C library. -# Important: also read the section on DTLS backends before enabling this feature. # Corresponding libcoap configure flag: --enable-dtls dtls = [] # Enable this feature to enable/require TLS support in addition to DTLS support. -# Note: Will also enable the TCP and DTLS features, so consider the above section regarding DTLS backends before + -# enabling this. tls = ["dtls", "tcp"] # Enable this feature to enable/require OSCORE functionality in the C library. # Corresponding libcoap configure flag: --enable-oscore diff --git a/libcoap-sys/build/build_system/manual.rs b/libcoap-sys/build/build_system/manual.rs index 48cf6ceb..b251d505 100644 --- a/libcoap-sys/build/build_system/manual.rs +++ b/libcoap-sys/build/build_system/manual.rs @@ -42,11 +42,7 @@ impl ManualBuildSystem { }; let use_static = match env::var("LIBCOAP_RS_STATIC") { Ok(v) => { - if v == "0" || v == "" { - false - } else { - true - } + !(v == "0" || v.is_empty()) }, Err(VarError::NotPresent) => false, Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_STATIC environment variable."), diff --git a/libcoap-sys/build/build_system/mod.rs b/libcoap-sys/build/build_system/mod.rs index 3ae93d1e..cff43f41 100644 --- a/libcoap-sys/build/build_system/mod.rs +++ b/libcoap-sys/build/build_system/mod.rs @@ -11,12 +11,42 @@ pub mod manual; pub mod pkgconfig; pub mod vendored; +/// Trait that is implemented by build systems for libcoap. +/// +/// It is assumed that the constructor structs implementing this trait already performs all +/// necessary steps to link against libcoap, and that only binding generation and compile-time +/// checks remain. +/// +/// If you want to implement your own build system, you may want to use the `manual` build system +/// as a basis. +/// +/// In order to implement the compile-time checks, you may want to use +/// [`LibcoapDefineParser`](crate::bindings::LibcoapDefineParser), at least in cases where you have +/// the corresponding `coap_defines.h` header file available. pub trait BuildSystem { + /// Returns the set of features that are supported by the linked version of libcoap, or `None` + /// if this detection is not possible or has not been performed yet. + /// + /// It is assumed that after `generate_bindings()` is called, a `None` return value indicates + /// that compile-time feature detection is unsupported. fn detected_features(&self) -> Option>; + /// Returns the DTLS backend that has been used in the `libcoap` version this build + /// system built against, or `None` if this detection is not possible or has not been performed + /// yet. + /// + /// It is assumed that after `generate_bindings()` is called, a `None` return value indicates + /// that compile-time DTLS library detection is unsupported. fn detected_dtls_backend(&self) -> Option; + /// Returns the `libcoap` library version this build system built against, or `None` if this + /// detection is not possible or has not been performed yet. + /// + /// It is assumed that after `generate_bindings()` is called, a `None` return value indicates + /// that compile-time DTLS library detection is unsupported. fn version(&self) -> Option; + /// Generate Rust bindings to the `libcoap` C library that we linked against and return a + /// `PathBuf` to the generated bindings file to use. fn generate_bindings(&mut self) -> Result; } diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index 14713973..5a0c6e5c 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -239,7 +239,7 @@ impl VendoredBuildSystem { } #[cfg(feature = "dtls-tinydtls-sys")] - fn configure_tinydtls_sys(mut build_config: &mut autotools::Config) -> Result<(Option, bool)> { + fn configure_tinydtls_sys(build_config: &mut autotools::Config) -> Result<(Option, bool)> { if env::var_os("TinyDTLS_CFLAGS").is_some() || env::var_os("TinyDTLS_LIBS").is_some() { // Do not use tinydtls-sys if the user manually set either the corresponding LIBS or // CFLAGS variable. @@ -252,7 +252,7 @@ impl VendoredBuildSystem { .expect("tinydtls-sys dependency has been added, but DEP_TINYDTLS_INCLUDE has not been set"); let tinydtls_libs = env::var_os("DEP_TINYDTLS_LIBS") .expect("tinydtls-sys dependency has been added, but DEP_TINYDTLS_LIBS has not been set"); - build_config = build_config.env( + build_config.env( "TinyDTLS_CFLAGS", format!( "-I{} -I{}", @@ -267,7 +267,7 @@ impl VendoredBuildSystem { ); // Need to set TinyDTLS_LIBS explicitly to force static linking (TinyDTLS also builds a shared version of the library). - build_config = build_config.env( + build_config.env( "TinyDTLS_LIBS", format!( "-L{} -l:libtinydtls.a", @@ -283,7 +283,7 @@ impl VendoredBuildSystem { } #[cfg(feature = "dtls-openssl-sys")] - fn configure_openssl_sys(build_config: &mut autotools::Config) -> Result<(Option, bool)> { + fn configure_openssl_sys(_build_config: &mut autotools::Config) -> Result<(Option, bool)> { if env::var_os("OpenSSL_CFLAGS").is_some() || env::var_os("OpenSSL_LIBS").is_some() { // Do not use tinydtls-sys if the user manually set either the corresponding LIBS or // CFLAGS variable. @@ -307,7 +307,7 @@ impl VendoredBuildSystem { #[cfg(feature = "dtls-mbedtls-sys")] fn configure_mbedtls_sys( out_dir: &Path, - mut build_config: &mut autotools::Config, + build_config: &mut autotools::Config, ) -> Result<(Option, bool)> { if env::var_os("MbedTLS_CFLAGS").is_some() || env::var_os("MbedTLS_LIBS").is_some() { // Do not use tinydtls-sys if the user manually set either the corresponding LIBS or @@ -342,7 +342,7 @@ impl VendoredBuildSystem { .join("build") .join("library"); - build_config = build_config.env( + build_config.env( "MbedTLS_CFLAGS", format!( "-I{} -I{}", @@ -352,7 +352,7 @@ impl VendoredBuildSystem { .expect("DEP_MBEDTLS_INCLUDE is not a valid UTF-8 string") ), ); - build_config = build_config.env( + build_config.env( "MbedTLS_LIBS", format!( "-L{0} -l:libmbedtls.a -l:libmbedcrypto.a -l:libmbedx509.a", diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index eb3a6db9..51baf973 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -97,6 +97,24 @@ fn main() -> Result<()> { if let Some(dtls_backend) = build_system.detected_dtls_backend() { println!("cargo::metadata=dtls_backend={}", dtls_backend.as_str()); println!("cargo::rustc-cfg=dtls_backend=\"{}\"", dtls_backend.as_str()); + + if !bypass_compile_time_feature_checks { + if let Some(req_backend) = requested_dtls_backend { + assert_eq!(req_backend, dtls_backend, + concat!( + "the libcoap-rs compile-time check has determined that the DTLS library\n", + "the used version of libcoap linked against ({}) does not match the one set in LIBCOAP_RS_DTLS_BACKEND ({}).\n", + "If you are certain that this check is mistaken (e.g., because you are cross-compiling), you\n", + "may bypass this check by setting the `LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS` environment\n", + "variable to any non-zero value.\n", + "Be aware, however, that this might lead to more cryptic errors if the requested features are\n", + "not available after all.\n", + "Refer to the libcoap-sys crate-level documentation for more information: https://docs.rs/libcoap-sys." + ), dtls_backend.as_str(), req_backend.as_str()) + } else if bypass_compile_time_feature_checks { + println!("cargo:warning=You have bypassed the libcoap-sys compile-time DTLS library check.") + } + } } match build_system.detected_features() { @@ -115,7 +133,8 @@ fn main() -> Result<()> { "may bypass this check by setting the `LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS` environment\n", "variable to any non-zero value.\n", "Be aware, however, that this might lead to more cryptic errors if the requested features are\n", - "not available after all." + "not available after all.\n", + "Refer to the libcoap-sys crate-level documentation for more information: https://docs.rs/libcoap-sys." ), missing_features .iter() diff --git a/libcoap-sys/build/metadata.rs b/libcoap-sys/build/metadata.rs index 2427ab47..631906b3 100644 --- a/libcoap-sys/build/metadata.rs +++ b/libcoap-sys/build/metadata.rs @@ -12,6 +12,8 @@ use enumset::{EnumSet, EnumSetType}; /// increased if building on older versions causes issues with libcoap-sys specifically. pub const MINIMUM_LIBCOAP_VERSION: &str = "4.3.5"; +/// Information about a version of libcoap that was parsed by +/// [`LibcoapDefineParser`](crate::bindings::LibcoapDefineParser). #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] pub struct LibcoapDefineInfo { pub version: Option, @@ -19,6 +21,7 @@ pub struct LibcoapDefineInfo { pub supported_features: EnumSet, } +/// An optional feature that may or may not be supported by a version of `libcoap`. #[derive(EnumSetType, Debug)] pub enum LibcoapFeature { AfUnix, @@ -191,6 +194,7 @@ impl LibcoapFeature { } } +/// A DLTS library that can be used by `libcoap` for encryption support. #[derive(Debug, EnumSetType, Hash)] pub enum DtlsBackend { GnuTls, diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 5f60cdcb..21e69be6 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -51,7 +51,7 @@ //! If the documentation below does not solve your issue, feel free to open an issue //! [on GitHub](https://github.com/namib-project/libcoap-rs/) and ask for help. //! -//! Cargo Features +//! Optional Features //! -------------- //! Most features specified in this crate's Cargo.toml directly correspond to a feature that can be //! enabled or disabled in libcoap's configure-script and/or CMake configuration, refer to the @@ -63,20 +63,30 @@ //! Depending on the build system and linked version of libcoap, the features actually provided may //! differ from the ones indicated by the crate features. //! If you want to ensure that all features that are enabled for this crate are actually supported -//! by the linked version of libcoap, you may call [coap_startup_with_feature_checks]. +//! by the linked version of libcoap, you may call [`coap_startup_with_feature_checks()`]. //! //! Aside from the features relating to libcoap functionality, the following features may also be //! enabled for this crate: //! - `vendored`: Build and statically link against a version of libcoap bundled with this crate //! instead of using a system-provided one[^1]. -//! - `dtls--sys`: Allows the [vendored](#TODO) libcoap version to link against the +//! - `dtls--sys`: Allows the [vendored](#vendored-build-system) libcoap version to link against the //! same version of a DTLS library that is used by the corresponding -sys //! crate[^2]. -//! Note, however, that this does not imply that this DTLS library *will* be used, it -//! should +//! Note, however, that this does not imply that this DTLS library *will* be used, refer to +//! the [documentation below](#dtls-library-selection) for more information. +//! +//! If a different build system than `vendored` is used, this feature is effectively a no-op. //! - `dtls--sys-vendored` instructs the sys-crate of the DTLS library corresponding //! to the feature name to use a vendored version of the underlying library (implies //! `dtls--sys`). +//! - `dtls-(cid|psk|pki|pkcs11|rpk)`: Require support for specific DTLS features in `libcoap`. +//! These features can not be enabled explicitly while building `libcoap`, support for them is +//! automatically made available based on the used DTLS library (see +//! [the corresponding section below](#dtls-library-selection)). +//! +//! Enabling these features will add appropriate checks during compile- and/or runtime +//! initialization to ensure these features are available in the used DTLS library (or panic +//! otherwise). //! //! [^1]: Note that when building for the ESP-IDF, this feature will be a no-op, as the version //! provided by the ESP-IDF will always be used. @@ -85,7 +95,212 @@ //! //! Build Process //! ------------- -//! [TODO] +//! In general, `libcoap-sys` supports four different build systems, which will be explained in more +//! detail in the following sections: +//! - `vendored`: Build libcoap from source using a bundled version of the library (requires the +//! `vendored` feature to be enabled. +//! - `pkgconfig`: Link against a system-provided version of libcoap, obtaining the library and +//! include paths using the `pkg-config` utility. +//! - `manual`: Provide include and library directories+compiler/linker flags via environment +//! variables. +//! - `espidf`: Build for the ESP family of microcontrollers using the ESP-IDF framework (used +//! instead of the regular `vendored` build if the build system is set to `vendored` and +//! building for an ESP-IDF Rust target). +//! +//! The build system that should be used can be specified manually by setting the +//! `LIBCOAP_RS_BUILD_SYSTEM` environment variable to the corresponding value. +//! +//! If you have explicitly specified a build system and building using that system fails, no other +//! system will be tried. +//! +//! If you do not explicitly provide a build system to use, the build script will follow these +//! steps to determine a suitable build system: +//! +//! 1. If the `vendored` crate feature is enabled, or we are building for the ESP-IDF, act as if the +//! build system is set to `vendored`. If a vendored build is attempted and fails, return with an +//! error and do not try anything else. +//! 2. Otherwise, try `pkgconfig` first. +//! 3. If `pkgconfig` doesn't work, fall back to `manual` (which will fail if the environment +//! variables aren't set). +//! 4. If `manual` doesn't work, return an error indicating the issues with all previously attempted +//! build systems. +//! +//! ## Generic Information (applies to all build systems) +//! The following information applies to all build systems (although some specifics may be detailed +//! in the respective build system's section). +//! +//! ### C Standard Library Functions +//! Some `libcoap` functions utilize types from the C standard library (e.g., `sockaddr_in` in +//! `coap_address_t`). +//! +//! For most targets, the data types defined in the [`libc`](https://docs.rs/libc/latest/libc/) +//! crate will be used to provide those data types. +//! However, some targets (especially embedded ones such as `espidf`) will use a different library +//! instead, which may cause compilation issues in code that assumes `libc` data types to be +//! compatible. +//! +//! For your convenience, this crate re-exports the used standard library crate as the `c_stdlib` +//! module. For best interoperability, you should `use` this module instead of using the actual +//! crates directly to import the required data types. +//! +//! ### DTLS Library Selection +//! +//! In order to provide DTLS support, `libcoap` must be combined with a DTLS library/backend. +//! DTLS libraries are mutually exclusive, and multiple versions of `libcoap` linked against +//! different DTLS libraries may be installed in a system simultaneously, so `libcoap-sys` must +//! decide on a variant of `libcoap` to link against during build. +//! +//! While the default mechanism for determining a DTLS library differs between build systems, you +//! may select a DTLS library explicitly by setting the `LIBCOAP_RS_DTLS_BACKEND` environment +//! variable to any of the supported values (`gnutls`, `openssl`, `mbedtls`, `tinydtls`, or +//! `wolfssl`). Refer to the build-system-specific documentation for information about supported +//! DTLS libraries and specifics. +//! +//! Note that some DTLS-related features (such as `dtls-(cid|psk|pki|pkcs11|rpk)`) are dependent on +//! the used DTLS backend, refer to [the `coap_encryption(3)` man page](https://libcoap.net/doc/reference/4.3.5/man_coap_encryption.html) +//! for information on supported features for each DTLS library. +//! +//! ### Feature Support and Compile-Time/Initialization Checks +//! During compilation, each build system will attempt to ensure that the used version of `libcoap` +//! does in fact support all [features](#optional-features) that were enabled in `Cargo.toml`. +//! The exact method differs based on each build system, but most will attempt to parse the +//! `coap3/coap_defines.h` header file in order to determine missing features (with `espidf` being a +//! notable exception). +//! +//! If a build system detects that a requested feature is missing, an appropriate error message will +//! be returned. In most cases, these errors must be resolved by linking to a different version of +//! `libcoap`. +//! +//! Unfortunately, for various reasons, this compile-time feature check may produce false +//! positive and/or false negative results (especially when cross compiling[^3]) +//! is not available for all features (especially ones dependent on the DTLS library) and may not +//! even be available at all on some platforms. +//! +//! Therefore, library users should assume that the compile-time checks may not provide accurate +//! results, and should call [`coap_startup_with_feature_checks()`] during initialization to perform +//! run-time checks for all requested features. This run-time check will always work and be +//! accurate. +//! +//! Lastly, if you encounter a false positive error (i.e., a compile time error that indicates that +//! some feature is missing, even though you are 100% certain that it is available), you may bypass +//! the compile-time checks by setting `LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS` to any non-zero +//! value. +//! Note, however, that this might lead to cryptic errors if your assumption was wrong and the +//! feature is not available after all. +//! +//! In most cases, a false positive might be caused by the include paths/header files used for +//! binding generation refering to a different version of `libcoap` than the one that is linked +//! against, which could also cause difficult-to-debug issues and indicates a more severe problem +//! with the build process. +//! +//! [^3]: For this reason, using this method [is noted to be unsafe in `libcoap`'s documentation](https://libcoap.net/doc/reference/4.3.5/man_coap_supported.html). +//! +//! ## Vendored Build System +//! +//! The vendored build system uses a bundled version of libcoap (usually the latest stable version +//! at the time of release) to build and statically link against. +//! Under the hood, it uses the [`autotools`](https://docs.rs/autotools/latest/autotools/) crate to +//! configure and run the build process, and you may therefore customize the build's compiler and +//! linker flags by setting the environment variables used in `libcoap`'s `configure` script. +//! +//! This build system will enable only those features in `libcoap` that are requested as +//! `Cargo.toml` features, and will explicitly disable all other ones. +//! +//! If a DTLS library is explicitly selected by the user, it will instruct libcoap to link against +//! that library by setting the corresponding `--with-` configure flag. +//! +//! If you enable the `dtls--sys` features and do not set the `_CFLAGS` +//! or `_LIBS` environment variables, this build system will set these environment +//! variables to ensure that if this DTLS library is the one that libcoap uses, we link against +//! exactly the same version as used in the `-sys` crate. +//! This is especially relevant if those crates also provide a `vendored` version in order to avoid +//! multiple versions of the same library being in use. +//! If a different DTLS library is used, this feature should have no effect (it will set the +//! environment variable, but `libcoap` will ignore it). +//! +//! If you do not specify a DTLS library, this build system will follow the same default order that +//! libcoap does (gnutls > openssl > wolfssl > mbedtls > tinydtls), unless you enabled one of the +//! `dtls--sys` features, in which case those will have priority. +//! If multiple of these features are enabled, they are prioritized in the same order as used by +//! `libcoap` (openssl > mbedtls > tinydtls). +//! +//! ## `pkg-config` Build System +//! +//! The `pkg-config` build system utilizes the `pkg-config` utility available on most Unix-like +//! systems to link against a system-provided version of `libcoap`. +//! To do so, it uses the [`pkg_config`](https://docs.rs/pkg-config/latest/pkg_config/) crate, and +//! you may therefore customize the build process by setting the environment variables described in +//! that library's documentation (which may be of special relevance if you try to cross compile). +//! +//! By default, it will probe `pkg-config` for a library with the name `libcoap-3`, which will +//! usually symlink to the DTLS library variant of libcoap that was installed most recently. +//! If you have explicitly requested use of a specific DTLS library, this build system will attempt +//! to find the `libcoap-3-` library instead. +//! +//! However, library selection does not take into account any other requested features (i.e., it +//! will not check for feature support before generating the bindings), but will use header-based +//! compile-time feature checks (see the general section) to ensure support for all required +//! features after binding generation. +//! +//! The `dtls--sys` features have no effect on this build system, but note that static +//! linking against a system-provided version of `libcoap` may cause issues if it causes multiple +//! versions of the same DTLS library to be statically linked into the same Rust binary. +//! +//! ## `manual` Build System +//! +//! This build system is intended as a fallback solution if all other options fail. It will attempt +//! to generate bindings and link against the version of `libcoap` that is described by the +//! following environment variables: +//! +//! - `LIBCOAP_RS_INCLUDE_DIRS`: Paths that should be added to `clang`'s include path to search for +//! header files (e.g., `/usr/local/include`, _not_ +//! `/usr/local/include/coap3`). Multiple values are separated by +//! colons (`:`). +//! - `LIBCOAP_RS_LIB_DIRS`: Paths that should be added to `rustc`'s library path to search for +//! object files to link against. Multiple values are separated by +//! colons (`:`). +//! - `LIBCOAP_RS_STATIC`: Set to any non-zero and non-empty value to instruct `rustc` to use +//! static linking instead of dynamic linking for `libcoap`. +//! - `LIBCOAP_RS_ADDITIONAL_LIBRARIES`: Additional libraries (such as DTLS libraries) that should +//! be linked against, separated by colons (`:`). +//! Note that these will be added after `libcoap`, and that the order +//! in which they are specified matters for most linkers. +//! You may also request static linking by prepending `static=` to the +//! library name. +//! +//! ## `espidf` Build System +//! +//! This build system will be used instead of the regular `vendored` build if you are building for +//! targets that are based on the `ESP-IDF`. +//! +//! If `libcoap-sys` is a direct dependency, it will automatically enable the +//! [`espressif/coap`](https://components.espressif.com/components/espressif/coap) component in +//! order to instruct `esp-idf-sys` to compile and link `libcoap` and generate bindings for it. +//! +//! If you encounter errors that indicate that the `espressif/coap` component may not be enabled in +//! the ESP-IDF, this could have the following reasons: +//! - You may have to run `cargo clean`, as the `esp-idf-sys` build script does not always detect +//! changes in requested extra components properly. +//! - `libcoap-sys` is a transient dependency: the `esp-idf-sys` build script +//! [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) +//! to determine which components to install. +//! In order to solve this, you can either add `libcoap-sys` as a direct dependency, or copy +//! this crate's `src/wrapper.h` file and add the following snippet to your own `Cargo.toml`. +//! ```cargo +//! [[package.metadata.esp-idf-sys.extra_components]] +//! remote_component = { name = "espressif/coap", version = "4.3.5~3" } +//! bindings_header = "src/wrapper.h" +//! ``` +//! Afterward, run `cargo clean` (see the issue mentioned above) and try again. +//! +//! It will then parse the generated bindings file and re-export all symbols in `esp-idf-sys` that +//! are related to `libcoap`. +//! +//! Note that `esp-idf-sys` may use a different version of `bindgen` than the other build systems +//! and that bindings might differ slightly as a result. +//! +//! Unlike most other targets, this one will use `esp-idf-sys` instead of `libc` to provide its +//! standard library types (see the generic information section above). // Bindgen translates the C headers, clippy's and rustfmt's recommendations are not applicable here. #![allow(clippy::all)] @@ -217,16 +432,17 @@ macro_rules! panic_wrong_dtls { } /// Initialize the CoAP library and additionally perform runtime checks to ensure that required -/// features (as enabled in `Cargo.toml`) are available. +/// features (as enabled in `Cargo.toml`) are available and that the used DTLS library matches the +/// one that was determined during compile-time. /// -/// You *should* prefer using this function over [coap_startup], as without calling this function +/// You *should* prefer using this function over [`coap_startup()`], as without calling this function /// some of the features enabled using the Cargo features may not actually be available. /// -/// Either this function or [coap_startup] must be run once before any libcoap function is called. +/// Either this function or [`coap_startup()`] must be run once before any libcoap function is called. /// /// If you are absolutely 100% certain that all features you require are always available (or are /// prepared to deal with error return values/different behavior on your own if they aren't), you -/// may use [coap_startup] instead. +/// may use [`coap_startup()`] instead. // Make sure that if we're compiling for the ESP-IDF, this function is only compiled if the // libcoap component is installed in the ESP-IDF. // This way, these function calls will not cause missing function or struct definition errors that diff --git a/libcoap/src/lib.rs b/libcoap/src/lib.rs index ed115a2f..bad62a8a 100644 --- a/libcoap/src/lib.rs +++ b/libcoap/src/lib.rs @@ -36,16 +36,20 @@ //! - [x] Notifying observers as a server //! //! # Building -//! libcoap-rs can be linked to either an included version of libcoap or to a version provided by -//! the environment. -//! By default, it will use the vendored version, which can be disabled by disabling the default -//! feature `vendored`. +//! libcoap-rs is based on libcoap-sys, which provide many different ways to obtain and link against +//! a system-provided or vendored version of the libcoap C library. //! -//! In order to use DTLS, a DTLS library must be chosen, see the later section on using -//! cryptography for more information. +//! Refer to [its documentation](https://docs.rs/libcoap-sys) for detailed instructions on how to +//! build libcoap-sys as well as this library. //! -//! Some (but not all) of the available DTLS libraries may also be vendored using the -//! `dtls_[LIBRARY]_vendored` feature. +//! Most of these instructions can be applied to libcoap-rs directly, although libcoap-rs does +//! abstract away some of the features. +//! +//! For your convenience, libcoap-rs "re-exports" some features that do not have any influence on +//! the safe wrapper, but may have to be set in libcoap-sys to enable building (e.g., the +//! `dtls--sys` features). +//! This way, you don't need to add libcoap-sys as a dependency yourself, and may just enable the +//! feature in this crate instead. //! //! ## Building on the ESP32 //! diff --git a/libcoap/src/prng.rs b/libcoap/src/prng.rs index 65f3f54e..c2b361b2 100644 --- a/libcoap/src/prng.rs +++ b/libcoap/src/prng.rs @@ -20,8 +20,6 @@ use std::ffi::{c_uint, c_void}; use std::ffi::c_int; use std::sync::Mutex; -#[cfg(feature = "rand")] -use libc::size_t; #[cfg(feature = "rand")] use rand::{CryptoRng, RngCore}; @@ -186,7 +184,7 @@ pub fn set_coap_prng(rng: RNG) /// This function is intended as a [libcoap_sys::coap_rand_func_t], therefore `out` should be valid /// and point to the start of an area of memory that can be filled with `len` bytes. #[cfg(feature = "rand")] -unsafe extern "C" fn prng_callback(out: *mut c_void, len: size_t) -> c_int { +unsafe extern "C" fn prng_callback(out: *mut c_void, len: usize) -> c_int { let out_slice = std::slice::from_raw_parts_mut(out as *mut u8, len); match COAP_RNG_FN_MUTEX.lock() { Ok(mut rng_fn) => rng_fn From 3f17b4e42ee4a1fe2e9494234f7a8ebc02c423a3 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Wed, 22 Jan 2025 02:59:13 +0100 Subject: [PATCH 20/26] fix(ci): Adjust CI pipeline for new build script, fix docs deploy --- .github/workflows/report.yml | 4 ++-- .github/workflows/test.yml | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/workflows/report.yml b/.github/workflows/report.yml index 874b34ce..0d7e42da 100644 --- a/.github/workflows/report.yml +++ b/.github/workflows/report.yml @@ -59,7 +59,7 @@ jobs: pattern: docs run-id: ${{ github.event.workflow_run.id }} github-token: ${{ secrets.GITHUB_TOKEN }} - path: coverage-data + path: ./doc - name: 'Get artifact ID' id: get-artifact-id uses: actions/github-script@v7 @@ -79,7 +79,7 @@ jobs: - if: ${{ (github.event.workflow_run.head_repository.owner.login == github.event.workflow_run.repository.owner.login) && (vars.DOCS_AND_COV_REPO != '') }} uses: peaceiris/actions-gh-pages@v4 with: - publish_dir: ./target/doc + publish_dir: ./doc publish_branch: main external_repository: ${{ vars.DOCS_AND_COV_REPO }} personal_token: ${{ secrets.DOCS_AND_COV_REPO_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f3e05a6e..68a54b61 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,18 +30,20 @@ jobs: RUSTDOCFLAGS: "${{ matrix.rust_version == 'nightly' && '-C instrument-coverage -Cpanic=abort -Zpanic_abort_tests -Z unstable-options --persist-doctests target/debug/doctests' || ' ' }}" LIBRARY_FEATURES: | ${{ (matrix.crate == 'libcoap-rs' && 'tcp,vendored,rand') - || (matrix.crate == 'libcoap-sys' && 'default') + || (matrix.crate == 'libcoap-sys' && 'default,vendored') || 'vendored' }} + LIBCOAP_RS_DTLS_BACKEND: ${{ matrix.dtls_backend }} + LIBCOAP_RS_BUILD_SYSTEM: "vendored" DTLS_LIBRARY_FEATURES: | - ${{ (matrix.crate == 'libcoap-rs' && matrix.dtls_backend == 'tinydtls' && 'tcp,dtls-psk,dtls-rpk,dtls_tinydtls_vendored') - || (matrix.crate == 'libcoap-rs' && matrix.dtls_backend == 'mbedtls' && 'tcp,dtls-psk,dtls-pki,dtls_mbedtls_vendored') - || (matrix.crate == 'libcoap-rs' && matrix.dtls_backend == 'openssl' && 'tcp,dtls-psk,dtls-pki,dtls_openssl_vendored') - || (matrix.crate == 'libcoap-rs' && matrix.dtls_backend == 'gnutls' && 'tcp,dtls-psk,dtls-pki,dtls-rpk,dtls_gnutls') - || (matrix.crate == 'libcoap-sys' && matrix.dtls_backend == 'tinydtls' && 'dtls,dtls_backend_tinydtls,dtls_backend_tinydtls_vendored') - || (matrix.crate == 'libcoap-sys' && matrix.dtls_backend == 'mbedtls' && 'dtls,dtls_backend_mbedtls,dtls_backend_mbedtls_vendored') - || (matrix.crate == 'libcoap-sys' && matrix.dtls_backend == 'openssl' && 'dtls,dtls_backend_openssl,dtls_backend_openssl_vendored') - || (matrix.crate == 'libcoap-sys' && matrix.dtls_backend == 'gnutls' && 'dtls,dtls_backend_gnutls') + ${{ (matrix.crate == 'libcoap-rs' && matrix.dtls_backend == 'tinydtls' && 'tcp,dtls-psk,dtls-rpk,dtls-tinydtls-sys-vendored') + || (matrix.crate == 'libcoap-rs' && matrix.dtls_backend == 'mbedtls' && 'tcp,dtls-psk,dtls-pki,dtls-mbedtls-sys') + || (matrix.crate == 'libcoap-rs' && matrix.dtls_backend == 'openssl' && 'tcp,dtls-psk,dtls-pki,dtls-openssl-sys-vendored') + || (matrix.crate == 'libcoap-rs' && matrix.dtls_backend == 'gnutls' && 'tcp,dtls-psk,dtls-pki,dtls-rpk') + || (matrix.crate == 'libcoap-sys' && matrix.dtls_backend == 'tinydtls' && 'dtls,dtls-tinydtls-sys-vendored') + || (matrix.crate == 'libcoap-sys' && matrix.dtls_backend == 'mbedtls' && 'dtls,dtls-mbedtls-sys') + || (matrix.crate == 'libcoap-sys' && matrix.dtls_backend == 'openssl' && 'dtls,dtls-openssl-sys-vendored') + || (matrix.crate == 'libcoap-sys' && matrix.dtls_backend == 'gnutls' && 'dtls') || 'vendored' }} steps: From e251b56c103b8d2e8452ef3346bb618523f31fc4 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Wed, 22 Jan 2025 03:11:54 +0100 Subject: [PATCH 21/26] chore(lib|sys): bump MSRV to 1.82 to support bindgen-generated unsafe extern blocks --- .github/workflows/test.yml | 2 +- libcoap-sys/Cargo.toml | 6 ++++-- libcoap/Cargo.toml | 8 +++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 68a54b61..eca1f6d5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,7 +53,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: components: rust-src, rustc, rust-std, cargo, llvm-tools, llvm-tools-preview - toolchain: ${{ matrix.rust_version == 'msrv' && '1.81' || matrix.rust_version }} + toolchain: ${{ matrix.rust_version == 'msrv' && '1.82' || matrix.rust_version }} - if: matrix.dtls_backend == 'gnutls' uses: awalsh128/cache-apt-pkgs-action@latest with: diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index ce9c0df1..323da3c6 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -19,8 +19,10 @@ keywords = ["coap", "libcoap"] exclude = ["src/libcoap/ext/"] resolver = "2" build = "build/main.rs" -# Current reason for MSRV (please update when increasing MSRV): Transient dependency "home" requires Rust 1.81. -rust-version = "1.81.0" +# Current reason for MSRV (please update when increasing MSRV): bindgen generates unsafe extern "C" blocks, which are +# not supported on Rust < 1.82. +# See also: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-extern.html +rust-version = "1.82.0" [features] # The default features match those of libcoap's configure script for diff --git a/libcoap/Cargo.toml b/libcoap/Cargo.toml index dbee783e..189bfaa5 100644 --- a/libcoap/Cargo.toml +++ b/libcoap/Cargo.toml @@ -16,8 +16,10 @@ authors = ["Hugo Hakim Damer "] categories = ["api-bindings", "network-programming", "embedded"] keywords = ["coap", "libcoap"] resolver = "2" -# Current reason for MSRV (please update when increasing MSRV): Transient dependency "home" requires Rust 1.81. -rust-version = "1.81.0" +# Current reason for MSRV (please update when increasing MSRV): bindgen generates unsafe extern "C" blocks, which are +# not supported on Rust < 1.82, and Rust < 1.84 is not MSRV aware, so we can't just increase libcoap-sys's MSRV. +# See also: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/unsafe-extern.html +rust-version = "1.82.0" [features] default = ["dtls-psk", "tcp"] @@ -61,4 +63,4 @@ esp-idf-sys = { version = "0.36.1" } # or add this snippet themselves. [[package.metadata.esp-idf-sys.extra_components]] remote_component = { name = "espressif/coap", version = "4.3.5~3" } -bindings_header = "src/wrapper.h" \ No newline at end of file +bindings_header = "src/wrapper.h" From 5978778e6bda98b2aaaa057721059039e464d5a9 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Wed, 22 Jan 2025 03:12:37 +0100 Subject: [PATCH 22/26] chore(lib|sys): Run cargo fmt to make linter happy --- libcoap-sys/build/bindings.rs | 3 +- libcoap-sys/build/build_system/esp_idf.rs | 14 ++++---- libcoap-sys/build/build_system/manual.rs | 17 ++++------ libcoap-sys/build/build_system/mod.rs | 4 +-- libcoap-sys/build/build_system/vendored.rs | 33 ++++++++++--------- libcoap-sys/build/main.rs | 7 ++-- libcoap-sys/src/lib.rs | 5 +-- libcoap/build.rs | 3 +- libcoap/src/context.rs | 3 +- libcoap/src/crypto/pki_rpk/key.rs | 9 +++--- libcoap/src/crypto/pki_rpk/mod.rs | 34 +++++++++++--------- libcoap/src/crypto/pki_rpk/pki.rs | 33 ++++++++++++------- libcoap/src/crypto/pki_rpk/rpk.rs | 30 +++++++++++------ libcoap/src/crypto/psk/client.rs | 22 +++++++------ libcoap/src/crypto/psk/key.rs | 5 ++- libcoap/src/crypto/psk/server.rs | 26 ++++++++------- libcoap/src/error.rs | 4 +-- libcoap/src/event.rs | 11 +++---- libcoap/src/mem.rs | 12 ++++--- libcoap/src/message/mod.rs | 23 ++++++------- libcoap/src/message/request.rs | 8 ++--- libcoap/src/message/response.rs | 12 ++++--- libcoap/src/prng.rs | 14 ++++---- libcoap/src/protocol.rs | 5 ++- libcoap/src/resource.rs | 24 ++++++-------- libcoap/src/session/client.rs | 20 ++++++++---- libcoap/src/types.rs | 24 ++++++-------- libcoap/tests/common/dtls.rs | 24 +++++++------- libcoap/tests/common/mod.rs | 26 +++++++++------ libcoap/tests/dtls_pki_client_server_test.rs | 9 ++++-- libcoap/tests/dtls_psk_client_server_test.rs | 6 ++-- libcoap/tests/dtls_rpk_client_server_test.rs | 4 +-- libcoap/tests/tcp_client_server_test.rs | 8 ++--- libcoap/tests/udp_client_server_test.rs | 6 ++-- 34 files changed, 257 insertions(+), 231 deletions(-) diff --git a/libcoap-sys/build/bindings.rs b/libcoap-sys/build/bindings.rs index dc60a01f..3f43ca9a 100644 --- a/libcoap-sys/build/bindings.rs +++ b/libcoap-sys/build/bindings.rs @@ -1,9 +1,10 @@ use std::{cell::RefCell, fmt::Debug, path::PathBuf, rc::Rc}; -use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}; use anyhow::{Context, Result}; use bindgen::callbacks::{DeriveTrait, ImplementsTrait, IntKind, ParseCallbacks}; +use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}; + /// Implementation of bindgen's [ParseCallbacks] that allow reading some meta-information about the /// used libcoap version from its defines (package version, supported features, ...) #[derive(Debug, Default)] diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs index b93ac5d4..59f2a8eb 100644 --- a/libcoap-sys/build/build_system/esp_idf.rs +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -1,15 +1,15 @@ -use crate::build_system::BuildSystem; -use crate::metadata::{DtlsBackend, LibcoapFeature}; +use std::{env, fs::File, io::Write, iter::once, path::PathBuf}; + use anyhow::{anyhow, Context, Result}; use enumset::EnumSet; -use std::env; -use std::fs::File; -use std::io::Write; -use std::iter::once; -use std::path::PathBuf; use syn::{ForeignItem, Ident, Item}; use version_compare::Version; +use crate::{ + build_system::BuildSystem, + metadata::{DtlsBackend, LibcoapFeature}, +}; + pub struct EspIdfBuildSystem { out_dir: PathBuf, esp_idf_bindings_file: PathBuf, diff --git a/libcoap-sys/build/build_system/manual.rs b/libcoap-sys/build/build_system/manual.rs index b251d505..e8a45ec1 100644 --- a/libcoap-sys/build/build_system/manual.rs +++ b/libcoap-sys/build/build_system/manual.rs @@ -1,15 +1,14 @@ -use std::cell::RefCell; -use std::env; -use std::env::VarError; -use std::path::PathBuf; +use std::{cell::RefCell, env, env::VarError, path::PathBuf}; use anyhow::{Context, Result}; use enumset::EnumSet; use version_compare::Version; -use crate::bindings::{generate_libcoap_bindings, LibcoapDefineParser}; -use crate::metadata::{DtlsBackend, LibcoapDefineInfo}; -use crate::{build_system::BuildSystem, metadata::LibcoapFeature}; +use crate::{ + bindings::{generate_libcoap_bindings, LibcoapDefineParser}, + build_system::BuildSystem, + metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}, +}; pub struct ManualBuildSystem { out_dir: PathBuf, @@ -41,9 +40,7 @@ impl ManualBuildSystem { Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_ADDITIONAL_LIBRARIES environment variable."), }; let use_static = match env::var("LIBCOAP_RS_STATIC") { - Ok(v) => { - !(v == "0" || v.is_empty()) - }, + Ok(v) => !(v == "0" || v.is_empty()), Err(VarError::NotPresent) => false, Err(e) => return Err(e).context("Unable to parse LIBCOAP_RS_STATIC environment variable."), }; diff --git a/libcoap-sys/build/build_system/mod.rs b/libcoap-sys/build/build_system/mod.rs index cff43f41..47651d29 100644 --- a/libcoap-sys/build/build_system/mod.rs +++ b/libcoap-sys/build/build_system/mod.rs @@ -20,8 +20,8 @@ pub mod vendored; /// If you want to implement your own build system, you may want to use the `manual` build system /// as a basis. /// -/// In order to implement the compile-time checks, you may want to use -/// [`LibcoapDefineParser`](crate::bindings::LibcoapDefineParser), at least in cases where you have +/// In order to implement the compile-time checks, you may want to use +/// [`LibcoapDefineParser`](crate::bindings::LibcoapDefineParser), at least in cases where you have /// the corresponding `coap_defines.h` header file available. pub trait BuildSystem { /// Returns the set of features that are supported by the linked version of libcoap, or `None` diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index 5a0c6e5c..cb000e9c 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -1,16 +1,22 @@ -use crate::bindings::{generate_libcoap_bindings, LibcoapDefineParser}; -use crate::build_system::BuildSystem; -use crate::metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}; +use std::{ + cell::RefCell, + env, + env::VarError, + ffi::OsString, + path::{Path, PathBuf}, + process::Command, +}; + use anyhow::{anyhow, ensure, Context, Result}; use enumset::EnumSet; -use std::cell::RefCell; -use std::env; -use std::env::VarError; -use std::ffi::OsString; -use std::path::{Path, PathBuf}; -use std::process::Command; use version_compare::Version; +use crate::{ + bindings::{generate_libcoap_bindings, LibcoapDefineParser}, + build_system::BuildSystem, + metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}, +}; + const VENDORED_LIBCOAP_VERSION: &str = "4.3.5"; pub struct VendoredBuildSystem { @@ -213,7 +219,7 @@ impl VendoredBuildSystem { let library = pkg_config::Config::new().statik(true).exactly_version(VENDORED_LIBCOAP_VERSION).probe("libcoap-3").context("unable to link against build version of libcoap using pkg-config (which is necessary if you're not using a Rust dependency to link the DTLS library)")?; // SAFETY: We are still single-threaded here. - unsafe { env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or(OsString::new())) } + unsafe { env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or_default()) } Ok(Self { out_dir, define_info: None, @@ -221,7 +227,7 @@ impl VendoredBuildSystem { }) } else { // SAFETY: We are still single-threaded here. - unsafe { env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or(OsString::new())) } + unsafe { env::set_var("PKG_CONFIG_PATH", pkg_config_path_bak.unwrap_or_default()) } println!( "cargo:rustc-link-search={}", libcoap_build_prefix @@ -305,10 +311,7 @@ impl VendoredBuildSystem { } #[cfg(feature = "dtls-mbedtls-sys")] - fn configure_mbedtls_sys( - out_dir: &Path, - build_config: &mut autotools::Config, - ) -> Result<(Option, bool)> { + fn configure_mbedtls_sys(out_dir: &Path, build_config: &mut autotools::Config) -> Result<(Option, bool)> { if env::var_os("MbedTLS_CFLAGS").is_some() || env::var_os("MbedTLS_LIBS").is_some() { // Do not use tinydtls-sys if the user manually set either the corresponding LIBS or // CFLAGS variable. diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index 51baf973..bc166b5a 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -4,10 +4,11 @@ use anyhow::{anyhow, bail, Context, Result}; use enumset::EnumSet; use version_compare::Version; -use crate::build_system::esp_idf::EspIdfBuildSystem; -use crate::build_system::vendored::VendoredBuildSystem; use crate::{ - build_system::{manual::ManualBuildSystem, pkgconfig::PkgConfigBuildSystem, BuildSystem}, + build_system::{ + esp_idf::EspIdfBuildSystem, manual::ManualBuildSystem, pkgconfig::PkgConfigBuildSystem, + vendored::VendoredBuildSystem, BuildSystem, + }, metadata::{DtlsBackend, LibcoapFeature, MINIMUM_LIBCOAP_VERSION}, }; diff --git a/libcoap-sys/src/lib.rs b/libcoap-sys/src/lib.rs index 21e69be6..1add89fc 100644 --- a/libcoap-sys/src/lib.rs +++ b/libcoap-sys/src/lib.rs @@ -311,6 +311,7 @@ use core::ffi::c_void; +use c_stdlib::{epoll_event, fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t}; /// Re-export of the crate that provides libc data types used by libcoap. /// /// In most cases, this will be libc, but on the ESP-IDF, it will be esp_idf_sys. @@ -321,10 +322,6 @@ pub use esp_idf_sys as c_stdlib; /// In most cases, this will be libc, but on the ESP-IDF, it will be esp_idf_sys. #[cfg(not(target_os = "espidf"))] pub use libc as c_stdlib; - - -use c_stdlib::{epoll_event, fd_set, memcmp, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, socklen_t, time_t}; - // use dtls backend libraries in cases where they set our linker flags, otherwise rustc will // optimize them out, resulting in missing symbols. #[allow(unused_imports)] diff --git a/libcoap/build.rs b/libcoap/build.rs index 0a27e9ab..0f883095 100644 --- a/libcoap/build.rs +++ b/libcoap/build.rs @@ -1,7 +1,8 @@ // SPDX-License-Identifier: BSD-2-CLAUSE -use anyhow::{bail, Result}; use std::env::VarError; + +use anyhow::{bail, Result}; use version_compare::Version; /// The minimal version of libcoap that is expected to work with libcoap-rs. diff --git a/libcoap/src/context.rs b/libcoap/src/context.rs index b4b26b92..ee6e2c01 100644 --- a/libcoap/src/context.rs +++ b/libcoap/src/context.rs @@ -9,6 +9,7 @@ //! Module containing context-internal types and traits. +use core::ffi::c_uint; #[cfg(feature = "dtls-pki")] use std::ffi::CString; #[cfg(feature = "dtls")] @@ -17,8 +18,6 @@ use std::{any::Any, ffi::c_void, fmt::Debug, net::SocketAddr, ops::Sub, sync::On #[cfg(all(feature = "dtls-pki", unix))] use std::{os::unix::ffi::OsStrExt, path::Path}; -use core::ffi::c_uint; - #[cfg(feature = "dtls-pki")] use libcoap_sys::coap_context_set_pki_root_cas; use libcoap_sys::{ diff --git a/libcoap/src/crypto/pki_rpk/key.rs b/libcoap/src/crypto/pki_rpk/key.rs index 5f8f9a6f..acdc20c0 100644 --- a/libcoap/src/crypto/pki_rpk/key.rs +++ b/libcoap/src/crypto/pki_rpk/key.rs @@ -7,6 +7,10 @@ * See the README as well as the LICENSE file for more information. */ +#[cfg(unix)] +use std::os::unix::ffi::OsStrExt; +use std::{ffi::CString, fmt::Debug, path::Path}; + use libcoap_sys::{ coap_asn1_privatekey_type_t, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_CMAC, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DH, coap_asn1_privatekey_type_t_COAP_ASN1_PKEY_DHX, @@ -20,11 +24,6 @@ use libcoap_sys::{ }; use num_derive::FromPrimitive; use num_traits::FromPrimitive; -use std::ffi::CString; -use std::fmt::Debug; -#[cfg(unix)] -use std::os::unix::ffi::OsStrExt; -use std::path::Path; /// Trait for marker structs that describe different types of asymmetric DTLS keys (RPK or PKI). #[allow(private_bounds)] diff --git a/libcoap/src/crypto/pki_rpk/mod.rs b/libcoap/src/crypto/pki_rpk/mod.rs index f35f8f57..5d0918b6 100644 --- a/libcoap/src/crypto/pki_rpk/mod.rs +++ b/libcoap/src/crypto/pki_rpk/mod.rs @@ -295,27 +295,31 @@ mod pki; #[cfg(feature = "dtls-rpk")] mod rpk; -#[cfg(feature = "dtls-pki")] -pub use pki::*; -#[cfg(feature = "dtls-rpk")] -pub use rpk::*; +use std::{ + cell::RefCell, + ffi::{c_char, c_int, c_uint, c_void, CStr, CString, NulError}, + fmt::{Debug, Formatter}, + marker::PhantomData, + ptr::NonNull, + rc::{Rc, Weak}, +}; pub use key::*; - -use crate::error::{ContextConfigurationError, SessionCreationError}; -use crate::session::CoapSession; -use crate::types::CoapAddress; -use crate::CoapContext; use libcoap_sys::{ coap_context_set_pki, coap_context_t, coap_dtls_key_t, coap_dtls_pki_t, coap_new_client_session_pki, coap_proto_t, coap_session_t, COAP_DTLS_PKI_SETUP_VERSION, }; -use std::cell::RefCell; -use std::ffi::{c_char, c_int, c_uint, c_void, CStr, CString, NulError}; -use std::fmt::{Debug, Formatter}; -use std::marker::PhantomData; -use std::ptr::NonNull; -use std::rc::{Rc, Weak}; +#[cfg(feature = "dtls-pki")] +pub use pki::*; +#[cfg(feature = "dtls-rpk")] +pub use rpk::*; + +use crate::{ + error::{ContextConfigurationError, SessionCreationError}, + session::CoapSession, + types::CoapAddress, + CoapContext, +}; /// A context configuration for server-side PKI or RPK based DTLS encryption. #[derive(Clone, Debug)] diff --git a/libcoap/src/crypto/pki_rpk/pki.rs b/libcoap/src/crypto/pki_rpk/pki.rs index bc441de1..4223db49 100644 --- a/libcoap/src/crypto/pki_rpk/pki.rs +++ b/libcoap/src/crypto/pki_rpk/pki.rs @@ -7,24 +7,33 @@ * See the README as well as the LICENSE file for more information. */ -use crate::crypto::pki_rpk; -use crate::crypto::pki_rpk::key::{KeyComponentSealed, KeyTypeSealed}; -use crate::crypto::pki_rpk::{ - Asn1PrivateKeyType, CertVerificationMode, CertVerifying, CnCallback, DerFileKeyComponent, DerMemoryKeyComponent, - EngineKeyComponent, KeyComponent, KeyDef, KeyDefSealed, NonCertVerifying, PemFileKeyComponent, - PemMemoryKeyComponent, Pkcs11KeyComponent, PkiRpkContext, PkiRpkContextBuilder, ServerPkiRpkCryptoContext, +use std::{ + ffi::{c_uint, CStr, CString}, + fmt::Debug, }; -use crate::crypto::ClientCryptoContext; -use crate::session::CoapSession; + use libcoap_sys::{ coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_key_t__bindgen_ty_1, coap_dtls_pki_t, coap_pki_define_t, coap_pki_define_t_COAP_PKI_KEY_DEF_DER, coap_pki_define_t_COAP_PKI_KEY_DEF_DER_BUF, coap_pki_define_t_COAP_PKI_KEY_DEF_ENGINE, coap_pki_define_t_COAP_PKI_KEY_DEF_PEM, - coap_pki_define_t_COAP_PKI_KEY_DEF_PEM_BUF, coap_pki_define_t_COAP_PKI_KEY_DEF_PKCS11, coap_pki_key_define_t - , coap_pki_key_t_COAP_PKI_KEY_DEFINE, + coap_pki_define_t_COAP_PKI_KEY_DEF_PEM_BUF, coap_pki_define_t_COAP_PKI_KEY_DEF_PKCS11, coap_pki_key_define_t, + coap_pki_key_t_COAP_PKI_KEY_DEFINE, +}; + +use crate::{ + crypto::{ + pki_rpk, + pki_rpk::{ + key::{KeyComponentSealed, KeyTypeSealed}, + Asn1PrivateKeyType, CertVerificationMode, CertVerifying, CnCallback, DerFileKeyComponent, + DerMemoryKeyComponent, EngineKeyComponent, KeyComponent, KeyDef, KeyDefSealed, NonCertVerifying, + PemFileKeyComponent, PemMemoryKeyComponent, Pkcs11KeyComponent, PkiRpkContext, PkiRpkContextBuilder, + ServerPkiRpkCryptoContext, + }, + ClientCryptoContext, + }, + session::CoapSession, }; -use std::ffi::{c_uint, CStr, CString}; -use std::fmt::Debug; /// (Marker) key type for keys with a certificate signed by a trusted CA. #[derive(Debug, Clone, Copy)] diff --git a/libcoap/src/crypto/pki_rpk/rpk.rs b/libcoap/src/crypto/pki_rpk/rpk.rs index 8693ec64..486f6a9b 100644 --- a/libcoap/src/crypto/pki_rpk/rpk.rs +++ b/libcoap/src/crypto/pki_rpk/rpk.rs @@ -7,17 +7,27 @@ * See the README as well as the LICENSE file for more information. */ -use crate::crypto::pki_rpk; -use crate::crypto::pki_rpk::key::{KeyComponentSealed, KeyTypeSealed}; -use crate::crypto::pki_rpk::{ - Asn1PrivateKeyType, CnCallback, KeyComponent, KeyDef, KeyDefSealed, NonCertVerifying, PemMemoryKeyComponent, - Pkcs11KeyComponent, PkiRpkContext, PkiRpkContextBuilder, ServerPkiRpkCryptoContext, +use std::{ffi::CString, fmt::Debug}; + +use libcoap_sys::{ + coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_key_t__bindgen_ty_1, coap_dtls_pki_t, coap_pki_define_t, + coap_pki_define_t_COAP_PKI_KEY_DEF_PEM, coap_pki_define_t_COAP_PKI_KEY_DEF_PKCS11_RPK, + coap_pki_define_t_COAP_PKI_KEY_DEF_RPK_BUF, coap_pki_key_define_t, coap_pki_key_t, + coap_pki_key_t_COAP_PKI_KEY_DEFINE, +}; + +use crate::{ + crypto::{ + pki_rpk, + pki_rpk::{ + key::{KeyComponentSealed, KeyTypeSealed}, + Asn1PrivateKeyType, CnCallback, KeyComponent, KeyDef, KeyDefSealed, NonCertVerifying, + PemMemoryKeyComponent, Pkcs11KeyComponent, PkiRpkContext, PkiRpkContextBuilder, ServerPkiRpkCryptoContext, + }, + ClientCryptoContext, + }, + session::CoapSession, }; -use crate::crypto::ClientCryptoContext; -use crate::session::CoapSession; -use libcoap_sys::{coap_const_char_ptr_t, coap_dtls_key_t, coap_dtls_key_t__bindgen_ty_1, coap_dtls_pki_t, coap_pki_define_t, coap_pki_define_t_COAP_PKI_KEY_DEF_PEM, coap_pki_define_t_COAP_PKI_KEY_DEF_PKCS11_RPK, coap_pki_define_t_COAP_PKI_KEY_DEF_RPK_BUF, coap_pki_key_define_t, coap_pki_key_t, coap_pki_key_t_COAP_PKI_KEY_DEFINE}; -use std::ffi::CString; -use std::fmt::Debug; /// (Marker) key type for asymmetric DTLS keys not signed by a CA (raw public keys). #[derive(Debug, Clone, Copy)] diff --git a/libcoap/src/crypto/psk/client.rs b/libcoap/src/crypto/psk/client.rs index 01ba8ed5..ec907be9 100644 --- a/libcoap/src/crypto/psk/client.rs +++ b/libcoap/src/crypto/psk/client.rs @@ -7,20 +7,22 @@ * See the README as well as the LICENSE file for more information. */ -use crate::crypto::psk::key::PskKey; -use crate::error::SessionCreationError; -use crate::session::CoapClientSession; -use crate::types::CoapAddress; -use crate::CoapContext; +use std::{ + cell::RefCell, + ffi::{c_char, c_void, CString, NulError}, + fmt::Debug, + ptr::NonNull, + rc::{Rc, Weak}, +}; + use libcoap_sys::{ coap_dtls_cpsk_info_t, coap_dtls_cpsk_t, coap_new_client_session_psk2, coap_proto_t, coap_session_t, coap_str_const_t, COAP_DTLS_CPSK_SETUP_VERSION, }; -use std::cell::RefCell; -use std::ffi::{c_char, c_void, CString, NulError}; -use std::fmt::Debug; -use std::ptr::NonNull; -use std::rc::{Rc, Weak}; + +use crate::{ + crypto::psk::key::PskKey, error::SessionCreationError, session::CoapClientSession, types::CoapAddress, CoapContext, +}; /// Builder for a client-side DTLS encryption context for use with pre-shared keys (PSK). #[derive(Debug)] diff --git a/libcoap/src/crypto/psk/key.rs b/libcoap/src/crypto/psk/key.rs index eebf94de..11503451 100644 --- a/libcoap/src/crypto/psk/key.rs +++ b/libcoap/src/crypto/psk/key.rs @@ -7,10 +7,9 @@ * See the README as well as the LICENSE file for more information. */ +use std::{borrow::Cow, marker::PhantomData, ptr::NonNull}; + use libcoap_sys::{coap_bin_const_t, coap_dtls_cpsk_info_t, coap_dtls_spsk_info_t}; -use std::borrow::Cow; -use std::marker::PhantomData; -use std::ptr::NonNull; /// A pre-shared DTLS key. #[derive(Debug, Clone)] diff --git a/libcoap/src/crypto/psk/server.rs b/libcoap/src/crypto/psk/server.rs index 357a7f20..5f281e83 100644 --- a/libcoap/src/crypto/psk/server.rs +++ b/libcoap/src/crypto/psk/server.rs @@ -7,22 +7,24 @@ * See the README as well as the LICENSE file for more information. */ -use crate::crypto::psk::key::PskKey; -use crate::error::ContextConfigurationError; -use crate::session::CoapServerSession; +use std::{ + borrow::Borrow, + cell::RefCell, + collections::{BTreeMap, HashMap}, + ffi::{c_void, CStr}, + fmt::Debug, + hash::Hash, + os::raw::c_char, + ptr::NonNull, + rc::{Rc, Weak}, +}; + use libcoap_sys::{ coap_bin_const_t, coap_context_set_psk2, coap_context_t, coap_dtls_spsk_info_t, coap_dtls_spsk_t, coap_session_t, COAP_DTLS_SPSK_SETUP_VERSION, }; -use std::borrow::Borrow; -use std::cell::RefCell; -use std::collections::{BTreeMap, HashMap}; -use std::ffi::{c_void, CStr}; -use std::fmt::Debug; -use std::hash::Hash; -use std::os::raw::c_char; -use std::ptr::NonNull; -use std::rc::{Rc, Weak}; + +use crate::{crypto::psk::key::PskKey, error::ContextConfigurationError, session::CoapServerSession}; /// Builder for a server-side DTLS encryption context for use with pre-shared keys (PSK). #[derive(Debug)] diff --git a/libcoap/src/error.rs b/libcoap/src/error.rs index 14fcf6f5..6c1630dd 100644 --- a/libcoap/src/error.rs +++ b/libcoap/src/error.rs @@ -9,9 +9,7 @@ //! Error types -use std::ffi::NulError; -use std::string::FromUtf8Error; -use std::sync::PoisonError; +use std::{ffi::NulError, string::FromUtf8Error, sync::PoisonError}; use thiserror::Error; diff --git a/libcoap/src/event.rs b/libcoap/src/event.rs index e3a8743d..fb716763 100644 --- a/libcoap/src/event.rs +++ b/libcoap/src/event.rs @@ -13,14 +13,13 @@ use std::fmt::Debug; use libcoap_sys::{ coap_event_t, coap_event_t_COAP_EVENT_SERVER_SESSION_NEW, coap_event_t_COAP_EVENT_TCP_CONNECTED, - coap_session_get_context, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_SERVER, + coap_session_get_context, coap_session_get_type, coap_session_t, coap_session_type_t_COAP_SESSION_TYPE_SERVER, }; -use libcoap_sys::coap_session_get_type; -use crate::context::CoapContext; -use crate::session::CoapSession; - -use crate::session::CoapServerSession; +use crate::{ + context::CoapContext, + session::{CoapServerSession, CoapSession}, +}; /// Trait for CoAP event handlers. /// diff --git a/libcoap/src/mem.rs b/libcoap/src/mem.rs index 64097377..5fb078d4 100644 --- a/libcoap/src/mem.rs +++ b/libcoap/src/mem.rs @@ -9,11 +9,13 @@ //! Code related to memory handling, especially for passing objects through FFI -use std::cell::{Ref, RefCell, RefMut}; -use std::ffi::c_void; -use std::fmt::{Debug, Formatter}; -use std::ops::{Deref, DerefMut}; -use std::rc::{Rc, Weak}; +use std::{ + cell::{Ref, RefCell, RefMut}, + ffi::c_void, + fmt::{Debug, Formatter}, + ops::{Deref, DerefMut}, + rc::{Rc, Weak}, +}; /// Trait implemented by libcoap wrapper structs that contain an inner value that may be dropped /// exclusively, i.e., that can be dropped with the additional check that there are no further diff --git a/libcoap/src/message/mod.rs b/libcoap/src/message/mod.rs index fd31b57c..2c294f48 100644 --- a/libcoap/src/message/mod.rs +++ b/libcoap/src/message/mod.rs @@ -15,10 +15,7 @@ //! process of creating requests and responses and setting the appropriate options ([CoapRequest] //! and [CoapResponse]). -use std::{ffi::c_void, mem::MaybeUninit, slice::Iter}; -use std::fmt::Write; - -use num_traits::FromPrimitive; +use std::{ffi::c_void, fmt::Write, mem::MaybeUninit, slice::Iter}; use libcoap_sys::{ coap_add_data, coap_add_data_large_request, coap_add_optlist_pdu, coap_add_token, coap_delete_optlist, @@ -27,23 +24,23 @@ use libcoap_sys::{ coap_pdu_get_mid, coap_pdu_get_token, coap_pdu_get_type, coap_pdu_init, coap_pdu_set_code, coap_pdu_set_type, coap_pdu_t, coap_session_t, }; +use num_traits::FromPrimitive; pub use request::CoapRequest; pub use response::CoapResponse; use crate::{ + context::ensure_coap_started, error::{MessageConversionError, OptionValueError}, protocol::{ - Block, CoapMatch, CoapMessageCode, CoapMessageType, CoapOptionNum, CoapOptionType, ContentFormat, ETag, - HopLimit, MaxAge, NoResponse, Observe, ProxyScheme, ProxyUri, Size, UriHost, UriPath, UriPort, UriQuery, + Block, CoapMatch, CoapMessageCode, CoapMessageType, CoapOptionNum, CoapOptionType, ContentFormat, ETag, Echo, + HopLimit, MaxAge, NoResponse, Observe, Oscore, ProxyScheme, ProxyUri, RequestTag, Size, UriHost, UriPath, + UriPort, UriQuery, }, session::CoapSessionCommon, - types::CoapMessageId, -}; -use crate::context::ensure_coap_started; -use crate::protocol::{Echo, Oscore, RequestTag}; -use crate::types::{ - decode_var_len_u16, decode_var_len_u32, decode_var_len_u8, encode_var_len_u16, encode_var_len_u32, - encode_var_len_u8, + types::{ + decode_var_len_u16, decode_var_len_u32, decode_var_len_u8, encode_var_len_u16, encode_var_len_u32, + encode_var_len_u8, CoapMessageId, + }, }; pub mod request; diff --git a/libcoap/src/message/request.rs b/libcoap/src/message/request.rs index 862984f8..85854872 100644 --- a/libcoap/src/message/request.rs +++ b/libcoap/src/message/request.rs @@ -10,17 +10,15 @@ use std::str::FromStr; use crate::{ - error::{MessageConversionError, MessageTypeError}, - message::{CoapMessage, CoapMessageCommon, CoapOption}, + error::{MessageConversionError, MessageTypeError, OptionValueError}, + message::{construct_path_string, construct_query_string, CoapMessage, CoapMessageCommon, CoapOption}, protocol::{ CoapMatch, CoapMessageCode, CoapMessageType, CoapOptionType, CoapRequestCode, ContentFormat, ETag, HopLimit, NoResponse, Observe, }, + session::CoapSessionCommon, types::{CoapUri, CoapUriScheme}, }; -use crate::error::OptionValueError; -use crate::message::{construct_path_string, construct_query_string}; -use crate::session::CoapSessionCommon; /// Representation of a CoAP request message. /// diff --git a/libcoap/src/message/response.rs b/libcoap/src/message/response.rs index c3a0cc9a..844c3dc5 100644 --- a/libcoap/src/message/response.rs +++ b/libcoap/src/message/response.rs @@ -7,12 +7,14 @@ * See the README as well as the LICENSE file for more information. */ -use crate::error::{MessageConversionError, MessageTypeError, OptionValueError}; -use crate::message::{CoapMessage, CoapMessageCommon, CoapOption, construct_path_string, construct_query_string}; -use crate::protocol::{ - CoapMessageCode, CoapMessageType, CoapOptionType, CoapResponseCode, ContentFormat, Echo, ETag, MaxAge, Observe, +use crate::{ + error::{MessageConversionError, MessageTypeError, OptionValueError}, + message::{construct_path_string, construct_query_string, CoapMessage, CoapMessageCommon, CoapOption}, + protocol::{ + CoapMessageCode, CoapMessageType, CoapOptionType, CoapResponseCode, ContentFormat, ETag, Echo, MaxAge, Observe, + }, + types::CoapUri, }; -use crate::types::CoapUri; #[derive(Debug, Clone, Eq, PartialEq)] pub struct CoapResponse { diff --git a/libcoap/src/prng.rs b/libcoap/src/prng.rs index c2b361b2..53aa2354 100644 --- a/libcoap/src/prng.rs +++ b/libcoap/src/prng.rs @@ -15,20 +15,20 @@ //! [rand] crate that allow using the libcoap PRNG as a [rand::Rng] or setting the libcoap PRNG to //! an existing [rand::Rng]. -use std::ffi::{c_uint, c_void}; #[cfg(feature = "rand")] use std::ffi::c_int; -use std::sync::Mutex; +use std::{ + ffi::{c_uint, c_void}, + sync::Mutex, +}; #[cfg(feature = "rand")] -use rand::{CryptoRng, RngCore}; - +use libcoap_sys::coap_set_prng; use libcoap_sys::{coap_prng, coap_prng_init}; #[cfg(feature = "rand")] -use libcoap_sys::coap_set_prng; +use rand::{CryptoRng, RngCore}; -use crate::context::ensure_coap_started; -use crate::error::RngError; +use crate::{context::ensure_coap_started, error::RngError}; // TODO If we can assert that libcoap's own thread-safety features are enabled at some point, we // don't need these mutexes. diff --git a/libcoap/src/protocol.rs b/libcoap/src/protocol.rs index 45d2ff28..7b9b27d0 100644 --- a/libcoap/src/protocol.rs +++ b/libcoap/src/protocol.rs @@ -14,9 +14,6 @@ use std::{ fmt::{Display, Formatter}, }; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; - use libcoap_sys::{ coap_option_num_t, coap_pdu_code_t, coap_pdu_code_t_COAP_EMPTY_CODE, coap_pdu_code_t_COAP_REQUEST_CODE_DELETE, coap_pdu_code_t_COAP_REQUEST_CODE_FETCH, coap_pdu_code_t_COAP_REQUEST_CODE_GET, @@ -58,6 +55,8 @@ use libcoap_sys::{ COAP_OPTION_Q_BLOCK2, COAP_OPTION_RTAG, COAP_OPTION_SIZE1, COAP_OPTION_SIZE2, COAP_OPTION_URI_HOST, COAP_OPTION_URI_PATH, COAP_OPTION_URI_PORT, COAP_OPTION_URI_QUERY, }; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; use crate::error::{MessageCodeError, UnknownOptionError}; diff --git a/libcoap/src/resource.rs b/libcoap/src/resource.rs index 4319d148..8c01f67f 100644 --- a/libcoap/src/resource.rs +++ b/libcoap/src/resource.rs @@ -9,16 +9,14 @@ //! Resource and resource handler descriptions +use core::ffi::c_int; use std::{ any::Any, - cell::Ref, - cell::RefMut, + cell::{Ref, RefMut}, fmt::{Debug, Formatter}, marker::PhantomData, }; -use core::ffi::c_int; - use libcoap_sys::{ coap_delete_resource, coap_new_str_const, coap_pdu_t, coap_register_request_handler, coap_resource_get_uri_path, coap_resource_get_userdata, coap_resource_init, coap_resource_notify_observers, coap_resource_set_get_observable, @@ -26,16 +24,14 @@ use libcoap_sys::{ COAP_RESOURCE_FLAGS_NOTIFY_CON, COAP_RESOURCE_FLAGS_NOTIFY_NON, COAP_RESOURCE_FLAGS_RELEASE_URI, }; -use crate::context::ensure_coap_started; -use crate::mem::{CoapFfiRcCell, DropInnerExclusively}; -use crate::message::request::CoapRequest; -use crate::message::response::CoapResponse; -use crate::message::CoapMessageCommon; -use crate::protocol::CoapMessageCode; -use crate::protocol::CoapMessageType; -use crate::session::CoapServerSession; -use crate::session::CoapSessionCommon; -use crate::{error::MessageConversionError, message::CoapMessage, protocol::CoapRequestCode}; +use crate::{ + context::ensure_coap_started, + error::MessageConversionError, + mem::{CoapFfiRcCell, DropInnerExclusively}, + message::{request::CoapRequest, response::CoapResponse, CoapMessage, CoapMessageCommon}, + protocol::{CoapMessageCode, CoapMessageType, CoapRequestCode}, + session::{CoapServerSession, CoapSessionCommon}, +}; // Trait aliases are experimental //trait CoapMethodHandlerFn = FnMut(&D, &mut CoapSession, &CoapRequestMessage, &mut CoapResponseMessage); diff --git a/libcoap/src/session/client.rs b/libcoap/src/session/client.rs index e229221f..6da6fffc 100644 --- a/libcoap/src/session/client.rs +++ b/libcoap/src/session/client.rs @@ -7,8 +7,10 @@ * See the README as well as the LICENSE file for more information. */ -use std::cell::{Ref, RefMut}; -use std::net::SocketAddr; +use std::{ + cell::{Ref, RefMut}, + net::SocketAddr, +}; use libcoap_sys::{ coap_new_client_session, coap_proto_t_COAP_PROTO_DTLS, coap_proto_t_COAP_PROTO_TCP, coap_proto_t_COAP_PROTO_UDP, @@ -19,13 +21,16 @@ use libcoap_sys::{ }; use super::{CoapSessionCommon, CoapSessionInner, CoapSessionInnerProvider}; -use crate::event::event_handler_callback; -use crate::mem::{CoapFfiRcCell, DropInnerExclusively}; -use crate::prng::coap_prng_try_fill; -use crate::{context::CoapContext, error::SessionCreationError, types::CoapAddress}; - #[cfg(feature = "dtls")] use crate::crypto::ClientCryptoContext; +use crate::{ + context::CoapContext, + error::SessionCreationError, + event::event_handler_callback, + mem::{CoapFfiRcCell, DropInnerExclusively}, + prng::coap_prng_try_fill, + types::CoapAddress, +}; #[derive(Debug)] struct CoapClientSessionInner<'a> { @@ -272,6 +277,7 @@ impl<'a> CoapSessionInnerProvider<'a> for CoapClientSession<'a> { fn inner_ref<'b>(&'b self) -> Ref<'b, CoapSessionInner<'a>> { Ref::map(self.inner.borrow(), |v| &v.inner) } + fn inner_mut<'b>(&'b self) -> RefMut<'b, CoapSessionInner<'a>> { RefMut::map(self.inner.borrow_mut(), |v| &mut v.inner) } diff --git a/libcoap/src/types.rs b/libcoap/src/types.rs index f240d96b..4ac83f27 100644 --- a/libcoap/src/types.rs +++ b/libcoap/src/types.rs @@ -10,24 +10,19 @@ //! Types required for conversion between libcoap C library abstractions and Rust types. use core::ffi::c_ushort; -use libcoap_sys::c_stdlib::{in6_addr, in_addr, sa_family_t, sockaddr_in, sockaddr_in6, socklen_t, AF_INET, AF_INET6}; -use num_derive::FromPrimitive; -use num_traits::FromPrimitive; -use std::ffi::{CStr, CString}; -use std::fmt::{Display, Formatter}; -use std::marker::PhantomPinned; -use std::pin::Pin; use std::{ - fmt::Debug, + ffi::{CStr, CString}, + fmt::{Debug, Display, Formatter}, + marker::PhantomPinned, mem::MaybeUninit, net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, os::raw::c_int, + pin::Pin, str::FromStr, }; -#[cfg(feature = "url")] -use url::Url; use libcoap_sys::{ + c_stdlib::{in6_addr, in_addr, sa_family_t, sockaddr_in, sockaddr_in6, socklen_t, AF_INET, AF_INET6}, coap_address_t, coap_delete_optlist, coap_mid_t, coap_proto_t, coap_proto_t_COAP_PROTO_DTLS, coap_proto_t_COAP_PROTO_NONE, coap_proto_t_COAP_PROTO_TCP, coap_proto_t_COAP_PROTO_TLS, coap_proto_t_COAP_PROTO_UDP, coap_split_proxy_uri, coap_split_uri, coap_str_const_t, coap_string_equal, @@ -37,11 +32,12 @@ use libcoap_sys::{ coap_uri_scheme_t_COAP_URI_SCHEME_COAP_WS, coap_uri_scheme_t_COAP_URI_SCHEME_HTTP, coap_uri_scheme_t_COAP_URI_SCHEME_HTTPS, coap_uri_t, COAP_URI_SCHEME_SECURE_MASK, }; +use num_derive::FromPrimitive; +use num_traits::FromPrimitive; +#[cfg(feature = "url")] +use url::Url; -use crate::context::ensure_coap_started; -use crate::error::UriParsingError; -use crate::message::CoapOption; -use crate::protocol::UriPort; +use crate::{context::ensure_coap_started, error::UriParsingError, message::CoapOption, protocol::UriPort}; /// Interface index used internally by libcoap to refer to an endpoint. pub type IfIndex = c_int; diff --git a/libcoap/tests/common/dtls.rs b/libcoap/tests/common/dtls.rs index 00bed47e..b572df40 100644 --- a/libcoap/tests/common/dtls.rs +++ b/libcoap/tests/common/dtls.rs @@ -1,21 +1,23 @@ -use crate::common; -use libcoap_rs::crypto::pki_rpk::{ - KeyDef, KeyType, NonCertVerifying, PkiRpkContext, PkiRpkContextBuilder, ServerPkiRpkCryptoContext, +use std::{ffi::CStr, path::PathBuf, time::Duration}; + +use libcoap_rs::{ + crypto::{ + pki_rpk::{KeyDef, KeyType, NonCertVerifying, PkiRpkContext, PkiRpkContextBuilder, ServerPkiRpkCryptoContext}, + ClientCryptoContext, + }, + message::CoapMessageCommon, + protocol::{CoapMessageCode, CoapResponseCode}, + session::{CoapClientSession, CoapSessionCommon}, + CoapContext, }; -use libcoap_rs::crypto::ClientCryptoContext; -use libcoap_rs::message::CoapMessageCommon; -use libcoap_rs::protocol::{CoapMessageCode, CoapResponseCode}; -use libcoap_rs::session::{CoapClientSession, CoapSessionCommon}; -use libcoap_rs::CoapContext; use libcoap_sys::{ coap_get_tls_library_version, coap_package_version, coap_tls_library_t_COAP_TLS_LIBRARY_GNUTLS, coap_tls_library_t_COAP_TLS_LIBRARY_MBEDTLS, coap_tls_library_t_COAP_TLS_LIBRARY_NOTLS, coap_tls_library_t_COAP_TLS_LIBRARY_OPENSSL, coap_tls_library_t_COAP_TLS_LIBRARY_TINYDTLS, coap_tls_library_t_COAP_TLS_LIBRARY_WOLFSSL, }; -use std::ffi::CStr; -use std::path::PathBuf; -use std::time::Duration; + +use crate::common; // Is used in some test cases, but not in others (causing a compiler warning) #[allow(unused)] diff --git a/libcoap/tests/common/mod.rs b/libcoap/tests/common/mod.rs index 28a146a6..8a63e287 100644 --- a/libcoap/tests/common/mod.rs +++ b/libcoap/tests/common/mod.rs @@ -10,17 +10,23 @@ #[cfg(any(feature = "dtls-pki", feature = "dtls-rpk"))] pub mod dtls; -use std::net::{SocketAddr, UdpSocket}; -use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Condvar, Mutex}; -use std::thread::JoinHandle; -use std::time::Duration; +use std::{ + net::{SocketAddr, UdpSocket}, + rc::Rc, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Condvar, Mutex, + }, + thread::JoinHandle, + time::Duration, +}; -use libcoap_rs::message::{CoapMessageCommon, CoapRequest, CoapResponse}; -use libcoap_rs::protocol::{CoapMessageCode, CoapMessageType, CoapRequestCode, CoapResponseCode}; -use libcoap_rs::session::CoapSessionCommon; -use libcoap_rs::{CoapContext, CoapRequestHandler, CoapResource}; +use libcoap_rs::{ + message::{CoapMessageCommon, CoapRequest, CoapResponse}, + protocol::{CoapMessageCode, CoapMessageType, CoapRequestCode, CoapResponseCode}, + session::CoapSessionCommon, + CoapContext, CoapRequestHandler, CoapResource, +}; use libcoap_sys::{coap_dtls_set_log_level, coap_log_t_COAP_LOG_DEBUG, coap_set_log_level}; pub(crate) fn get_unused_server_addr() -> SocketAddr { diff --git a/libcoap/tests/dtls_pki_client_server_test.rs b/libcoap/tests/dtls_pki_client_server_test.rs index eec324a6..3f834e1e 100644 --- a/libcoap/tests/dtls_pki_client_server_test.rs +++ b/libcoap/tests/dtls_pki_client_server_test.rs @@ -9,11 +9,14 @@ #![cfg(feature = "dtls-pki")] -use crate::common::dtls::dtls_client_server_request_common; -use libcoap_rs::crypto::pki_rpk::{Asn1PrivateKeyType, DerFileKeyComponent, NonCertVerifying, PkiRpkContextBuilder}; -use libcoap_rs::crypto::pki_rpk::{Pki, PkiKeyDef}; use std::path::PathBuf; +use libcoap_rs::crypto::pki_rpk::{ + Asn1PrivateKeyType, DerFileKeyComponent, NonCertVerifying, Pki, PkiKeyDef, PkiRpkContextBuilder, +}; + +use crate::common::dtls::dtls_client_server_request_common; + mod common; #[test] diff --git a/libcoap/tests/dtls_psk_client_server_test.rs b/libcoap/tests/dtls_psk_client_server_test.rs index 757e9612..a19dcacf 100644 --- a/libcoap/tests/dtls_psk_client_server_test.rs +++ b/libcoap/tests/dtls_psk_client_server_test.rs @@ -10,13 +10,11 @@ #![cfg(feature = "dtls-psk")] use std::time::Duration; -use libcoap_rs::crypto::psk::PskKey; -use libcoap_rs::crypto::psk::{ClientPskContextBuilder, ServerPskContextBuilder}; -use libcoap_rs::session::CoapClientSession; use libcoap_rs::{ + crypto::psk::{ClientPskContextBuilder, PskKey, ServerPskContextBuilder}, message::CoapMessageCommon, protocol::{CoapMessageCode, CoapResponseCode}, - session::CoapSessionCommon, + session::{CoapClientSession, CoapSessionCommon}, CoapContext, }; diff --git a/libcoap/tests/dtls_rpk_client_server_test.rs b/libcoap/tests/dtls_rpk_client_server_test.rs index 5ed48495..7ca64b04 100644 --- a/libcoap/tests/dtls_rpk_client_server_test.rs +++ b/libcoap/tests/dtls_rpk_client_server_test.rs @@ -9,9 +9,9 @@ #![cfg(feature = "dtls-rpk")] +use libcoap_rs::crypto::pki_rpk::{NonCertVerifying, PkiRpkContextBuilder, Rpk, RpkKeyDef}; + use crate::common::dtls::dtls_client_server_request_common; -use libcoap_rs::crypto::pki_rpk::{NonCertVerifying, PkiRpkContextBuilder}; -use libcoap_rs::crypto::pki_rpk::{Rpk, RpkKeyDef}; mod common; diff --git a/libcoap/tests/tcp_client_server_test.rs b/libcoap/tests/tcp_client_server_test.rs index e5a64258..fe9b8a5b 100644 --- a/libcoap/tests/tcp_client_server_test.rs +++ b/libcoap/tests/tcp_client_server_test.rs @@ -6,16 +6,16 @@ * Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved. * See the README as well as the LICENSE file for more information. */ - #![cfg(feature = "tcp")] +#![cfg(feature = "tcp")] + +use std::time::Duration; -use libcoap_rs::session::CoapClientSession; use libcoap_rs::{ message::CoapMessageCommon, protocol::{CoapMessageCode, CoapResponseCode}, - session::CoapSessionCommon, + session::{CoapClientSession, CoapSessionCommon}, CoapContext, }; -use std::time::Duration; mod common; diff --git a/libcoap/tests/udp_client_server_test.rs b/libcoap/tests/udp_client_server_test.rs index c3d9d27e..21226193 100644 --- a/libcoap/tests/udp_client_server_test.rs +++ b/libcoap/tests/udp_client_server_test.rs @@ -7,14 +7,14 @@ * See the README as well as the LICENSE file for more information. */ -use libcoap_rs::session::CoapClientSession; +use std::time::Duration; + use libcoap_rs::{ message::CoapMessageCommon, protocol::{CoapMessageCode, CoapResponseCode}, - session::CoapSessionCommon, + session::{CoapClientSession, CoapSessionCommon}, CoapContext, }; -use std::time::Duration; mod common; From 3e1493c7685a2e6cee212c689356ab6b6502bf9e Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Wed, 22 Jan 2025 13:43:03 +0100 Subject: [PATCH 23/26] chore(sys): remove old build script --- libcoap-sys/build/build_system/vendored.rs | 1 - libcoap-sys/old_build.rs | 634 --------------------- 2 files changed, 635 deletions(-) delete mode 100644 libcoap-sys/old_build.rs diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index cb000e9c..6afd267f 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -2,7 +2,6 @@ use std::{ cell::RefCell, env, env::VarError, - ffi::OsString, path::{Path, PathBuf}, process::Command, }; diff --git a/libcoap-sys/old_build.rs b/libcoap-sys/old_build.rs deleted file mode 100644 index 96c470f6..00000000 --- a/libcoap-sys/old_build.rs +++ /dev/null @@ -1,634 +0,0 @@ -// SPDX-License-Identifier: BSD-2-CLAUSE -/* - * build.rs - build script for libcoap Rust bindings. - * This file is part of the libcoap-sys crate, see the README and LICENSE files for - * more information and terms of use. - * Copyright © 2021-2023 The NAMIB Project Developers, all rights reserved. - * See the README as well as the LICENSE file for more information. - */ - -use std::{ - cell::RefCell, - collections::BTreeSet, - default::Default, - env, - ffi::{OsStr, OsString}, - fmt::{Debug, Display}, - io::ErrorKind, - path::{Path, PathBuf}, - process::Command, - rc::Rc, -}; - -mod bindings; -mod build_system; -mod metadata; - -use bindgen::{ - callbacks::{IntKind, ParseCallbacks}, - EnumVariation, -}; -use pkg_config::probe_library; -use version_compare::{Cmp, Version}; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum DtlsBackend { - GnuTls, - OpenSsl, - MbedTls, - TinyDtls, -} -impl Display for DtlsBackend { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let str = match self { - DtlsBackend::GnuTls => "gnutls", - DtlsBackend::OpenSsl => "openssl", - DtlsBackend::MbedTls => "mbedtls", - DtlsBackend::TinyDtls => "tinydtls", - } - .to_string(); - write!(f, "{}", str) - } -} - -fn get_target_mcu() -> &'static str { - let cfg_flags = embuild::espidf::sysenv::cfg_args().expect("missing cfg flags from IDF"); - let mcus = [ - "esp32", "esp32s2", "esp32s3", "esp32c3", "esp32c2", "esp32h2", "esp32c5", "esp32c6", "esp32p4", - ]; - for mcu in mcus { - if cfg_flags.get(mcu).is_some() { - return mcu; - } - } - panic!("unknown ESP target MCU, please add target to libcoap-sys build.rs file!") -} - -fn get_builder_espidf() -> bindgen::Builder { - embuild::espidf::sysenv::output(); - let esp_idf_path = embuild::espidf::sysenv::idf_path().expect("missing IDF path"); - let esp_idf_buildroot = env::var("DEP_ESP_IDF_ROOT").expect("DEP_ESP_IDF_ROOT is not set"); - let esp_include_path = embuild::espidf::sysenv::cincl_args().expect("missing IDF cincl args"); - let embuild_env = embuild::espidf::sysenv::env_path().expect("missing IDF env path"); - let esp_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("CARGO_CFG_TARGET_ARCH is not set"); - - // Determine compiler path - // SAFETY: Always safe to call in a single-threaded environment (see docs of env::set_var). - unsafe { env::set_var("PATH", embuild_env) }; - let cmake_info = embuild::cmake::Query::new( - &Path::new(&esp_idf_buildroot).join("build"), - "cargo", - &[ - embuild::cmake::file_api::ObjKind::Codemodel, - embuild::cmake::file_api::ObjKind::Toolchains, - embuild::cmake::file_api::ObjKind::Cache, - ], - ) - .expect("unable to query cmake API for compiler path") - .get_replies() - .expect("unable to get cmake query replies for compiler path"); - let compiler = cmake_info - .get_toolchains() - .map_err(|_e| "Can't get toolchains") - .and_then(|mut t| { - t.take(embuild::cmake::file_api::codemodel::Language::C) - .ok_or("No C toolchain") - }) - .and_then(|t| t.compiler.path.ok_or("No compiler path set")) - .expect("unable to determine compiler path"); - - // Parse include arguments - // Regexes are correct and never change, therefore it is ok to unwrap here. - let arg_splitter = regex::Regex::new(r##"(?:[^\\]"[^"]*[^\\]")?(\s)"##).unwrap(); - let apostrophe_remover = regex::Regex::new(r##"^"(?.*)"$"##).unwrap(); - let esp_clang_args = arg_splitter - .split(esp_include_path.args.as_str()) - .map(|x| apostrophe_remover.replace(x.trim(), "$content").to_string()) - .collect::>(); - let bindgen_builder = embuild::bindgen::Factory { - clang_args: esp_clang_args.clone(), - linker: Some(compiler), - mcu: None, - force_cpp: false, - sysroot: None, - } - .builder() - .expect("unable to create bindgen builder for libcoap bindings from ESP-IDF"); - - let clang_target = if esp_arch.starts_with("riscv32") { - "riscv32" - } else { - esp_arch.as_str() - }; - let short_target = if esp_arch.starts_with("riscv32") { - "riscv" - } else { - esp_arch.as_str() - }; - let target_mcu = get_target_mcu(); - - bindgen_builder - .clang_args(&esp_clang_args) - .clang_arg("-target") - .clang_arg(clang_target) - .clang_arg("-DESP_PLATFORM") - .clang_arg("-DLWIP_IPV4=1") - .clang_arg("-DLWIP_IPV6=1") - .clang_arg("-DconfigUSE_PASSIVE_IDLE_HOOK=1") - .clang_arg(format!("-I{}/components/newlib/platform_include", esp_idf_path)) - .clang_arg(format!("-I{}/components/lwip/port/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/lwip/port/esp32xx/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/lwip/lwip/src/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/lwip/port/freertos/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/esp_system/include", esp_idf_path)) - .clang_arg(format!( - "-I{}/components/freertos/config/include/freertos", - esp_idf_path - )) - .clang_arg(format!("-I{}/components/freertos/esp_additions/include", esp_idf_path)) - .clang_arg(format!( - "-I{}/components/freertos/esp_additions/include/freertos", - esp_idf_path - )) - .clang_arg(format!( - "-I{}/components/freertos/esp_additions/arch/{}/include", - esp_idf_path, short_target - )) // for older espidf - .clang_arg(format!( - "-I{}/components/freertos/config/{}/include", - esp_idf_path, short_target - )) // for newer espidf - .clang_arg(format!("-I{}/components/{}/include", esp_idf_path, short_target)) - .clang_arg(format!( - "-I{}/components/{}/{}/include", - esp_idf_path, short_target, target_mcu - )) - .clang_arg(format!("-I{}/components/esp_hw_support/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/esp_common/include", esp_idf_path)) - .clang_arg(format!( - "-I{}/components/freertos/FreeRTOS-Kernel-SMP/include", - esp_idf_path - )) - .clang_arg(format!( - "-I{}/components/freertos/FreeRTOS-Kernel-SMP/portable/{}/include/freertos", - esp_idf_path, short_target - )) - .clang_arg(format!("-I{}/components/soc/{}/include", esp_idf_path, target_mcu)) - .clang_arg(format!("-I{}/components/heap/include", esp_idf_path)) - .clang_arg(format!("-I{}/components/esp_rom/include", esp_idf_path)) - .clang_arg(format!( - "-I{}/managed_components/espressif__coap/port/include", - esp_idf_buildroot - )) - .clang_arg(format!( - "-I{}/managed_components/espressif__coap/libcoap/include", - esp_idf_buildroot - )) - .clang_arg(format!("-I{}/build/config/", esp_idf_buildroot)) - .allowlist_type("epoll_event") -} - -fn get_builder() -> bindgen::Builder { - bindgen::Builder::default().blocklist_type("epoll_event") -} - -fn build_vendored_library( - out_dir: &OsString, - dtls_backend: Option<&DtlsBackend>, - mut builder: bindgen::Builder, -) -> bindgen::Builder { - let libcoap_src_dir = Path::new(&out_dir).join("libcoap"); - - // Even though libcoap supports out-of-source builds, autogen.sh (or the corresponding - // autotools) modify files in the source tree, which causes verification problems when - // running cargo package. - // Therefore, we copy the libcoap source over to the output directory and build from there. - let copy_options = fs_extra::dir::CopyOptions { - overwrite: true, - ..Default::default() - }; - match std::fs::remove_dir_all(&libcoap_src_dir) { - Ok(_) => {}, - Err(e) if e.kind() == ErrorKind::NotFound => {}, - e => e.expect("unable to clear libcoap build directory"), - } - fs_extra::dir::copy( - Path::new(env!("CARGO_MANIFEST_DIR")).join("src").join("libcoap"), - Path::new(&out_dir), - ©_options, - ) - .expect("unable to prepare libcoap build source directory"); - let current_dir_backup = env::current_dir().expect("unable to get current directory"); - env::set_current_dir(&libcoap_src_dir).expect("unable to change to libcoap build dir"); - Command::new(libcoap_src_dir.join("autogen.sh")) - .status() - .expect("unable to execute autogen.sh"); - let mut build_config = autotools::Config::new(&libcoap_src_dir); - build_config.out_dir(out_dir); - if let Some(dtls_backend) = dtls_backend { - build_config - .enable("dtls", None) - .with(dtls_backend.to_string().as_str(), None); - - // If DTLS is vendored we need to tell libcoap about the vendored version - match dtls_backend { - DtlsBackend::TinyDtls => { - // We do not ship tinydtls with our source distribution. Instead, we use tinydtls-sys. - build_config.without("submodule-tinydtls", None); - - // If tinydtls-sys is built with the vendored feature, the library is built alongside - // the Rust crate. To use the version built by the tinydtls-sys build script, we use the - // environment variables set by the build script. - if let Some(tinydtls_include) = env::var_os("DEP_TINYDTLS_INCLUDE") { - build_config.env( - "TinyDTLS_CFLAGS", - format!( - "-I{} -I{}", - tinydtls_include - .to_str() - .expect("DEP_TINYDTLS_INCLUDE is not a valid string"), - Path::new(&tinydtls_include) - .join("tinydtls") - .to_str() - .expect("DEP_TINYDTLS_INCLUDE is not a valid string") - ), - ); - }; - - if let Some(tinydtls_libs) = env::var_os("DEP_TINYDTLS_LIBS") { - build_config.env( - "TinyDTLS_LIBS", - format!( - "-L{}", - tinydtls_libs.to_str().expect("DEP_TINYDTLS_LIBS is invalid string") - ), - ); - - build_config.env( - "PKG_CONFIG_PATH", - Path::new(tinydtls_libs.as_os_str()) - .join("lib") - .join("pkgconfig") - .into_os_string(), - ); - } - }, - DtlsBackend::OpenSsl => { - // Set include path according to the path provided by openssl-sys (relevant if - // openssl-sys is vendored) - if let Some(openssl_include) = env::var_os("DEP_OPENSSL_INCLUDE") { - build_config.env( - "OpenSSL_CFLAGS", - format!( - "-I{}", - openssl_include.to_str().expect("DEP_OPENSSL_INCLUDE is invalid path") - ), - ); - build_config.env( - "PKG_CONFIG_PATH", - Path::new(openssl_include.as_os_str()) - .parent() - .expect("DEP_OPENSSL_INCLUDE has no parent directory") - .join("lib") - .join("pkgconfig") - .into_os_string(), - ); - } - }, - DtlsBackend::MbedTls => { - // Set include path according to the path provided by mbedtls-sys (relevant if - // mbedtls-sys is vendored). - // libcoap doesn't support overriding the MbedTLS CFLAGS, but doesn't set those - // either, so we just set CFLAGS and hope they propagate. - if let Some(mbedtls_include) = env::var_os("DEP_MBEDTLS_INCLUDE") { - // the config.h of mbedtls-sys-auto is generated separately from all other - // includes in the root of mbedtls-sys-auto's OUT_DIR. - // In order to let libcoap read use the correct config file, we need to copy - // this file into our own OUT_DIR under include/mbedtls/config.h, so that we - // can then set OUT_DIR/include as an additional include path. - let config_h = env::var_os("DEP_MBEDTLS_CONFIG_H") - .expect("DEP_MBEDTLS_INCLUDE is set but DEP_MBEDTLS_CONFIG_H is not"); - let config_path = Path::new(&config_h); - let out_include = Path::new(&out_dir).join("include"); - std::fs::create_dir_all(out_include.join("mbedtls")) - .expect("unable to prepare include directory for mbedtls config.h"); - std::fs::copy(config_path, out_include.join("mbedtls").join("config.h")) - .expect("unable to copy mbedtls config.h to include directory"); - let mbedtls_library_path = config_path - .parent() - .expect("DEP_MBEDTLS_CONFIG_H has no parent directory") - .join("build") - .join("library"); - build_config.env( - "MbedTLS_CFLAGS", - format!( - "-I{} -I{}", - out_include.to_str().expect("OUT_DIR is not a valid string"), - mbedtls_include - .to_str() - .expect("DEP_MBEDTLS_INCLUDE is not a valid string") - ), - ); - build_config.env( - "MbedTLS_LIBS", - format!( - "-L{0} -l:libmbedtls.a -l:libmbedcrypto.a -l:libmbedx509.a", - mbedtls_library_path - .to_str() - .expect("DEP_MBEDTLS_CONFIG_H is not a valid string"), - ), - ); - } - }, - DtlsBackend::GnuTls => { - // Vendoring not supported - }, - } - } else { - build_config.disable("dtls", None); - } - build_config - // Disable shared library compilation because the vendored library will always be - // statically linked - .disable("shared", None) - // Disable any documentation for vendored C library - .disable("documentation", None) - .disable("doxygen", None) - .disable("manpages", None) - // This would install the license into the documentation directory, but we don't use the - // generated documentation anywhere. - .disable("license-install", None) - // Disable tests and examples as well as test coverage - .disable("tests", None) - .disable("examples", None) - .disable("gcov", None); - - // Enable debug symbols if enabled in Rust - match env::var_os("DEBUG") - .expect("env variable DEBUG that should have been set by cargo is not set") - .to_str() - .expect("env variable DEBUG is not valid") - { - "0" | "false" => {}, - _ => { - build_config.with("debug", None); - }, - } - // Enable dependency features based on selected cargo features. - build_config - .enable("oscore", Some(if cfg!(feature = "oscore") { "yes" } else { "no" })) - .enable("ipv4-support", Some(if cfg!(feature = "ipv4") { "yes" } else { "no" })) - .enable("ipv6-support", Some(if cfg!(feature = "ipv6") { "yes" } else { "no" })) - .enable( - "af-unix-support", - Some(if cfg!(feature = "af-unix") { "yes" } else { "no" }), - ) - .enable("tcp", Some(if cfg!(feature = "tcp") { "yes" } else { "no" })) - .enable( - "websockets", - Some(if cfg!(feature = "websockets") { "yes" } else { "no" }), - ) - .enable("async", Some(if cfg!(feature = "async") { "yes" } else { "no" })) - .enable( - "observe-persist", - Some(if cfg!(feature = "observe-persist") { "yes" } else { "no" }), - ) - .enable("q-block", Some(if cfg!(feature = "q-block") { "yes" } else { "no" })) - .enable( - "thread-safe", - Some(if cfg!(feature = "thread-safe") { "yes" } else { "no" }), - ) - .enable( - "thread-recursive-lock-detection", - Some(if cfg!(feature = "thread-recursive-lock-detection") { - "yes" - } else { - "no" - }), - ) - .enable( - "small-stack", - Some(if cfg!(feature = "small-stack") { "yes" } else { "no" }), - ) - .enable("server-mode", Some(if cfg!(feature = "server") { "yes" } else { "no" })) - .enable("client-mode", Some(if cfg!(feature = "client") { "yes" } else { "no" })) - .with("epoll", Some(if cfg!(feature = "epoll") { "yes" } else { "no" })); - - // Run build - let dst = build_config.build(); - - // Add the built library to the search path - println!( - "cargo:rustc-link-search=native={}", - dst.join("lib") - .to_str() - .expect("libcoap build output dir is not a valid string") - ); - println!( - "cargo:include={}", - dst.join("include") - .to_str() - .expect("libcoap build output dir is not a valid string") - ); - builder = builder - .clang_arg(format!( - "-I{}", - dst.join("include") - .to_str() - .expect("libcoap build output dir is not a valid string") - )) - .clang_arg(format!( - "-L{}", - dst.join("lib") - .to_str() - .expect("libcoap build output dir is not a valid string") - )); - env::set_current_dir(current_dir_backup).expect("unable to switch back to source dir"); - builder -} - -fn main() { - println!("cargo::rustc-check-cfg=cfg(feature_checks_available)"); - println!("cargo::rustc-check-cfg=cfg(non_inlined_coap_send_rst)"); - println!("cargo:rerun-if-changed=src/libcoap/"); - println!("cargo:rerun-if-changed=src/wrapper.h"); - // Read required environment variables. - let out_dir = env::var_os("OUT_DIR").expect("unsupported OUT_DIR"); - let target_os = env::var("CARGO_CFG_TARGET_OS").expect("invalid TARGET_OS environment variable"); - - let mut bindgen_builder = match target_os.as_str() { - "espidf" => get_builder_espidf(), - _ => get_builder(), - }; - - let mut dtls_backend = Option::None; - if cfg!(feature = "dtls") { - // We can only select one TLS backend at a time for libcoap, but cargo does not support mutually - // exclusive features, and it would be really bad if a project that uses multiple dependencies - // which depend on different TLS backends would not compile. - // Therefore, if multiple TLS backend features are enabled, we choose one based on the following - // priority order: gnutls > openssl > mbedtls > tinydtls, matching the order specified in - // https://github.com/obgm/libcoap/blob/develop/configure.ac#L494 - let mut multiple_backends = false; - if cfg!(feature = "dtls_backend_tinydtls") { - dtls_backend = Some(DtlsBackend::TinyDtls); - } - if cfg!(feature = "dtls_backend_mbedtls") { - if dtls_backend.is_some() { - multiple_backends = true; - } - println!("cargo:rerun-if-env-changed=MBEDTLS_LIBRARY_PATH"); - dtls_backend = Some(DtlsBackend::MbedTls); - } - if cfg!(feature = "dtls_backend_openssl") { - if dtls_backend.is_some() { - multiple_backends = true; - } - dtls_backend = Some(DtlsBackend::OpenSsl); - } - if cfg!(feature = "dtls_backend_gnutls") { - if dtls_backend.is_some() { - multiple_backends = true; - } - dtls_backend = Some(DtlsBackend::GnuTls); - } - if multiple_backends { - // more than one backend was set, so unwrapping is ok here. - println!("cargo:warning=Multiple DTLS backends enabled for libcoap-sys. Only one can be enabled, choosing {:?} as the backend to use. This may cause problems.", dtls_backend.as_ref().unwrap()); - } - if dtls_backend.is_none() { - println!("cargo:warning=No DTLS backend selected for libcoap-sys, aborting build."); - panic!("No DTLS backend selected for libcoap-sys, aborting build") - } - } - - // Build vendored library if feature was set. - if cfg!(feature = "vendored") && target_os.as_str() != "espidf" { - bindgen_builder = build_vendored_library(&out_dir, dtls_backend.as_ref(), bindgen_builder); - }; - - if target_os.as_str() != "espidf" { - // Tell cargo to link libcoap. - println!( - "cargo:rustc-link-lib={}{}", - cfg!(feature = "static").then(|| "static=").unwrap_or("dylib="), - format!( - "coap-3-{}", - &dtls_backend - .as_ref() - .map(|v| v.to_string()) - .unwrap_or_else(|| "notls".to_string()) - ) - .as_str() - ); - - // For the DTLS libraries, we need to tell cargo which external libraries to link. - // Note that these linker instructions have to be added *after* the linker instruction - // for libcoap itself, as some linkers require dependencies to be in reverse order. - if let Some(dtls_backend) = dtls_backend { - match dtls_backend { - DtlsBackend::TinyDtls => { - // Handled by tinydtls-sys - }, - DtlsBackend::OpenSsl => { - // Handled by openssl-sys - }, - DtlsBackend::MbedTls => { - // If mbedtls is vendored, mbedtls-sys-auto already takes care of linking. - if env::var_os("DEP_MBEDTLS_INCLUDE").is_none() { - // We aren't using mbedtls-sys-auto if we aren't vendoring (as it doesn't support - // mbedtls >= 3.0.0), so we need to tell cargo to link to mbedtls ourselves. - - // Try to find mbedtls using pkg-config, will emit cargo link statements if successful - if pkg_config::Config::new() - .statik(cfg!(feature = "static")) - .probe("mbedtls") - .is_err() - { - // couldn't find using pkg-config, just try linking with given library - // search path. - println!("cargo:rustc-link-lib=mbedtls",); - println!("cargo:rustc-link-lib=mbedx509",); - println!("cargo:rustc-link-lib=mbedcrypto",); - } - } - }, - DtlsBackend::GnuTls => { - // gnutls-sys is unmaintained, so we need to link to gnutls ourselves. - - // try pkg-config - if probe_library("gnutls").is_err() { - // if that doesn't work, try using the standard library search path. - println!("cargo:rustc-link-lib=gnutls") - } - }, - } - } - } - - let libcoap_defines = Rc::new(RefCell::new(LibcoapDefineMetadata::default())); - - let cfg_info = Box::new(CoapDefineParser { - defines: Rc::clone(&libcoap_defines), - }); - - bindgen_builder = bindgen_builder - .header("src/wrapper.h") - .default_enum_style(EnumVariation::Rust { non_exhaustive: true }) - // Causes invalid syntax for some reason, so we have to disable it. - .generate_comments(false) - .dynamic_link_require_all(true) - .allowlist_function("(oscore|coap)_.*") - .allowlist_type("(oscore|coap)_.*") - .allowlist_var("(oscore|coap)_.*") - .allowlist_function("(OSCORE|COAP)_.*") - .allowlist_type("(OSCORE|COAP)_.*") - .allowlist_var("(OSCORE|COAP|LIBCOAP)_.*") - // We use the definitions made by the libc crate instead - .blocklist_type("sockaddr(_in|_in6)?") - .blocklist_type("in6?_(addr|port)(_t)?") - .blocklist_type("in6_addr__bindgen_ty_1") - .blocklist_type("(__)?socklen_t") - .blocklist_type("fd_set") - .blocklist_type("sa_family_t") - .blocklist_type("(__)?time_t") - .blocklist_type("__fd_mask") - // Are generated because they are typedef-ed inside of the C headers, blocklisting them - // will instead replace them with the appropriate rust types. - // See https://github.com/rust-lang/rust-bindgen/issues/1215 for an open issue concerning - // this problem. - .blocklist_type("__(u)?int(8|16|32|64|128)_t") - .size_t_is_usize(true) - .parse_callbacks(cfg_info); - if !cfg!(feature = "vendored") { - // Triggers a rebuild on every cargo build invocation if used for the vendored version, as - // the included headers seem to come from our built version. - // Should be fine though, as we already printed `cargo:rerun-if-changed=src/libcoap/` at the - // start of the file. - bindgen_builder = bindgen_builder.parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); - } - let bindings = bindgen_builder.generate().expect("unable to generate bindings"); - - // Check if required features are available in libcoap. - let libcoap_defines = libcoap_defines.take(); - if libcoap_defines.feature_defines_available { - println!("cargo:rustc-cfg=feature_checks_available"); - for feature in COMPILE_TIME_FEATURE_CHECKS { - let feature_env_var_name = "CARGO_FEATURE_".to_string() + &feature.replace('-', "_").to_uppercase(); - if env::var(&feature_env_var_name).is_ok() && !libcoap_defines.feature_defines.contains(feature) { - panic!("Required feature {feature} is not available in the used version of libcoap!"); - } - } - if dtls_backend != libcoap_defines.dtls_backend { - // Should be fine, as applications should expect that the DTLS library could differ. - println!("cargo:warning=DTLS library used by libcoap does not match chosen one. This might lead to issues.") - } - } else { - println!("cargo:warning=The used version of libcoap does not provide a coap_defines.h file, either because it is too old (<4.3.5) or because this file is somehow not included. Compile-time feature checks are not available, and the availability of some features (small-stack, IPv4/IPv6,) cannot be asserted at all!"); - } - - let out_path = PathBuf::from(out_dir); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("unable to write generated bindings to file"); -} From 448822f4943d83c14cef7d0b4b2785ca9a4db749 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Thu, 23 Jan 2025 16:53:37 +0100 Subject: [PATCH 24/26] refactor(lib|sys): address comments from PR #42 Co-authored-by: Jan Romann --- libcoap-sys/Cargo.toml | 2 +- libcoap-sys/build/bindings.rs | 15 ++++++++- libcoap-sys/build/build_system/esp_idf.rs | 31 ++++++++++++++++--- libcoap-sys/build/build_system/manual.rs | 10 +++++- libcoap-sys/build/build_system/mod.rs | 10 +++++- libcoap-sys/build/build_system/pkgconfig.rs | 9 ++++++ libcoap-sys/build/build_system/vendored.rs | 9 ++++++ libcoap-sys/build/main.rs | 13 ++++++-- libcoap-sys/build/metadata.rs | 9 ++++++ .../examples/esp-idf/sdkconfig.defaults | 2 +- libcoap/examples/esp-idf/sdkconfig.defaults | 2 +- libcoap/tests/common/mod.rs | 7 +++-- 12 files changed, 103 insertions(+), 16 deletions(-) diff --git a/libcoap-sys/Cargo.toml b/libcoap-sys/Cargo.toml index 323da3c6..08faf2ee 100644 --- a/libcoap-sys/Cargo.toml +++ b/libcoap-sys/Cargo.toml @@ -42,7 +42,7 @@ default = [ "thread-recursive-lock-detection", "server", "client", - "epoll" + "epoll", # TODO add proxy ] diff --git a/libcoap-sys/build/bindings.rs b/libcoap-sys/build/bindings.rs index 3f43ca9a..c8f95868 100644 --- a/libcoap-sys/build/bindings.rs +++ b/libcoap-sys/build/bindings.rs @@ -1,3 +1,12 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * build/bindings.rs - Binding generation tools for the libcoap-sys build script. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2025 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ + use std::{cell::RefCell, fmt::Debug, path::PathBuf, rc::Rc}; use anyhow::{Context, Result}; @@ -18,7 +27,11 @@ impl LibcoapDefineParser { let host = std::env::var_os("HOST").unwrap_or_default(); if target != host { - println!("cargo:warning=libcoap-rs compile-time feature checks may be inaccurate when cross compiling, see https://libcoap.net/doc/reference/4.3.5/man_coap_supported.html for more information."); + println!(concat!( + "cargo:warning=libcoap-rs compile-time feature checks may be inaccurate when cross", + " compiling, see https://libcoap.net/doc/reference/4.3.5/man_coap_supported.html", + " for more information." + )); } let value: LibcoapDefineParser = Default::default(); diff --git a/libcoap-sys/build/build_system/esp_idf.rs b/libcoap-sys/build/build_system/esp_idf.rs index 59f2a8eb..02379a47 100644 --- a/libcoap-sys/build/build_system/esp_idf.rs +++ b/libcoap-sys/build/build_system/esp_idf.rs @@ -1,3 +1,11 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * build/build_system/esp_idf.rs - ESP-IDF build system for libcoap-sys. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2025 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ use std::{env, fs::File, io::Write, iter::once, path::PathBuf}; use anyhow::{anyhow, Context, Result}; @@ -32,7 +40,7 @@ impl EspIdfBuildSystem { if let Some(backend) = requested_dtls_backend { if backend != DtlsBackend::MbedTls { - return Err(anyhow!("libcoap only supports the MbedTLS DTLS backend when compiling for the ESP-IDF, but you have requested the {backend} backend.")); + return Err(anyhow!("libcoap only supports the MbedTLS DTLS backend when compiling for ESP-IDF, but you have requested the {backend} backend.")); } } @@ -60,7 +68,18 @@ impl BuildSystem for EspIdfBuildSystem { .filter(|v| v.define_name().is_some() && v.sdkconfig_flag_name().is_none()) .collect(); if !uncheckable_features.is_empty() { - println!("cargo:warning=When building for ESP-IDF, the availability of the following requested features that usually can be checked during compile time can only be checked during runtime instead: {}", uncheckable_features.iter().map(|v| v.as_str()).collect::>().join(", ")) + println!( + concat!( + "cargo:warning=When building for ESP-IDF, the availability of the following ", + "requested features that usually can be checked during compile time can only", + "be checked during runtime instead: {}" + ), + uncheckable_features + .iter() + .map(|v| v.as_str()) + .collect::>() + .join(", ") + ) } Some(self.requested_features) @@ -115,8 +134,9 @@ impl BuildSystem for EspIdfBuildSystem { }; for ident in ident.map(|i| i.to_string()) { + let lowercase_ident = ident.to_lowercase(); // If the item belongs to the libcoap crate (starts with coap or oscore), re-export it in our bindings. - if ident.to_lowercase().starts_with("coap") || ident.to_lowercase().starts_with("oscore") { + if lowercase_ident.starts_with("coap") || lowercase_ident.starts_with("oscore") { writeln!(&mut libcoap_bindings_file, "pub use esp_idf_sys::{};", ident) .context("unable to write to bindings file")?; } @@ -129,16 +149,17 @@ impl BuildSystem for EspIdfBuildSystem { .iter() .filter_map(|v| v.sdkconfig_flag_name().map(|flag| (v.as_str(), flag))) { + let feature_flag_lowercase = feature_flag.to_lowercase(); // For some reason, embuild adds expected cfg flags for some, but not all // feature-related sdkconfig flags, causing warnings if we don't do this. - println!("cargo::rustc-check-cfg=cfg(esp_idf_{})", feature_flag.to_lowercase()); + println!("cargo::rustc-check-cfg=cfg(esp_idf_{})", feature_flag_lowercase); writeln!( &mut libcoap_bindings_file, // Only show these errors if the coap component is enabled at all (in order to // only show the relevant compilation error). "#[cfg(all(esp_idf_comp_espressif__coap_enabled, not(esp_idf_{})))]", - feature_flag.to_lowercase() + feature_flag_lowercase ) .context("unable to write to bindings file")?; writeln!( diff --git a/libcoap-sys/build/build_system/manual.rs b/libcoap-sys/build/build_system/manual.rs index e8a45ec1..fc18437c 100644 --- a/libcoap-sys/build/build_system/manual.rs +++ b/libcoap-sys/build/build_system/manual.rs @@ -1,3 +1,12 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * build/build_system/manual.rs - Manual build system for libcoap-sys. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2025 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ + use std::{cell::RefCell, env, env::VarError, path::PathBuf}; use anyhow::{Context, Result}; @@ -9,7 +18,6 @@ use crate::{ build_system::BuildSystem, metadata::{DtlsBackend, LibcoapDefineInfo, LibcoapFeature}, }; - pub struct ManualBuildSystem { out_dir: PathBuf, include_dirs: Vec, diff --git a/libcoap-sys/build/build_system/mod.rs b/libcoap-sys/build/build_system/mod.rs index 47651d29..ebc12399 100644 --- a/libcoap-sys/build/build_system/mod.rs +++ b/libcoap-sys/build/build_system/mod.rs @@ -1,3 +1,11 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * build/build_system/mod.rs - Basic definitions for libcoap-sys build systems. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2025 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ use std::path::PathBuf; use anyhow::Result; @@ -13,7 +21,7 @@ pub mod vendored; /// Trait that is implemented by build systems for libcoap. /// -/// It is assumed that the constructor structs implementing this trait already performs all +/// It is assumed that the constructor structs implementing this trait already perform all /// necessary steps to link against libcoap, and that only binding generation and compile-time /// checks remain. /// diff --git a/libcoap-sys/build/build_system/pkgconfig.rs b/libcoap-sys/build/build_system/pkgconfig.rs index b7b4fe0b..3d972930 100644 --- a/libcoap-sys/build/build_system/pkgconfig.rs +++ b/libcoap-sys/build/build_system/pkgconfig.rs @@ -1,3 +1,12 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * build/build_system/pkgconfig.rs - pkg-config build system for libcoap-sys. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2025 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ + use std::{cell::RefCell, path::PathBuf}; use anyhow::{anyhow, Context}; diff --git a/libcoap-sys/build/build_system/vendored.rs b/libcoap-sys/build/build_system/vendored.rs index 6afd267f..58d2d891 100644 --- a/libcoap-sys/build/build_system/vendored.rs +++ b/libcoap-sys/build/build_system/vendored.rs @@ -1,3 +1,12 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * build/build_system/vendored.rs - Vendored build system for libcoap-sys. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2025 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ + use std::{ cell::RefCell, env, diff --git a/libcoap-sys/build/main.rs b/libcoap-sys/build/main.rs index bc166b5a..96c7baae 100644 --- a/libcoap-sys/build/main.rs +++ b/libcoap-sys/build/main.rs @@ -1,3 +1,12 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * build/main.rs - Build script entrypoint for libcoap-sys. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2025 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ + use std::{env, env::VarError, path::PathBuf}; use anyhow::{anyhow, bail, Context, Result}; @@ -103,10 +112,10 @@ fn main() -> Result<()> { if let Some(req_backend) = requested_dtls_backend { assert_eq!(req_backend, dtls_backend, concat!( - "the libcoap-rs compile-time check has determined that the DTLS library\n", + "The libcoap-rs compile-time check has determined that the DTLS library\n", "the used version of libcoap linked against ({}) does not match the one set in LIBCOAP_RS_DTLS_BACKEND ({}).\n", "If you are certain that this check is mistaken (e.g., because you are cross-compiling), you\n", - "may bypass this check by setting the `LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS` environment\n", + "may bypass it by setting the `LIBCOAP_RS_BYPASS_COMPILE_FEATURE_CHECKS` environment\n", "variable to any non-zero value.\n", "Be aware, however, that this might lead to more cryptic errors if the requested features are\n", "not available after all.\n", diff --git a/libcoap-sys/build/metadata.rs b/libcoap-sys/build/metadata.rs index 631906b3..b7e63bbf 100644 --- a/libcoap-sys/build/metadata.rs +++ b/libcoap-sys/build/metadata.rs @@ -1,3 +1,12 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * build/metadata.rs - Type definitions for metadata in the libcoap-sys build script. + * This file is part of the libcoap-sys crate, see the README and LICENSE files for + * more information and terms of use. + * Copyright © 2021-2025 The NAMIB Project Developers, all rights reserved. + * See the README as well as the LICENSE file for more information. + */ + use std::{ fmt::{Display, Formatter}, str::FromStr, diff --git a/libcoap-sys/examples/esp-idf/sdkconfig.defaults b/libcoap-sys/examples/esp-idf/sdkconfig.defaults index 81361c8b..1c2bfab4 100644 --- a/libcoap-sys/examples/esp-idf/sdkconfig.defaults +++ b/libcoap-sys/examples/esp-idf/sdkconfig.defaults @@ -18,4 +18,4 @@ CONFIG_COAP_SERVER_SUPPORT=y CONFIG_COAP_THREAD_RECURSIVE_CHECK=y CONFIG_COAP_THREAD_SAFE=y CONFIG_COAP_OBSERVE_PERSIST=y -CONFIG_COAP_WEBSOCKETS=y \ No newline at end of file +CONFIG_COAP_WEBSOCKETS=y diff --git a/libcoap/examples/esp-idf/sdkconfig.defaults b/libcoap/examples/esp-idf/sdkconfig.defaults index 81361c8b..1c2bfab4 100644 --- a/libcoap/examples/esp-idf/sdkconfig.defaults +++ b/libcoap/examples/esp-idf/sdkconfig.defaults @@ -18,4 +18,4 @@ CONFIG_COAP_SERVER_SUPPORT=y CONFIG_COAP_THREAD_RECURSIVE_CHECK=y CONFIG_COAP_THREAD_SAFE=y CONFIG_COAP_OBSERVE_PERSIST=y -CONFIG_COAP_WEBSOCKETS=y \ No newline at end of file +CONFIG_COAP_WEBSOCKETS=y diff --git a/libcoap/tests/common/mod.rs b/libcoap/tests/common/mod.rs index 8a63e287..02934c35 100644 --- a/libcoap/tests/common/mod.rs +++ b/libcoap/tests/common/mod.rs @@ -83,10 +83,11 @@ pub(crate) fn spawn_test_server) -> CoapContext<' if timeout_result.timed_out() && server_handle.is_finished() { if let Err(e) = server_handle.join() { std::panic::resume_unwind(e); - } else { - panic!("Test server thread is dead and has not reported readiness after 10 seconds, but has also not panicked.") } - } else if timeout_result.timed_out() { + panic!("Test server thread is dead and has not reported readiness after 10 seconds, but has also not panicked.") + } + + if timeout_result.timed_out() { panic!("Test server thread has not reported readiness after 10 seconds, but has also not died (deadlock?).") } } From 7f98718c6bdb08d2c5f52fa7bf27daf399760bbc Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Thu, 23 Jan 2025 17:03:46 +0100 Subject: [PATCH 25/26] fix(ci): update cache-apt-pkgs-action to fix upload-artifacts deprecation error --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eca1f6d5..2cb36940 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,7 +55,7 @@ jobs: components: rust-src, rustc, rust-std, cargo, llvm-tools, llvm-tools-preview toolchain: ${{ matrix.rust_version == 'msrv' && '1.82' || matrix.rust_version }} - if: matrix.dtls_backend == 'gnutls' - uses: awalsh128/cache-apt-pkgs-action@latest + uses: awalsh128/cache-apt-pkgs-action@v1.4.3 with: packages: libgnutls28-dev libgnutls30 version: 1.0 @@ -107,7 +107,7 @@ jobs: submodules: true ref: ${{ env.HEAD_REF }} # --all-features uses GNUTLS as backend, must provide it. - - uses: awalsh128/cache-apt-pkgs-action@latest + - uses: awalsh128/cache-apt-pkgs-action@v1.4.3 with: packages: libgnutls28-dev libgnutls30 version: 1.0 From 8eb6f1e547002efd5d8c420508303be9c5f41a62 Mon Sep 17 00:00:00 2001 From: Hugo Hakim Damer Date: Thu, 23 Jan 2025 17:47:56 +0100 Subject: [PATCH 26/26] docs(lib|sys): Add READMEs explaining esp-idf examples --- .idea/libcoap-rs.iml | 4 ++++ libcoap-sys/examples/esp-idf/.gitignore | 1 + libcoap-sys/examples/esp-idf/README.md | 6 ++++++ libcoap/examples/esp-idf/.gitignore | 1 + libcoap/examples/esp-idf/README.md | 6 ++++++ 5 files changed, 18 insertions(+) create mode 100644 libcoap-sys/examples/esp-idf/README.md create mode 100644 libcoap/examples/esp-idf/README.md diff --git a/.idea/libcoap-rs.iml b/.idea/libcoap-rs.iml index 6c1d6fb4..e82ac0ec 100644 --- a/.idea/libcoap-rs.iml +++ b/.idea/libcoap-rs.iml @@ -7,9 +7,13 @@ + + + + diff --git a/libcoap-sys/examples/esp-idf/.gitignore b/libcoap-sys/examples/esp-idf/.gitignore index 73a638b5..aa1d4bf7 100644 --- a/libcoap-sys/examples/esp-idf/.gitignore +++ b/libcoap-sys/examples/esp-idf/.gitignore @@ -2,3 +2,4 @@ /.embuild /target /Cargo.lock +/vars.log \ No newline at end of file diff --git a/libcoap-sys/examples/esp-idf/README.md b/libcoap-sys/examples/esp-idf/README.md new file mode 100644 index 00000000..25f49b59 --- /dev/null +++ b/libcoap-sys/examples/esp-idf/README.md @@ -0,0 +1,6 @@ +This folder contains a slightly modified version of the ESP-IDF Rust project template that can be found +[here](https://github.com/esp-rs/esp-idf-template/tree/master/cargo), with slight modifications to +`sdkconfig.defaults`, `Cargo.toml` and `main.rs` to add support for `libcoap-sys`. + +It is mainly used to test the compilation and binding generation process for ESP-IDF builds of `libcoap-sys`, +but you may also use it as a reference for your own projects. \ No newline at end of file diff --git a/libcoap/examples/esp-idf/.gitignore b/libcoap/examples/esp-idf/.gitignore index 73a638b5..aa1d4bf7 100644 --- a/libcoap/examples/esp-idf/.gitignore +++ b/libcoap/examples/esp-idf/.gitignore @@ -2,3 +2,4 @@ /.embuild /target /Cargo.lock +/vars.log \ No newline at end of file diff --git a/libcoap/examples/esp-idf/README.md b/libcoap/examples/esp-idf/README.md new file mode 100644 index 00000000..ba3f678b --- /dev/null +++ b/libcoap/examples/esp-idf/README.md @@ -0,0 +1,6 @@ +This folder contains a slightly modified version of the ESP-IDF Rust project template that can be found +[here](https://github.com/esp-rs/esp-idf-template/tree/master/cargo), with slight modifications to +`sdkconfig.defaults`, `Cargo.toml` and `main.rs` to add support for `libcoap-sys` and `libcoap-rs`. + +It is mainly used to test the compilation and binding generation process for ESP-IDF builds of `libcoap-rs`, +but you may also use it as a reference for your own projects. \ No newline at end of file