Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ permissions:
pull-requests: write

env:
FEATURES: "async,ffi,qlog"
# `rpk` covers the boring 4 vs 5 RPK arms in
# `tokio-quiche/src/settings/config.rs`; see the same comment in
# `stable.yml`.
FEATURES: "async,ffi,qlog,rpk"
RUSTFLAGS: "-D warnings"
RUSTDOCFLAGS: "--cfg docsrs"
RUSTTOOLCHAIN: "nightly"
Expand Down Expand Up @@ -68,12 +71,15 @@ jobs:
# duplicate builds for PRs created from internal branches.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
# `quiche-fuzz` calls `RAND_reset_for_fuzzing`, which BoringSSL only
# exports when `BORINGSSL_UNSAFE_DETERMINISTIC_MODE` is defined.
# `boring-sys` doesn't expose a feature for that, but cmake-rs honors
# `CFLAGS`/`CXXFLAGS`, so inject the defines there.
# exports when built with `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`.
# BoringSSL 5.x consolidated the older
# `BORINGSSL_UNSAFE_{DETERMINISTIC,FUZZER}_MODE` defines into this
# single switch. `boring-sys` doesn't expose a feature for it, but
# cmake-rs (via cc-rs) honors `CFLAGS`/`CXXFLAGS`, so inject the
# define there.
env:
CFLAGS: "-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE -DBORINGSSL_UNSAFE_FUZZER_MODE"
CXXFLAGS: "-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE -DBORINGSSL_UNSAFE_FUZZER_MODE"
CFLAGS: "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
CXXFLAGS: "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
steps:
- name: Checkout sources
uses: actions/checkout@v4
Expand Down
86 changes: 76 additions & 10 deletions .github/workflows/stable.yml
Comment thread
cjpatton marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ permissions:
pull-requests: write

env:
DEFAULT_OPTIONS: "--features=async,ffi,qlog --workspace"
# `rpk` is included so the boring 4 vs 5 RPK arms in
# `tokio-quiche/src/settings/config.rs` are exercised by CI;
# without it neither arm is compiled and a regression on either
# version would slip through.
DEFAULT_OPTIONS: "--features=async,ffi,qlog,rpk --workspace"
# Used by `quiche_multiarch`, which can't link `boring` for foreign targets.
NO_BORING_OPTIONS: "--features=ffi,qlog --workspace --exclude h3i --exclude tokio-quiche"
RUSTFLAGS: "-D warnings"
Expand All @@ -20,6 +24,10 @@ concurrency:
jobs:
quiche:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
boring_version: ["latest", "4"]
# Only run on "pull_request" event for external PRs. This is to avoid
# duplicate builds for PRs created from internal branches.
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
Expand All @@ -38,6 +46,17 @@ jobs:
sudo apt-get update
sudo apt-get install libexpat1-dev libfreetype6-dev libfontconfig1-dev

- name: Maybe pin boring to latest 4.x
if: matrix.boring_version == '4'
run: |
# Pin boring 4. cargo's resolver normally picks the highest match
# (5). Generate the lockfile first, then downgrade to a known-good
# 4.x via `--precise`. `quiche/src/build.rs` and
# `tokio-quiche/build.rs` then read the lockfile and flip
# `cfg(boring_v4)` on.
cargo generate-lockfile
cargo update -p boring --precise 4.22.0

- name: Unused dependency check
uses: bnjbvr/cargo-machete@main

Expand Down Expand Up @@ -130,11 +149,21 @@ jobs:
matrix:
target: ["x86_64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-pc-windows-gnu", "i686-pc-windows-gnu"]
include:
# i686 (32-bit) BoringSSL builds require SSE2 explicitly. The
# toolchain file in BoringSSL sets `-msse2`, but cmake-rs
# passes `-DCMAKE_C_FLAGS=...` on the command line which
# overrides that, so we re-add it here.
- target: "i686-pc-windows-msvc"
cxxflags: "-msse2"
- target: "i686-pc-windows-gnu"
cflags: "-mlong-double-64" # bindgen detects that Rust doesn't support 80-bit long double
# `-mlong-double-64`: bindgen detects that Rust doesn't
# support 80-bit long double.
# `-msse2`: same reason as i686-pc-windows-msvc above.
cflags: "-mlong-double-64 -msse2"
cxxflags: "-mlong-double-64 -msse2"
env:
CFLAGS: ${{ matrix.cflags }}
CXXFLAGS: ${{ matrix.cflags }}
CXXFLAGS: ${{ matrix.cxxflags }}
BINDGEN_EXTRA_CLANG_ARGS: ${{ matrix.cflags }}
# Only run on "pull_request" event for external PRs. This is to avoid
# duplicate builds for PRs created from internal branches.
Expand All @@ -149,18 +178,55 @@ jobs:
toolchain: ${{ env.RUSTTOOLCHAIN }}
targets: ${{ matrix.target }}

# The windows-2022 image preinstalls MSYS2 (gcc 14.2.0) at
# `C:\msys64` but doesn't put it on PATH. For x86_64-pc-windows-gnu
# we rely on these preinstalled tools and just point the C/C++
# toolchain at them, matching `boring`'s own CI workflow.
- name: Set up MinGW for 64 bit
if: matrix.target == 'x86_64-pc-windows-gnu'
uses: bwoodsend/setup-winlibs-action@v1.10
with:
tag: 12.2.0-16.0.0-10.0.0-msvcrt-r5

shell: bash
run: |
echo >> "$GITHUB_ENV" CC=gcc
echo >> "$GITHUB_ENV" CXX=g++
echo >> "$GITHUB_ENV" 'C_INCLUDE_PATH=C:\msys64\usr\include'
echo >> "$GITHUB_ENV" 'CPLUS_INCLUDE_PATH=C:\msys64\usr\include'
echo >> "$GITHUB_ENV" 'LIBRARY_PATH=C:\msys64\usr\lib'

# For i686 we install a fresh 32-bit MSYS2 toolchain (and a
# 32-bit cmake from MSYS2, since the system cmake is 64-bit and
# picks up the wrong `gcc`). Mirrors `boring`'s CI workflow.
- name: Set up MinGW for 32 bit
if: matrix.target == 'i686-pc-windows-gnu'
uses: bwoodsend/setup-winlibs-action@v1.10
uses: msys2/setup-msys2@v2
id: msys2
with:
architecture: i686
tag: 12.2.0-16.0.0-10.0.0-msvcrt-r5
msystem: MINGW32
path-type: inherit
install: >-
mingw-w64-i686-gcc
mingw-w64-i686-cmake

- name: Set up 32-bit MSYS2 env vars
if: matrix.target == 'i686-pc-windows-gnu'
shell: bash
run: |
MSYS_ROOT='${{ steps.msys2.outputs.msys2-location }}'
test -d "$MSYS_ROOT\\mingw32\\bin"
echo >> $GITHUB_PATH "$MSYS_ROOT\\mingw32\\bin"
echo >> $GITHUB_PATH "$MSYS_ROOT\\usr\\bin"
echo >> $GITHUB_ENV CC="$MSYS_ROOT\\mingw32\\bin\\gcc"
echo >> $GITHUB_ENV CXX="$MSYS_ROOT\\mingw32\\bin\\g++"
echo >> $GITHUB_ENV AR="$MSYS_ROOT\\mingw32\\bin\\ar"
echo >> $GITHUB_ENV CFLAGS="$CFLAGS -I$MSYS_ROOT\\mingw32\\include"
echo >> $GITHUB_ENV CXXFLAGS="$CXXFLAGS -I$MSYS_ROOT\\mingw32\\include"
echo >> $GITHUB_ENV BINDGEN_EXTRA_CLANG_ARGS="$BINDGEN_EXTRA_CLANG_ARGS -I$MSYS_ROOT\\mingw32\\include"
echo >> $GITHUB_ENV LIBRARY_PATH="$MSYS_ROOT\\mingw32\\lib"
echo >> $GITHUB_ENV LDFLAGS="-L$MSYS_ROOT\\mingw32\\lib"
# Force cmake-rs to use the MinGW Makefiles generator instead
# of MSYS Makefiles; cmake-rs's default for windows-gnu picks
# the latter, which then can't find `cl.exe` (cmake's default
# Windows compiler) and fails configuration.
echo >> $GITHUB_ENV "CMAKE_GENERATOR=MinGW Makefiles"

- name: Install dependencies
uses: crazy-max/ghaction-chocolatey@v3
Expand Down
16 changes: 15 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ categories = ["network-programming"]
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(capture_keylogs)',
'cfg(test_invalid_len_compilation_fail)',
'cfg(boring_v4)',
] }

[workspace.metadata.release]
Expand All @@ -40,7 +41,20 @@ publish = false
[workspace.dependencies]
anyhow = { version = "1" }
assert_matches = { version = "1" }
boring = { version = "4.3" }
# Accept both `boring` 4.x (>= 4.21, where the APIs we use stabilised)
# and `boring` 5.x. The active version is picked by cargo's resolver
# based on the lockfile / downstream constraints; quiche's `build.rs`
# detects which one was selected and emits `cfg(boring_v4)` for the
# legacy 4.x case so source code can switch on it. The `boring_v4`
# cfg is meant to be a transitional flag; new code should live in the
# `not(boring_v4)` arm so the cfg can be retired by simply deleting
# the legacy arms once internal users have moved off 4.x. See
# `quiche/src/build.rs` and the `boringssl-boring-crate` feature in
# `quiche/Cargo.toml`.
#
# `boring-sys` uses `links = "boringssl"`, so only one major version
# can ever be in the dep graph at a time.
boring = { version = ">=4.21, <6" }
buffer-pool = { version = "0.2.1", path = "./buffer-pool" }
bytes = { version = "1.11.1" }
crossbeam = { version = "0.8.1", default-features = false }
Expand Down
12 changes: 12 additions & 0 deletions Cross.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,15 @@ pre-build = [
"ln -sf /usr/bin/gcc /usr/local/bin/i686-linux-gnu-gcc",
"ln -sf /usr/bin/g++ /usr/local/bin/i686-linux-gnu-g++",
]

# BoringSSL's x86 assembly requires SSE2. `boring-sys`'s cmake build
# doesn't add `-msse2` for i686 targets (its 32-bit-toolchain.cmake
# sets it as a CACHE STRING, but cmake-rs's `-DCMAKE_C_FLAGS=...` on
# the command line wins), so inject it via the target-scoped
# `CFLAGS`/`CXXFLAGS` env vars. `cc-rs` (and through it, `cmake-rs`)
# honors these and passes them along to BoringSSL's cmake build.
[target.i686-unknown-linux-gnu.env]
passthrough = [
"CFLAGS_i686_unknown_linux_gnu=-msse2 -mfpmath=sse",
"CXXFLAGS_i686_unknown_linux_gnu=-msse2 -mfpmath=sse",
]
16 changes: 16 additions & 0 deletions quiche/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ workspace = true
default = ["boringssl-boring-crate"]

# Use the BoringSSL library provided by the boring crate.
#
# Both `boring` 4.x (>= 4.21) and 5.x are supported. Cargo's resolver
# picks the version based on the lockfile and downstream constraints;
# `build.rs` detects which major version was selected at compile time
# and emits a `cfg(boring_v4)` flag for the legacy 4.x case. The two
# majors differ in their default TLS curve list (5.x advertises
# post-quantum key shares) and in their Rust API for raw public
# keys; see `tokio-quiche/src/settings/config.rs` for the per-version
# code. `boring_v4` is a transitional cfg meant to be retired once
# internal users have moved off 4.x.
#
# Downstream code that depends on `boring` directly (e.g. to construct
# an `SslContextBuilder` for `Config::with_boring_ssl_ctx_builder`)
# will resolve to the same `boring` version as quiche, since
# `boring-sys` uses `links = "boringssl"` and cargo allows only one
# copy in the dep graph.
boringssl-boring-crate = ["boring", "foreign-types-shared"]

# Allow client connections to provide a custom DCID when initiating a
Expand Down
8 changes: 4 additions & 4 deletions quiche/examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ LIBS = $(LIB_DIR)/libquiche.a -lev -ldl -pthread -lm
all: client server http3-client http3-server

client: client.c $(INCLUDE_DIR)/quiche.h $(LIB_DIR)/libquiche.a
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ $(INCS) $(LIBS)
$(CXX) $(CFLAGS) $(LDFLAGS) -x c $< -x none -o $@ $(INCS) $(LIBS)

server: server.c $(INCLUDE_DIR)/quiche.h $(LIB_DIR)/libquiche.a
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ $(INCS) $(LIBS)
$(CXX) $(CFLAGS) $(LDFLAGS) -x c $< -x none -o $@ $(INCS) $(LIBS)

http3-client: http3-client.c $(INCLUDE_DIR)/quiche.h $(LIB_DIR)/libquiche.a
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ $(INCS) $(LIBS)
$(CXX) $(CFLAGS) $(LDFLAGS) -x c $< -x none -o $@ $(INCS) $(LIBS)

http3-server: http3-server.c $(INCLUDE_DIR)/quiche.h $(LIB_DIR)/libquiche.a
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ $(INCS) $(LIBS)
$(CXX) $(CFLAGS) $(LDFLAGS) -x c $< -x none -o $@ $(INCS) $(LIBS)

$(LIB_DIR)/libquiche.a: $(shell find $(SOURCE_DIR) -type f -name '*.rs')
cd .. && cargo build --target-dir $(BUILD_DIR) --features ffi
Expand Down
3 changes: 3 additions & 0 deletions quiche/include/quiche.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ int quiche_config_load_verify_locations_from_file(quiche_config *config,
int quiche_config_load_verify_locations_from_directory(quiche_config *config,
const char *path);

// Configures the TLS curve preference list (colon-separated, e.g. "X25519MLKEM768:X25519:P-256:P-384").
int quiche_config_set_curves_list(quiche_config *config, const char *curves);

// Configures whether to verify the peer's certificate.
void quiche_config_verify_peer(quiche_config *config, bool v);

Expand Down
88 changes: 88 additions & 0 deletions quiche/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,83 @@ Cflags: -I${{includedir}}
out_file.write_all(output.as_bytes()).unwrap();
}

/// Returns true if cargo resolved `boring` to a 4.x version.
///
/// Walks up from `OUT_DIR` looking for a `Cargo.lock`, then scans it
/// for the `boring` package. We use `Cargo.lock` rather than shelling
/// out to `cargo metadata` because (a) the lockfile is guaranteed to
/// exist at this point in the build, (b) parsing it is cheap and has
/// no extra dependencies, and (c) it avoids re-entering cargo from a
/// build script.
fn detect_boring_v4() -> bool {
let Some(lockfile) = find_cargo_lock() else {
// No lockfile (shouldn't happen in normal cargo builds, but
// be conservative). Assume 5.x — the default and forward-
// looking version. Downstream can fix this by generating a
// lockfile (`cargo generate-lockfile`).
println!(
"cargo:warning=quiche: Cargo.lock not found; assuming boring 5.x"
);
return false;
};

println!("cargo:rerun-if-changed={}", lockfile.display());

let contents = match std::fs::read_to_string(&lockfile) {
Ok(s) => s,
Err(e) => {
println!(
"cargo:warning=quiche: failed to read {}: {e}; assuming boring 5.x",
lockfile.display(),
);
return false;
},
};

// The lockfile is TOML but a regex-light scan is enough: find a
// `[[package]]` whose `name = "boring"` (not "boring-sys") and
// read its `version`.
let mut in_boring = false;
for line in contents.lines() {
let line = line.trim();
if line == "[[package]]" {
in_boring = false;
continue;
}
if line == "name = \"boring\"" {
in_boring = true;
continue;
}
if in_boring {
if let Some(rest) = line.strip_prefix("version = \"") {
let version = rest.trim_end_matches('"');
let major = version.split('.').next().unwrap_or("");
return major == "4";
}
}
}

// `boring` not present in the lockfile (e.g.
// `boringssl-boring-crate` is off). Doesn't matter what we return
// since the `cfg` won't be observed.
false
}

fn find_cargo_lock() -> Option<std::path::PathBuf> {
// Start from `CARGO_MANIFEST_DIR` and walk up. Cargo guarantees
// the lockfile lives at the workspace root, which is an ancestor
// of the manifest dir.
let manifest_dir =
std::path::PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR")?);
for dir in manifest_dir.ancestors() {
let candidate = dir.join("Cargo.lock");
if candidate.is_file() {
return Some(candidate);
}
}
None
}

fn target_dir_path() -> std::path::PathBuf {
let out_dir = std::env::var("OUT_DIR").unwrap();
let out_dir = std::path::Path::new(&out_dir);
Expand All @@ -44,7 +121,18 @@ fn target_dir_path() -> std::path::PathBuf {
}

fn main() {
// Emit `cfg(boring_v4)` if boring version 4.x is detected. This is used to
// pick which APIs to expect and to guide test expectations. (Larger post
// quantum key shares are enabled by default in boring 5.x but not boring
// 4.x.)
//
// The cfg is always registered (even when the backend feature is
// off) so rustc doesn't warn about unknown cfg names.
println!("cargo::rustc-check-cfg=cfg(boring_v4)");
if cfg!(feature = "boringssl-boring-crate") {
if detect_boring_v4() {
println!("cargo:rustc-cfg=boring_v4");
}
println!("cargo:rustc-link-lib=static=ssl");
println!("cargo:rustc-link-lib=static=crypto");
}
Expand Down
13 changes: 13 additions & 0 deletions quiche/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,19 @@ pub extern "C" fn quiche_config_load_verify_locations_from_directory(
}
}

#[no_mangle]
pub extern "C" fn quiche_config_set_curves_list(
config: &mut Config, curves: *const c_char,
) -> c_int {
let curves = unsafe { ffi::CStr::from_ptr(curves).to_str().unwrap() };

match config.set_curves_list(curves) {
Ok(_) => 0,

Err(e) => e.to_c() as c_int,
}
}

#[no_mangle]
pub extern "C" fn quiche_config_verify_peer(config: &mut Config, v: bool) {
config.verify_peer(v);
Expand Down
5 changes: 4 additions & 1 deletion quiche/src/h3/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3651,7 +3651,10 @@ mod tests {
fn h3_handshake_0rtt() {
let mut buf = [0; 65535];

let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
// Test drives the handshake one Initial at a time; requires a
// single-Initial ClientHello.
let mut config =
crate::test_utils::config_no_pq(crate::PROTOCOL_VERSION).unwrap();
config
.load_cert_chain_from_pem_file("examples/cert.crt")
.unwrap();
Expand Down
Loading
Loading