diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index fd640e95..953c7c92 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -327,6 +327,21 @@ jobs: - name: cargo test (debug; ring; -Z minimal-versions) run: cargo -Z minimal-versions test --no-default-features --features=ring --locked + tools: + name: Test rustls-ffi-tools + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Install rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - name: Run tools unit tests + run: cargo test -p rustls-ffi-tools + format: name: Format runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 88a4a934..c5aa6991 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1223,9 +1223,11 @@ version = "0.1.0" dependencies = [ "hickory-resolver", "rustls", + "rustls-ffi", "serde", "serde_json", "tokio", + "toml", "tree-sitter", "tree-sitter-c", "tree-sitter-md", diff --git a/librustls/Cargo.toml b/librustls/Cargo.toml index 2d3a419c..d08ea4b6 100644 --- a/librustls/Cargo.toml +++ b/librustls/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "rustls-ffi" +# Keep in sync with defines in cbindgen.toml version = "0.15.0" license = "Apache-2.0 OR ISC OR MIT" readme = "../README-crates.io.md" diff --git a/librustls/cbindgen.toml b/librustls/cbindgen.toml index 185deef2..5f500d06 100644 --- a/librustls/cbindgen.toml +++ b/librustls/cbindgen.toml @@ -1,6 +1,20 @@ include_guard = "RUSTLS_H" language = "C" +# Keep in sync with "package.version" in Cargo.toml after_includes = """ + +#define RUSTLS_VERSION_MAJOR 0 +#define RUSTLS_VERSION_MINOR 15 +#define RUSTLS_VERSION_PATCH 0 + +/** + * This gives each version part 8 bits, and leaves the 8 least significant bits + * empty for future additions, for example pre-release versions. + */ +#define RUSTLS_VERSION_NUMBER ((RUSTLS_VERSION_MAJOR << 24) \\ + |(RUSTLS_VERSION_MINOR << 16) \\ + |(RUSTLS_VERSION_MINOR << 8)) + #if defined(__clang__) || defined(__GNUC__) # define DEPRECATED_FUNC(why) __attribute__((deprecated(why))) #elif defined(_MSC_VER) diff --git a/librustls/src/rustls.h b/librustls/src/rustls.h index 041258d7..d3d9faa3 100644 --- a/librustls/src/rustls.h +++ b/librustls/src/rustls.h @@ -6,6 +6,19 @@ #include #include #include + +#define RUSTLS_VERSION_MAJOR 0 +#define RUSTLS_VERSION_MINOR 15 +#define RUSTLS_VERSION_PATCH 0 + +/** + * This gives each version part 8 bits, and leaves the 8 least significant bits + * empty for future additions, for example pre-release versions. + */ +#define RUSTLS_VERSION_NUMBER ((RUSTLS_VERSION_MAJOR << 24) \ + |(RUSTLS_VERSION_MINOR << 16) \ + |(RUSTLS_VERSION_MINOR << 8)) + #if defined(__clang__) || defined(__GNUC__) # define DEPRECATED_FUNC(why) __attribute__((deprecated(why))) #elif defined(_MSC_VER) diff --git a/tools/Cargo.toml b/tools/Cargo.toml index 91c30572..61a8a6e8 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -6,8 +6,10 @@ publish = false [dependencies] rustls = { workspace = true } +rustls-ffi = { path = "../librustls" } hickory-resolver = { workspace = true } tokio = { workspace = true } +toml = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } tree-sitter = { workspace = true } diff --git a/tools/tests/rustls_header_version.rs b/tools/tests/rustls_header_version.rs new file mode 100644 index 00000000..91b07103 --- /dev/null +++ b/tools/tests/rustls_header_version.rs @@ -0,0 +1,91 @@ +use std::fs::File; +use std::fs::read_to_string; +use std::io::Read; +use std::path::PathBuf; + +use toml::Table; +use tree_sitter::{Parser, Query, QueryCursor}; + +/// Ensure that the correct version part defines are in src/rustls.h +/// +/// If this test starts to fail, you probably forgot to update cbindgen.toml with new version +/// parts, or need to rerun cbindgen after updating it. +/// +/// This test is in the tools crate because it requires an msrv of 1.76 and the librustls crate +/// currently has an msrv of 1.73. +#[test] +fn rustls_header_versions_match() { + // Parse Cargo.toml as a generic TOML Table. + let mut metadata_file = + File::open(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../librustls/Cargo.toml")) + .expect("failed to open Cargo.toml"); + let mut metadata_content = String::new(); + metadata_file + .read_to_string(&mut metadata_content) + .expect("failed to read Cargo.toml"); + let metadata = metadata_content.parse::().unwrap(); + + // Find the crate version specified in Cargo.toml + let package_metadata = metadata["package"] + .as_table() + .expect("missing package metadata"); + let crate_version = package_metadata["version"] + .as_str() + .expect("missing crate version"); + + let version_in_header = version_in_header(); + assert_eq!( + crate_version, version_in_header, + "Version in header (.h file) doesn't match version in Cargo.toml" + ); +} + +fn version_in_header() -> String { + // Create a C parser. + let mut parser = Parser::new(); + let language = tree_sitter_c::LANGUAGE; + parser.set_language(&language.into()).unwrap(); + + // Parse the .h into an AST. + let header_file = + read_to_string("../librustls/src/rustls.h").expect("Couldn't read header file"); + + let header_file_bytes = header_file.as_bytes(); + let tree = parser + .parse(&header_file, None) + .ok_or("no tree parsed from input") + .unwrap(); + let query = r#" + (preproc_def name: (identifier) @define.name + (#match? @define.name "^RUSTLS_VERSION_[MAJOR|MINOR|PATCH]") + )"#; + let query = Query::new(&language.into(), query).unwrap(); + let mut cursor = QueryCursor::new(); + let matches = cursor.matches(&query, tree.root_node(), header_file_bytes); + let mut version_parts: [&str; 3] = Default::default(); + for query_match in matches { + for preproc in query_match.nodes_for_capture_index(0) { + let Some(value_node) = preproc.parent().unwrap().child_by_field_name("value") else { + continue; + }; + let key = preproc.utf8_text(header_file_bytes).unwrap(); + let value = value_node.utf8_text(header_file_bytes).unwrap(); + match key { + "RUSTLS_VERSION_MAJOR" => { + version_parts[0] = value; + } + "RUSTLS_VERSION_MINOR" => { + version_parts[1] = value; + } + "RUSTLS_VERSION_PATCH" => { + version_parts[2] = value; + } + _ => (), + } + } + } + format!( + "{0}.{1}.{2}", + version_parts[0], version_parts[1], version_parts[2] + ) +}