diff --git a/.CodeQL.yml b/.CodeQL.yml index 1455700093..1ccd4b8022 100644 --- a/.CodeQL.yml +++ b/.CodeQL.yml @@ -2,13 +2,14 @@ # https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/codeql/troubleshooting/bugs/generated-library-code # (Access restricted to Microsoft employees only.) -# The following paths are explicitly classified which indicates they should no +# The following paths are explicitly classified which indicates they should not # be analyzed by CodeQL and generate alerts. path_classifiers: docs: - docs generated: + - build - src/cs - src/generated submodules: - - subodules + - submodules diff --git a/.github/workflows/build-darwin-framework.yml b/.github/workflows/build-darwin-framework.yml index 186ed64055..be92ab1f68 100644 --- a/.github/workflows/build-darwin-framework.yml +++ b/.github/workflows/build-darwin-framework.yml @@ -25,3 +25,4 @@ jobs: uses: ./.github/workflows/build-reuse-darwin-framework.yml with: config: 'Release' + repo: ${{ github.repository }} diff --git a/.github/workflows/build-reuse-darwin-framework.yml b/.github/workflows/build-reuse-darwin-framework.yml index 9aa7baba8e..0da51e5c93 100644 --- a/.github/workflows/build-reuse-darwin-framework.yml +++ b/.github/workflows/build-reuse-darwin-framework.yml @@ -7,6 +7,10 @@ on: required: false default: '' type: string + repo: + required: false + default: microsoft/msquic + type: string config: required: false default: 'Release' @@ -46,6 +50,7 @@ jobs: arch: ${{ matrix.arch }} tls: ${{ inputs.tls }} static: ${{ inputs.static }} + repo: ${{ inputs.repo }} build-darwin-universal: name: Build Universal Binaries @@ -55,7 +60,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Download Build Artifacts (x64) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 @@ -92,7 +97,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Download Build Artifacts (x64) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 @@ -116,7 +121,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Download Build Artifacts (iOS x64) uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 diff --git a/.github/workflows/build-reuse-unix.yml b/.github/workflows/build-reuse-unix.yml index 6bfadc0e56..518ba82bf3 100644 --- a/.github/workflows/build-reuse-unix.yml +++ b/.github/workflows/build-reuse-unix.yml @@ -9,6 +9,10 @@ on: required: false default: '' type: string + repo: + required: false + default: microsoft/msquic + type: string config: required: false default: 'Release' @@ -91,7 +95,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Set ownership if: inputs.plat == 'linux' diff --git a/.github/workflows/build-reuse-win.yml b/.github/workflows/build-reuse-win.yml index e13e5ef9f2..41f614cbbd 100644 --- a/.github/workflows/build-reuse-win.yml +++ b/.github/workflows/build-reuse-win.yml @@ -9,6 +9,10 @@ on: required: false default: '' type: string + repo: + required: false + default: microsoft/msquic + type: string config: required: false default: 'Release' @@ -71,7 +75,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Install Perl uses: shogo82148/actions-setup-perl@98dfedee230bcf1ee68d5b021931fc8d63f2016e diff --git a/.github/workflows/build-reuse-winkernel.yml b/.github/workflows/build-reuse-winkernel.yml index 0e55cba4ef..b6212bc4a2 100644 --- a/.github/workflows/build-reuse-winkernel.yml +++ b/.github/workflows/build-reuse-winkernel.yml @@ -9,6 +9,10 @@ on: required: false default: '' type: string + repo: + required: false + default: microsoft/msquic + type: string config: required: false default: 'Release' @@ -60,7 +64,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - repository: microsoft/msquic + repository: ${{ inputs.repo}} ref: ${{ inputs.ref }} - name: Prepare Machine shell: pwsh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aead751834..8d1ba58ae2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,6 +53,7 @@ jobs: arch: ${{ matrix.arch }} tls: ${{ matrix.tls }} static: ${{ matrix.static }} + repo: ${{ github.repository }} build-windows-kernel: name: WinKernel @@ -72,6 +73,7 @@ jobs: os: ${{ matrix.os }} arch: ${{ matrix.arch }} tls: ${{ matrix.tls }} + repo: ${{ github.repository }} build-ubuntu-cross-compile: name: UbuntuArm @@ -93,6 +95,7 @@ jobs: arch: ${{ matrix.arch }} tls: ${{ matrix.tls }} static: ${{ matrix.static }} + repo: ${{ github.repository }} build-ubuntu: name: Ubuntu @@ -177,6 +180,7 @@ jobs: clang: ${{ matrix.clang }} codecheck: ${{ matrix.codecheck }} xdp: ${{ matrix.xdp }} + repo: ${{ github.repository }} build-darwin: name: MacOs @@ -198,6 +202,7 @@ jobs: arch: ${{ matrix.arch }} tls: ${{ matrix.tls }} static: ${{ matrix.static }} + repo: ${{ github.repository }} build-nuget: name: Build Nuget Package diff --git a/.github/workflows/cargo.yml b/.github/workflows/cargo.yml index bf8b20c4e5..689676fdbe 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -17,6 +17,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-xlarge] + features: ["", "--features static"] runs-on: ${{ matrix.os }} name: Cargo steps: @@ -40,9 +41,13 @@ jobs: - name: Install Cargo if: runner.os == 'Linux' run: curl https://sh.rustup.rs -sSf | sh -s -- -y + - name: Cargo fmt + run: cargo fmt --all -- --check + - name: Cargo clippy + run: cargo clippy --all-targets -- -D warnings - name: Cargo build - run: cargo build --all + run: cargo build --all ${{ matrix.features }} - name: Cargo test - run: cargo test --all + run: cargo test --all ${{ matrix.features }} - name: Cargo Publish (dry run) run: cargo publish --dry-run --allow-dirty diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 8e9fbe0844..a5f8d18e5a 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -37,6 +37,7 @@ jobs: tls: ${{ matrix.vec.tls }} sanitize: ${{ matrix.vec.sanitize }} build: ${{ matrix.vec.build }} + repo: ${{ github.repository }} bvt-winlatest: name: BVT WinPrerelease diff --git a/.github/workflows/dotnet-test.yml b/.github/workflows/dotnet-test.yml index ccfe8f3829..0737d43e34 100644 --- a/.github/workflows/dotnet-test.yml +++ b/.github/workflows/dotnet-test.yml @@ -25,6 +25,7 @@ jobs: uses: ./.github/workflows/build-reuse-darwin-framework.yml with: config: 'Debug' + repo: ${{ github.repository }} build-linux: name: Ubuntu @@ -45,6 +46,7 @@ jobs: arch: ${{ matrix.vec.arch }} tls: ${{ matrix.vec.tls }} xdp: ${{ matrix.vec.xdp }} + repo: ${{ github.repository }} build-windows: name: Windows @@ -63,6 +65,7 @@ jobs: os: ${{ matrix.vec.os }} arch: ${{ matrix.vec.arch }} tls: ${{ matrix.vec.tls }} + repo: ${{ github.repository }} dotnet-test: name: DotNet Test diff --git a/.github/workflows/package-reuse-linux.yml b/.github/workflows/package-reuse-linux.yml index 354a940a1f..893f621824 100644 --- a/.github/workflows/package-reuse-linux.yml +++ b/.github/workflows/package-reuse-linux.yml @@ -75,6 +75,7 @@ jobs: clang: ${{ inputs.clang }} build: ${{ inputs.build }} xdp: ${{ inputs.xdp }} + repo: ${{ github.repository }} package: name: Package diff --git a/.github/workflows/stress.yml b/.github/workflows/stress.yml index 8e1d22f828..3e850df271 100644 --- a/.github/workflows/stress.yml +++ b/.github/workflows/stress.yml @@ -40,6 +40,7 @@ jobs: tls: ${{ matrix.vec.tls }} sanitize: ${{ matrix.vec.sanitize }} build: ${{ matrix.vec.build }} + repo: ${{ github.repository }} build-unix: name: Build Unix @@ -66,6 +67,7 @@ jobs: sanitize: ${{ matrix.vec.sanitize }} build: ${{ matrix.vec.build }} xdp: ${{ matrix.vec.xdp }} + repo: ${{ github.repository }} stress: name: Stress diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8f363ddab1..df74a07281 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,6 +43,7 @@ jobs: tls: ${{ matrix.vec.tls }} build: ${{ matrix.vec.build }} ref: ${{ inputs.ref || '' }} + repo: ${{ github.repository }} build-windows: name: Build WinUser @@ -66,6 +67,7 @@ jobs: sanitize: ${{ matrix.vec.sanitize }} build: ${{ matrix.vec.build }} ref: ${{ inputs.ref || '' }} + repo: ${{ github.repository }} build-unix: name: Build Unix @@ -94,6 +96,7 @@ jobs: build: ${{ matrix.vec.build }} xdp: ${{ matrix.vec.xdp }} ref: ${{ inputs.ref || '' }} + repo: ${{ github.repository }} bvt: name: BVT diff --git a/.github/workflows/wan-perf.yml b/.github/workflows/wan-perf.yml index 01872cfe64..d51e3b602b 100644 --- a/.github/workflows/wan-perf.yml +++ b/.github/workflows/wan-perf.yml @@ -1,24 +1,24 @@ name: WAN Perf on: - push: - branches: - - main - paths: - - .github/workflows/wan-perf.yml - - src/core/* - - src/platform/* - - src/perf/* - pull_request: - branches: - - main - paths: - - .github/workflows/wan-perf.yml - - src/core/* - - src/platform/* - - src/perf/* - - submodules/openssl/* - + workflow_dispatch: +# push: +# branches: +# - main +# paths: +# - .github/workflows/wan-perf.yml +# - src/core/* +# - src/platform/* +# - src/perf/* +# pull_request: +# branches: +# - main +# paths: +# - .github/workflows/wan-perf.yml +# - src/core/* +# - src/platform/* +# - src/perf/* +# - submodules/openssl/* concurrency: # Cancel any workflow currently in progress for the same PR. # Allow running concurrently with any other commits. @@ -92,11 +92,11 @@ jobs: name: bin path: artifacts/bin - name: Run WAN Perf (QUIC only) - if: ${{ github.event_name == 'pull_request' }} + if: ${{ github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' }} shell: pwsh run: scripts/emulated-performance.ps1 -Debug -Protocol QUIC -LogProfile Performance.Light -NoDateLogDir -NumIterations ${{ env.iterations }} -DurationMs ${{ env.duration }} -Pacing ${{ env.pacing }} -BottleneckMbps ${{ matrix.rate }} -RttMs ${{ matrix.rtt }} -BottleneckQueueRatio ${{ matrix.queueRatio }} -RandomLossDenominator ${{ env.loss }} -RandomReorderDenominator ${{ env.reorder }} -ReorderDelayDeltaMs ${{ env.delay }} -BaseRandomSeed ${{ env.seed }} -CongestionControl ${{ env.congestionControl }} - name: Run WAN Perf (QUIC + TCP) - if: ${{ github.event_name != 'pull_request' }} + if: ${{ github.event_name != 'pull_request' && github.event_name != 'workflow_dispatch' }} shell: pwsh run: scripts/emulated-performance.ps1 -Debug -Protocol ('QUIC','TCPTLS') -LogProfile Performance.Light -NoDateLogDir -NumIterations ${{ env.iterations }} -DurationMs ${{ env.duration }} -Pacing ${{ env.pacing }} -BottleneckMbps ${{ matrix.rate }} -RttMs ${{ matrix.rtt }} -BottleneckQueueRatio ${{ matrix.queueRatio }} -RandomLossDenominator ${{ env.loss }} -RandomReorderDenominator ${{ env.reorder }} -ReorderDelayDeltaMs ${{ env.delay }} -BaseRandomSeed ${{ env.seed }} -CongestionControl ${{ env.congestionControl }} - name: Upload Results diff --git a/CMakeLists.txt b/CMakeLists.txt index 718247224d..a32b057c25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,9 +67,6 @@ elseif (UNIX) endif() message(STATUS "QUIC Platform: ${CX_PLATFORM}") -set(FILENAME_DEP_REPLACE "get_filename_component(SELF_DIR \"$\{CMAKE_CURRENT_LIST_FILE\}\" PATH)") -set(SELF_DIR "$\{SELF_DIR\}") - enable_testing() # Set the default TLS method for each platform. diff --git a/Cargo.toml b/Cargo.toml index 9188ecd5be..ad41247b71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,11 @@ include = [ "/THIRD-PARTY-NOTICES", ] +[features] +default = [] +static = [] +preview-api = [] + [build-dependencies] cmake = "0.1" @@ -52,3 +57,5 @@ bitfield = "0.17.0" libc = "0.2.0" c-types = "4.0.0" serde = { version = "1.0.117", features = ["derive"] } +ctor = "0.2.9" +socket2 = "0.5.8" diff --git a/scripts/build.rs b/scripts/build.rs index 2881cb0001..5e3d8ba4f5 100644 --- a/scripts/build.rs +++ b/scripts/build.rs @@ -2,8 +2,8 @@ // Licensed under the MIT License. use cmake::Config; -use std::path::Path; use std::env; +use std::path::Path; fn main() { let path_extra = "lib"; @@ -13,13 +13,19 @@ fn main() { } let target = env::var("TARGET").unwrap(); + let out_dir = env::var("OUT_DIR").unwrap(); + // The output directory for the native MsQuic library. + let quic_output_dir = Path::new(&out_dir).join("lib"); // Builds the native MsQuic and installs it into $OUT_DIR. let mut config = Config::new("."); config .define("QUIC_ENABLE_LOGGING", logging_enabled) .define("QUIC_TLS", "openssl") - .define("QUIC_OUTPUT_DIR", "../lib"); + .define("QUIC_OUTPUT_DIR", quic_output_dir.to_str().unwrap()); + if cfg!(feature = "static") { + config.define("QUIC_BUILD_SHARED", "off"); + } // macos-latest's cargo automatically specify --target=${ARCH}-apple-macosx14.5 // which conflicts with -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}. @@ -32,10 +38,25 @@ fn main() { "aarch64-apple-darwin" => config .define("CMAKE_OSX_ARCHITECTURES", "arm64") .define("CMAKE_OSX_DEPLOYMENT_TARGET", "14.5"), - _ => &mut config + _ => &mut config, }; let dst = config.build(); let lib_path = Path::join(Path::new(&dst), Path::new(path_extra)); println!("cargo:rustc-link-search=native={}", lib_path.display()); + if cfg!(feature = "static") { + if cfg!(target_os = "linux") { + let numa_lib_path = match target.as_str() { + "x86_64-unknown-linux-gnu" => "/usr/lib/x86_64-linux-gnu", + "aarch64-unknown-linux-gnu" => "/usr/lib/aarch64-linux-gnu", + _ => panic!("Unsupported target: {}", target), + }; + println!("cargo:rustc-link-search=native={}", numa_lib_path); + println!("cargo:rustc-link-lib=static:+whole-archive=numa"); + } else if cfg!(target_os = "macos") { + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=Security"); + } + println!("cargo:rustc-link-lib=static=msquic"); + } } diff --git a/src/bin/CMakeLists.txt b/src/bin/CMakeLists.txt index 6b84d4555c..e69cf51ac5 100644 --- a/src/bin/CMakeLists.txt +++ b/src/bin/CMakeLists.txt @@ -11,6 +11,7 @@ endif() if(BUILD_SHARED_LIBS) add_library(msquic SHARED ${SOURCES}) + target_include_directories(msquic PUBLIC $) target_link_libraries(msquic PRIVATE core msquic_platform inc warnings logging base_link main_binary_link_args) set_target_properties(msquic PROPERTIES OUTPUT_NAME ${QUIC_LIBRARY_NAME}) if (NOT WIN32) @@ -269,7 +270,7 @@ else() endif() install(FILES ${PUBLIC_HEADERS} DESTINATION include) -configure_file(msquic-config.cmake.in ${CMAKE_BINARY_DIR}/msquic-config.cmake) +configure_file(msquic-config.cmake.in ${CMAKE_BINARY_DIR}/msquic-config.cmake @ONLY) install(FILES ${CMAKE_BINARY_DIR}/msquic-config.cmake DESTINATION share/msquic) diff --git a/src/bin/msquic-config-unix.cmake.in b/src/bin/msquic-config-unix.cmake.in deleted file mode 100644 index 9292018b12..0000000000 --- a/src/bin/msquic-config-unix.cmake.in +++ /dev/null @@ -1,5 +0,0 @@ -include(CMakeFindDependencyMacro) -@FILENAME_DEP_REPLACE@ - -include(${SELF_DIR}/msquic.cmake) -include(${SELF_DIR}/msquictraceprovider.cmake) diff --git a/src/bin/msquic-config.cmake.in b/src/bin/msquic-config.cmake.in index fba676be99..e61e63feab 100644 --- a/src/bin/msquic-config.cmake.in +++ b/src/bin/msquic-config.cmake.in @@ -1,7 +1,6 @@ include(CMakeFindDependencyMacro) -@FILENAME_DEP_REPLACE@ -include(${SELF_DIR}/msquic.cmake) +include("${CMAKE_CURRENT_LIST_DIR}/msquic.cmake") foreach(_t IN ITEMS msquic msquic_platform) if(TARGET msquic::${_t} AND NOT TARGET ${_t}) diff --git a/src/lib.rs b/src/lib.rs index 70ed8e3001..9a5e00b1d3 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,13 +7,20 @@ use c_types::AF_INET; use c_types::AF_INET6; #[allow(unused_imports)] use c_types::AF_UNSPEC; +use c_types::{sa_family_t, sockaddr_in, sockaddr_in6, socklen_t}; use libc::c_void; use serde::{Deserialize, Serialize}; +use socket2::SockAddr; use std::convert::TryInto; use std::fmt; +use std::io; +use std::marker::PhantomData; +use std::mem; +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::option::Option; use std::ptr; use std::result::Result; +use std::sync::Once; #[macro_use] extern crate bitfield; @@ -33,31 +40,13 @@ pub type BOOLEAN = ::std::os::raw::c_uchar; /// Family of an IP address. pub type AddressFamily = u16; +#[allow(clippy::unnecessary_cast)] pub const ADDRESS_FAMILY_UNSPEC: AddressFamily = c_types::AF_UNSPEC as u16; +#[allow(clippy::unnecessary_cast)] pub const ADDRESS_FAMILY_INET: AddressFamily = c_types::AF_INET as u16; +#[allow(clippy::unnecessary_cast)] pub const ADDRESS_FAMILY_INET6: AddressFamily = c_types::AF_INET6 as u16; -/// IPv4 address payload. -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct sockaddr_in { - pub family: AddressFamily, - pub port: u16, - pub addr: u32, - pub zero: [u8; 8usize], -} - -/// IPv6 address payload. -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct sockaddr_in6 { - pub family: AddressFamily, - pub port: u16, - pub flow_info: u32, - pub addr: [u8; 16usize], - pub scope_id: u32, -} - /// Generic representation of IPv4 or IPv6 addresses. #[repr(C)] #[derive(Copy, Clone)] @@ -67,40 +56,66 @@ pub union Addr { } impl Addr { - /// Create a representation of IPv4 address and perform Network byte order conversion - /// on the port number. - pub fn ipv4(family: u16, port: u16, addr: u32) -> Addr { - Addr { - ipv4: sockaddr_in { - family, - port: port, - addr, - zero: [0, 0, 0, 0, 0, 0, 0, 0], - }, + /// Converts the `Addr` to a `SocketAddr`. + pub fn as_socket(&self) -> Option { + unsafe { + SockAddr::try_init(|addr, len| { + if self.ipv4.sin_family == AF_INET as sa_family_t { + let addr = addr.cast::(); + *addr = self.ipv4; + *len = mem::size_of::() as socklen_t; + Ok(()) + } else if self.ipv4.sin_family == AF_INET6 as sa_family_t { + let addr = addr.cast::(); + *addr = self.ipv6; + *len = mem::size_of::() as socklen_t; + Ok(()) + } else { + Err(io::Error::from(io::ErrorKind::Other)) + } + }) } + .map(|((), addr)| addr.as_socket().unwrap()) + .ok() } - /// Create a representation of IPv6 address and perform Network byte order conversion - /// on the port number. - pub fn ipv6( - family: u16, - port: u16, - flow_info: u32, - addr: [u8; 16usize], - scope_id: u32, - ) -> Addr { - Addr { - ipv6: sockaddr_in6 { - family, - port: port, - flow_info, - addr, - scope_id, - }, + /// Get port number from the `Addr`. + pub fn port(&self) -> u16 { + unsafe { u16::from_be(self.ipv4.sin_port) } + } +} + +impl From for Addr { + fn from(addr: SocketAddr) -> Addr { + match addr { + SocketAddr::V4(addr) => addr.into(), + SocketAddr::V6(addr) => addr.into(), } } } +impl From for Addr { + fn from(addr: SocketAddrV4) -> Addr { + // SAFETY: a `Addr` of all zeros is valid. + let mut storage = unsafe { mem::zeroed::() }; + let addr: SockAddr = addr.into(); + let addr = addr.as_ptr().cast::(); + storage.ipv4 = unsafe { *addr }; + storage + } +} + +impl From for Addr { + fn from(addr: SocketAddrV6) -> Addr { + // SAFETY: a `Addr` of all zeros is valid. + let mut storage = unsafe { mem::zeroed::() }; + let addr: SockAddr = addr.into(); + let addr = addr.as_ptr().cast::(); + storage.ipv6 = unsafe { *addr }; + storage + } +} + #[cfg(target_os = "windows")] mod status { pub const QUIC_STATUS_SUCCESS: u32 = 0x0; @@ -128,7 +143,7 @@ mod status { pub const QUIC_STATUS_ALPN_NEG_FAILURE: u32 = 0x80410007; pub const QUIC_STATUS_STREAM_LIMIT_REACHED: u32 = 0x80410008; pub const QUIC_STATUS_ALPN_IN_USE: u32 = 0x80410009; - pub const QUIC_STATUS_CLOSE_NOTIFY: u32 = 0x80410100 | 0; + pub const QUIC_STATUS_CLOSE_NOTIFY: u32 = 0x80410100; pub const QUIC_STATUS_BAD_CERTIFICATE: u32 = 0x80410100 | 42; pub const QUIC_STATUS_UNSUPPORTED_CERTIFICATE: u32 = 0x80410100 | 43; pub const QUIC_STATUS_REVOKED_CERTIFICATE: u32 = 0x80410100 | 44; @@ -143,8 +158,8 @@ mod status { #[cfg(target_os = "linux")] mod status { pub const QUIC_STATUS_SUCCESS: u32 = 0; - pub const QUIC_STATUS_PENDING: u32 = 0xFFFFFFFE; /// -2 - pub const QUIC_STATUS_CONTINUE: u32 = 0xFFFFFFFF; /// -1 + pub const QUIC_STATUS_PENDING: u32 = 0xFFFFFFFE; // -2 + pub const QUIC_STATUS_CONTINUE: u32 = 0xFFFFFFFF; // -1 pub const QUIC_STATUS_OUT_OF_MEMORY: u32 = 12; pub const QUIC_STATUS_INVALID_PARAMETER: u32 = 22; pub const QUIC_STATUS_INVALID_STATE: u32 = 1; @@ -183,8 +198,8 @@ mod status { #[cfg(target_os = "macos")] mod status { pub const QUIC_STATUS_SUCCESS: u32 = 0; - pub const QUIC_STATUS_PENDING: u32 = 0xFFFFFFFE; /// -2 - pub const QUIC_STATUS_CONTINUE: u32 = 0xFFFFFFFF; /// -1 + pub const QUIC_STATUS_PENDING: u32 = 0xFFFFFFFE; // -2 + pub const QUIC_STATUS_CONTINUE: u32 = 0xFFFFFFFF; // -1 pub const QUIC_STATUS_OUT_OF_MEMORY: u32 = 12; pub const QUIC_STATUS_INVALID_PARAMETER: u32 = 22; pub const QUIC_STATUS_INVALID_STATE: u32 = 1; @@ -780,7 +795,7 @@ pub struct QuicTlsSecrets { } #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct Settings { pub is_set_flags: u64, pub max_bytes_per_key: u64, @@ -811,6 +826,7 @@ pub struct Settings { pub mtu_operations_per_drain: u8, pub mtu_discovery_missing_probe_count: u8, pub dest_cid_update_idle_timeout_ms: u32, + pub other2_flags: u64, } pub const PARAM_GLOBAL_RETRY_MEMORY_PERCENT: u32 = 0x01000000; @@ -895,19 +911,35 @@ pub const PARAM_STREAM_PRIORITY: u32 = 0x08000003; pub type ListenerEventType = u32; pub const LISTENER_EVENT_NEW_CONNECTION: ListenerEventType = 0; +pub const LISTENER_EVENT_STOP_COMPLETE: ListenerEventType = 1; #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ListenerEventNewConnection { pub info: *const NewConnectionInfo, pub connection: Handle, - pub new_negotiated_alpn: *const u8, +} + +bitfield! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + pub struct ListenerEventStopCompleteBitfields(u8); + // The fields default to u8 + pub app_close_in_progress, _: 0, 0; + _reserved, _: 7, 1; +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ListenerEventStopComplete { + pub bit_flags: ListenerEventStopCompleteBitfields, } #[repr(C)] #[derive(Copy, Clone)] pub union ListenerEventPayload { pub new_connection: ListenerEventNewConnection, + pub stop_complete: ListenerEventStopComplete, } #[repr(C)] @@ -959,10 +991,33 @@ pub struct ConnectionEventConnectionShutdownByPeer { pub error_code: u62, } +bitfield! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + pub struct ConnectionEventShutdownCompleteBitfields(BOOLEAN); + // The fields default to BOOLEAN + pub handshake_completed, _: 0, 0; + pub peer_acknowledged_shutdown, _: 1, 1; + pub app_close_in_progress, _: 2, 2; + _reserved, _: 7, 3; +} + #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct ConnectionEventShutdownComplete { - pub _bitfield: BOOLEAN, + pub bit_flags: ConnectionEventShutdownCompleteBitfields, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventLocalAddressChanged { + pub address: *const Addr, +} + +#[repr(C, packed)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventPeerAddressChanged { + pub address: *const Addr, } #[repr(C)] @@ -972,6 +1027,33 @@ pub struct ConnectionEventPeerStreamStarted { pub flags: StreamOpenFlags, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventStreamsAvailable { + pub bidirectional_count: u16, + pub unidirectional_count: u16, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventPeerNeedsStreams { + pub bidirectional: BOOLEAN, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventIdealProcessorChanged { + pub ideal_processor: u16, + pub partition_index: u16, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventDatagramStateChanged { + pub send_enabled: BOOLEAN, + pub max_send_length: u16, +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ConnectionEventDatagramReceived { @@ -986,6 +1068,13 @@ pub struct ConnectionEventDatagramSendStateChanged { pub state: DatagramSendState, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventResumed { + pub resumption_state_length: u16, + pub resumption_state: *const u8, +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ConnectionEventResumptionTicketReceived { @@ -993,6 +1082,15 @@ pub struct ConnectionEventResumptionTicketReceived { pub resumption_ticket: *const u8, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct ConnectionEventPeerCertificateReceived { + pub certificate: *const Certificate, + pub deferred_error_flags: u32, + pub deferred_status: u32, + pub chain: *const CertificateChain, +} + #[repr(C)] #[derive(Copy, Clone)] pub union ConnectionEventPayload { @@ -1000,17 +1098,18 @@ pub union ConnectionEventPayload { pub shutdown_initiated_by_transport: ConnectionEventConnectionShutdownByTransport, pub shutdown_initiated_by_peer: ConnectionEventConnectionShutdownByPeer, pub shutdown_complete: ConnectionEventShutdownComplete, - //pub local_address_changed: ConnectionEventLocalAddressChanged, - //pub peer_address_changed: ConnectionEventPeerAddressChanged, + pub local_address_changed: ConnectionEventLocalAddressChanged, + pub peer_address_changed: ConnectionEventPeerAddressChanged, pub peer_stream_started: ConnectionEventPeerStreamStarted, - //pub streams_available: ConnectionEventStreamsAvailable, - //pub ideal_processor_changed: ConnectionEventIdealProcessorChanged, - //pub datagram_state_changed: ConnectionEventDatagramStateChanged, + pub streams_available: ConnectionEventStreamsAvailable, + pub peer_needs_streams: ConnectionEventPeerNeedsStreams, + pub ideal_processor_changed: ConnectionEventIdealProcessorChanged, + pub datagram_state_changed: ConnectionEventDatagramStateChanged, pub datagram_received: ConnectionEventDatagramReceived, pub datagram_send_state_changed: ConnectionEventDatagramSendStateChanged, - //pub resumed: ConnectionEventResumed, + pub resumed: ConnectionEventResumed, pub resumption_ticket_received: ConnectionEventResumptionTicketReceived, - //pub peer_certificated_received: ConnectionEventPeerCertificateReceived, + pub peer_certificated_received: ConnectionEventPeerCertificateReceived, } #[repr(C)] @@ -1034,13 +1133,23 @@ pub const STREAM_EVENT_SEND_SHUTDOWN_COMPLETE: StreamEventType = 6; pub const STREAM_EVENT_SHUTDOWN_COMPLETE: StreamEventType = 7; pub const STREAM_EVENT_IDEAL_SEND_BUFFER_SIZE: StreamEventType = 8; pub const STREAM_EVENT_PEER_ACCEPTED: StreamEventType = 9; +pub const STREAM_EVENT_CANCEL_ON_LOSS: StreamEventType = 10; + +bitfield! { + #[repr(C)] + #[derive(Debug, Clone, Copy)] + pub struct StreamEventStartCompleteBitfields(u8); + // The fields default to u8 + pub peer_accepted, _: 0, 0; + _reserved, _: 7, 1; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct StreamEventStartComplete { - pub status: u64, + pub status: u32, pub id: u62, - pub bit_flags: u8, + pub bit_flags: StreamEventStartCompleteBitfields, } #[repr(C)] @@ -1063,13 +1172,13 @@ pub struct StreamEventSendComplete { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct StreamEventPeerSendAborted { - pub error_code: u64, + pub error_code: u62, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct StreamEventPeerReceiveAborted { - pub error_code: u64, + pub error_code: u62, } #[repr(C)] @@ -1081,17 +1190,21 @@ pub struct StreamEventSendShutdownComplete { bitfield! { #[repr(C)] #[derive(Clone, Copy)] - struct StreamEventShutdownCompleteBitfields(u8); + pub struct StreamEventShutdownCompleteBitfields(u8); // The fields default to u8 - app_close_in_progress, _: 1, 0; - _reserved, _: 7, 1; + pub app_close_in_progress, _: 0, 0; + pub conn_shutdown_by_app, _: 1, 1; + pub conn_closed_remotely, _: 2, 2; + _reserved, _: 7, 3; } #[repr(C)] #[derive(Copy, Clone)] pub struct StreamEventShutdownComplete { - connection_shutdown: bool, - flags: StreamEventShutdownCompleteBitfields, + pub connection_shutdown: bool, + pub bit_flags: StreamEventShutdownCompleteBitfields, + pub connection_error_code: u62, + pub connection_close_status: u32, } #[repr(C)] @@ -1100,6 +1213,12 @@ pub struct StreamEventIdealSendBufferSize { pub byte_count: u64, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct StreamEventCancelOnLoss { + pub error_code: u62, +} + #[repr(C)] #[derive(Copy, Clone)] pub union StreamEventPayload { @@ -1111,6 +1230,7 @@ pub union StreamEventPayload { pub send_shutdown_complete: StreamEventSendShutdownComplete, pub shutdown_complete: StreamEventShutdownComplete, pub ideal_send_buffer_size: StreamEventIdealSendBufferSize, + pub cancel_on_loss: StreamEventCancelOnLoss, } #[repr(C)] @@ -1216,15 +1336,10 @@ struct ApiTable { flags: SendFlags, client_send_context: *const c_void, ) -> u32, - resumption_ticket_validation_complete: extern "C" fn( - connection: Handle, - result: BOOLEAN, - ) -> u32, - certificate_validation_complete: extern "C" fn( - connection: Handle, - result: BOOLEAN, - tls_alert: TlsAlertCode - ) -> u32, + resumption_ticket_validation_complete: + extern "C" fn(connection: Handle, result: BOOLEAN) -> u32, + certificate_validation_complete: + extern "C" fn(connection: Handle, result: BOOLEAN, tls_alert: TlsAlertCode) -> u32, } #[link(name = "msquic")] @@ -1237,44 +1352,51 @@ extern "C" { // The following starts the "nice" Rust API wrapper on the C interop layer. // -/// Top level entry point for the MsQuic API. -/// -/// Developper must ensure a struct containing MsQuic members such as `Connection` -/// or `Stream` declares `API` last so that the API is dropped last when the containing -/// sruct goes out of scope. +// +// APITABLE will be initialized via MsQuicOpenVersion() when we first initialize Api or Registration. +// +static mut APITABLE: *const ApiTable = ptr::null(); +static START_MSQUIC: Once = Once::new(); + +/// Entry point for some global MsQuic APIs. pub struct Api { - table: *const ApiTable, + marker: PhantomData<()>, } /// The execution context for processing connections on the application's behalf. pub struct Registration { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Registration {} +unsafe impl Send for Registration {} /// Specifies how to configure a connection. pub struct Configuration { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Configuration {} +unsafe impl Send for Configuration {} /// A single QUIC connection. pub struct Connection { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Connection {} +unsafe impl Send for Connection {} /// A single server listener pub struct Listener { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Listener {} +unsafe impl Send for Listener {} /// A single QUIC stream on a parent connection. pub struct Stream { - table: *const ApiTable, handle: Handle, } +unsafe impl Sync for Stream {} +unsafe impl Send for Stream {} impl From<&str> for Buffer { fn from(data: &str) -> Buffer { @@ -1296,11 +1418,10 @@ impl From<&Vec> for Buffer { impl From<&[u8]> for Buffer { fn from(data: &[u8]) -> Buffer { - let buffer = Buffer { + Buffer { length: data.len() as u32, buffer: data.as_ptr() as *mut u8, - }; - buffer + } } } @@ -1321,38 +1442,8 @@ impl QuicPerformance { } impl Settings { - pub fn new() -> Settings { - Settings { - is_set_flags: 0, - max_bytes_per_key: 0, - handshake_idle_timeout_ms: 0, - idle_timeout_ms: 0, - mtu_discovery_search_complete_timeout_us: 0, - tls_client_max_send_buffer: 0, - tls_server_max_send_buffer: 0, - stream_recv_window_default: 0, - stream_recv_buffer_default: 0, - conn_flow_control_window: 0, - max_worker_queue_delay_us: 0, - max_stateless_operations: 0, - initial_window_packets: 0, - send_idle_timeout_ms: 0, - initiall_rtt_ms: 0, - max_ack_delay_ms: 0, - disconnect_timeout_ms: 0, - keep_alive_interval_ms: 0, - congestion_control_algorithm: 0, - peer_bidi_stream_count: 0, - peer_unidi_stream_count: 0, - max_binding_stateless_operations: 0, - stateless_operation_expiration_ms: 0, - minimum_mtu: 0, - maximum_mtu: 0, - other_flags: 0, - mtu_operations_per_drain: 0, - mtu_discovery_missing_probe_count: 0, - dest_cid_update_idle_timeout_ms: 0, - } + pub fn new() -> Self { + Self::default() } pub fn set_peer_bidi_stream_count(&mut self, value: u16) -> &mut Settings { self.is_set_flags |= 0x40000; @@ -1374,6 +1465,12 @@ impl Settings { self.other_flags |= (value as u8) << 3; self } + #[cfg(feature = "preview-api")] + pub fn set_stream_multi_receive_enabled(&mut self, value: bool) -> &mut Settings { + self.is_set_flags |= 1 << 42; + self.other2_flags |= (value as u64) << 5; + self + } } impl CredentialConfig { @@ -1392,29 +1489,43 @@ impl CredentialConfig { } } +impl Default for Api { + fn default() -> Self { + Self::new() + } +} + impl Api { - pub fn new() -> Result { - let new_table: *const ApiTable = ptr::null(); - let status = unsafe { MsQuicOpenVersion(2, &new_table) }; - if Status::failed(status) { - return Err(status); + pub fn new() -> Self { + // We initialize APITABLE at only once. + unsafe { + START_MSQUIC.call_once(|| { + let table: *const ApiTable = ptr::null(); + let status = MsQuicOpenVersion(2, &table); + if Status::failed(status) { + panic!("Failed to open MsQuic: {}", status); + } + APITABLE = table; + }); + } + Self { + marker: PhantomData, } - Ok(Api { table: new_table }) } pub fn close_listener(&self, listener: Handle) { unsafe { - ((*self.table).listener_close)(listener); + ((*APITABLE).listener_close)(listener); } } pub fn close_connection(&self, connection: Handle) { unsafe { - ((*self.table).connection_close)(connection); + ((*APITABLE).connection_close)(connection); } } pub fn close_stream(&self, stream: Handle) { unsafe { - ((*self.table).stream_close)(stream); + ((*APITABLE).stream_close)(stream); } } @@ -1424,7 +1535,7 @@ impl Api { }; let perf_length = std::mem::size_of::<[i64; PERF_COUNTER_MAX as usize]>() as u32; unsafe { - ((*self.table).get_param)( + ((*APITABLE).get_param)( std::ptr::null(), PARAM_GLOBAL_PERF_COUNTERS, (&perf_length) as *const u32 as *mut u32, @@ -1440,37 +1551,51 @@ impl Api { handler: *const c_void, context: *const c_void, ) { - unsafe { ((*self.table).set_callback_handler)(handle, handler, context) } + unsafe { ((*APITABLE).set_callback_handler)(handle, handler, context) } } } -impl Drop for Api { - fn drop(&mut self) { - unsafe { MsQuicClose(self.table) }; +#[ctor::dtor] +fn close_msquic() { + unsafe { + if !APITABLE.is_null() { + MsQuicClose(APITABLE); + APITABLE = ptr::null(); + } } } impl Registration { - pub fn new(api: &Api, config: *const RegistrationConfig) -> Result { + pub fn new(config: *const RegistrationConfig) -> Result { + // We initialize APITABLE at only once. + unsafe { + START_MSQUIC.call_once(|| { + let table: *const ApiTable = ptr::null(); + let status = MsQuicOpenVersion(2, &table); + if Status::failed(status) { + panic!("Failed to open MsQuic: {}", status); + } + APITABLE = table; + }); + } let new_registration: Handle = ptr::null(); - let status = unsafe { ((*api.table).registration_open)(config, &new_registration) }; + let status = unsafe { ((*APITABLE).registration_open)(config, &new_registration) }; if Status::failed(status) { return Err(status); } Ok(Registration { - table: api.table, handle: new_registration, }) } pub fn shutdown(&self) { - unsafe { ((*self.table).registration_shutdown)(self.handle) } + unsafe { ((*APITABLE).registration_shutdown)(self.handle) } } } impl Drop for Registration { fn drop(&mut self) { - unsafe { ((*self.table).registration_close)(self.handle) }; + unsafe { ((*APITABLE).registration_close)(self.handle) }; } } @@ -1483,11 +1608,11 @@ impl Configuration { let context: *const c_void = ptr::null(); let new_configuration: Handle = ptr::null(); let mut settings_size: u32 = 0; - if settings != ptr::null() { + if !settings.is_null() { settings_size = ::std::mem::size_of::() as u32; } let status = unsafe { - ((*registration.table).configuration_open)( + ((*APITABLE).configuration_open)( registration.handle, alpn.as_ptr(), alpn.len() as u32, @@ -1501,14 +1626,13 @@ impl Configuration { return Err(status); } Ok(Configuration { - table: registration.table, handle: new_configuration, }) } pub fn load_credential(&self, cred_config: &CredentialConfig) -> Result<(), u32> { let status = - unsafe { ((*self.table).configuration_load_credential)(self.handle, *&cred_config) }; + unsafe { ((*APITABLE).configuration_load_credential)(self.handle, cred_config) }; if Status::failed(status) { return Err(status); } @@ -1518,23 +1642,25 @@ impl Configuration { impl Drop for Configuration { fn drop(&mut self) { - unsafe { ((*self.table).configuration_close)(self.handle) }; + unsafe { ((*APITABLE).configuration_close)(self.handle) }; + } +} + +impl Default for Connection { + fn default() -> Self { + Self::new() } } impl Connection { - pub fn new(registration: &Registration) -> Connection { + pub fn new() -> Connection { Connection { - table: registration.table, handle: ptr::null(), } } - pub fn from_parts(handle: Handle, api: &Api) -> Connection { - Connection { - table: api.table, - handle, - } + pub fn from_parts(handle: Handle) -> Connection { + Connection { handle } } pub fn open( @@ -1544,7 +1670,7 @@ impl Connection { context: *const c_void, ) -> Result<(), u32> { let status = unsafe { - ((*self.table).connection_open)(registration.handle, handler, context, &self.handle) + ((*APITABLE).connection_open)(registration.handle, handler, context, &self.handle) }; if Status::failed(status) { return Err(status); @@ -1552,14 +1678,19 @@ impl Connection { Ok(()) } - pub fn start(&self, configuration: &Configuration, server_name: &str, server_port: u16) -> Result<(), u32> { + pub fn start( + &self, + configuration: &Configuration, + server_name: &str, + server_port: u16, + ) -> Result<(), u32> { let server_name_safe = std::ffi::CString::new(server_name).unwrap(); let status = unsafe { - ((*self.table).connection_start)( + ((*APITABLE).connection_start)( self.handle, configuration.handle, 0, - server_name_safe.as_ptr() as *const i8, + server_name_safe.as_ptr(), server_port, ) }; @@ -1571,23 +1702,23 @@ impl Connection { pub fn close(&self) { unsafe { - ((*self.table).connection_close)(self.handle); + ((*APITABLE).connection_close)(self.handle); } } pub fn shutdown(&self, flags: ConnectionShutdownFlags, error_code: u62) { unsafe { - ((*self.table).connection_shutdown)(self.handle, flags, error_code); + ((*APITABLE).connection_shutdown)(self.handle, flags, error_code); } } pub fn set_param(&self, param: u32, buffer_length: u32, buffer: *const c_void) -> u32 { - unsafe { ((*self.table).set_param)(self.handle, param, buffer_length, buffer) } + unsafe { ((*APITABLE).set_param)(self.handle, param, buffer_length, buffer) } } pub fn stream_close(&self, stream: Handle) { unsafe { - ((*self.table).stream_close)(stream); + ((*APITABLE).stream_close)(stream); } } @@ -1596,7 +1727,7 @@ impl Connection { [0; std::mem::size_of::()]; let stat_size_mut = std::mem::size_of::(); unsafe { - ((*self.table).get_param)( + ((*APITABLE).get_param)( self.handle, PARAM_CONN_STATISTICS, (&stat_size_mut) as *const usize as *const u32 as *mut u32, @@ -1612,7 +1743,7 @@ impl Connection { [0; std::mem::size_of::()]; let stat_size_mut = std::mem::size_of::(); unsafe { - ((*self.table).get_param)( + ((*APITABLE).get_param)( self.handle, PARAM_CONN_STATISTICS_V2, (&stat_size_mut) as *const usize as *const u32 as *mut u32, @@ -1625,7 +1756,7 @@ impl Connection { pub fn set_configuration(&self, configuration: &Configuration) -> Result<(), u32> { let status = unsafe { - ((*self.table).connection_set_configuration)(self.handle, configuration.handle) + ((*APITABLE).connection_set_configuration)(self.handle, configuration.handle) }; if Status::failed(status) { return Err(status); @@ -1635,7 +1766,7 @@ impl Connection { pub fn set_callback_handler(&self, handler: ConnectionEventHandler, context: *const c_void) { unsafe { - ((*self.table).set_callback_handler)(self.handle, handler as *const c_void, context) + ((*APITABLE).set_callback_handler)(self.handle, handler as *const c_void, context) }; } @@ -1646,7 +1777,7 @@ impl Connection { context: *const c_void, ) { unsafe { - ((*self.table).set_callback_handler)(stream_handle, handler as *const c_void, context) + ((*APITABLE).set_callback_handler)(stream_handle, handler as *const c_void, context) }; } @@ -1658,9 +1789,9 @@ impl Connection { client_send_context: *const c_void, ) -> Result<(), u32> { let status = unsafe { - ((*self.table).datagram_send)( + ((*APITABLE).datagram_send)( self.handle, - *&buffer, + buffer, buffer_count, flags, client_send_context, @@ -1672,79 +1803,107 @@ impl Connection { Ok(()) } - pub fn resumption_ticket_validation_complete( + pub fn resumption_ticket_validation_complete(&self, result: BOOLEAN) -> Result<(), u32> { + let status = + unsafe { ((*APITABLE).resumption_ticket_validation_complete)(self.handle, result) }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } + + pub fn certificate_validation_complete( &self, result: BOOLEAN, + tls_alert: TlsAlertCode, ) -> Result<(), u32> { let status = unsafe { - ((*self.table).resumption_ticket_validation_complete)( + ((*APITABLE).certificate_validation_complete)(self.handle, result, tls_alert) + }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } + + pub fn get_local_addr(&self) -> Result { + let mut addr_buffer: [u8; mem::size_of::()] = [0; mem::size_of::()]; + let addr_size_mut = mem::size_of::(); + let status = unsafe { + ((*APITABLE).get_param)( self.handle, - result, + PARAM_CONN_LOCAL_ADDRESS, + (&addr_size_mut) as *const usize as *const u32 as *mut u32, + addr_buffer.as_mut_ptr() as *const c_void, ) }; if Status::failed(status) { return Err(status); } - Ok(()) + Ok(unsafe { *(addr_buffer.as_ptr() as *const c_void as *const Addr) }) } - pub fn certificate_validation_complete( - &self, - result: BOOLEAN, - tls_alert: TlsAlertCode, - ) -> Result<(), u32> { + pub fn get_remote_addr(&self) -> Result { + let mut addr_buffer: [u8; mem::size_of::()] = [0; mem::size_of::()]; + let addr_size_mut = mem::size_of::(); let status = unsafe { - ((*self.table).certificate_validation_complete)( + ((*APITABLE).get_param)( self.handle, - result, - tls_alert, + PARAM_CONN_REMOTE_ADDRESS, + (&addr_size_mut) as *const usize as *const u32 as *mut u32, + addr_buffer.as_mut_ptr() as *const c_void, ) }; if Status::failed(status) { return Err(status); } - Ok(()) + Ok(unsafe { *(addr_buffer.as_ptr() as *const c_void as *const Addr) }) } } impl Drop for Connection { fn drop(&mut self) { - unsafe { ((*self.table).connection_close)(self.handle) }; + unsafe { ((*APITABLE).connection_close)(self.handle) }; + } +} + +impl Default for Listener { + fn default() -> Self { + Self::new() } } impl Listener { - pub fn new( + pub fn new() -> Listener { + Listener { + handle: ptr::null(), + } + } + + pub fn open( + &self, registration: &Registration, handler: ListenerEventHandler, context: *const c_void, - ) -> Result { - let new_listener: Handle = ptr::null(); + ) -> Result<(), u32> { let status = unsafe { - ((*registration.table).listener_open)( - registration.handle, - handler, - context, - &new_listener, - ) + ((*APITABLE).listener_open)(registration.handle, handler, context, &self.handle) }; if Status::failed(status) { return Err(status); } - - Ok(Listener { - table: registration.table, - handle: new_listener, - }) + Ok(()) } - pub fn start(&self, alpn: &[Buffer], local_address: &Addr) -> Result<(), u32> { + pub fn start(&self, alpn: &[Buffer], local_address: Option<&Addr>) -> Result<(), u32> { let status = unsafe { - ((*self.table).listener_start)( + ((*APITABLE).listener_start)( self.handle, alpn.as_ptr(), alpn.len() as u32, - *&local_address, + local_address + .map(|addr| addr as *const _) + .unwrap_or(ptr::null()), ) }; if Status::failed(status) { @@ -1753,33 +1912,57 @@ impl Listener { Ok(()) } + pub fn stop(&self) { + unsafe { + ((*APITABLE).listener_stop)(self.handle); + } + } + + pub fn get_local_addr(&self) -> Result { + let mut addr_buffer: [u8; mem::size_of::()] = [0; mem::size_of::()]; + let addr_size_mut = mem::size_of::(); + let status = unsafe { + ((*APITABLE).get_param)( + self.handle, + PARAM_LISTENER_LOCAL_ADDRESS, + (&addr_size_mut) as *const usize as *const u32 as *mut u32, + addr_buffer.as_mut_ptr() as *const c_void, + ) + }; + if Status::failed(status) { + return Err(status); + } + Ok(unsafe { *(addr_buffer.as_ptr() as *const c_void as *const Addr) }) + } + pub fn close(&self) { unsafe { - ((*self.table).listener_close)(self.handle); + ((*APITABLE).listener_close)(self.handle); } } } impl Drop for Listener { fn drop(&mut self) { - unsafe { ((*self.table).listener_close)(self.handle) }; + unsafe { ((*APITABLE).listener_close)(self.handle) }; + } +} + +impl Default for Stream { + fn default() -> Self { + Self::new() } } impl Stream { - pub fn new(context: *const c_void) -> Stream { - let api = unsafe { &*(context as *const Api) }; + pub fn new() -> Stream { Stream { - table: api.table, handle: ptr::null(), } } - pub fn from_parts(handle: Handle, api: &Api) -> Stream { - Stream { - table: api.table, - handle, - } + pub fn from_parts(handle: Handle) -> Stream { + Stream { handle } } pub fn open( @@ -1790,7 +1973,7 @@ impl Stream { context: *const c_void, ) -> Result<(), u32> { let status = unsafe { - ((*self.table).stream_open)(connection.handle, flags, handler, context, &self.handle) + ((*APITABLE).stream_open)(connection.handle, flags, handler, context, &self.handle) }; if Status::failed(status) { return Err(status); @@ -1799,7 +1982,15 @@ impl Stream { } pub fn start(&self, flags: StreamStartFlags) -> Result<(), u32> { - let status = unsafe { ((*self.table).stream_start)(self.handle, flags) }; + let status = unsafe { ((*APITABLE).stream_start)(self.handle, flags) }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } + + pub fn shutdown(&self, flags: StreamShutdownFlags, error_code: u62) -> Result<(), u32> { + let status = unsafe { ((*APITABLE).stream_shutdown)(self.handle, flags, error_code) }; if Status::failed(status) { return Err(status); } @@ -1808,7 +1999,7 @@ impl Stream { pub fn close(&self) { unsafe { - ((*self.table).stream_close)(self.handle); + ((*APITABLE).stream_close)(self.handle); } } @@ -1820,9 +2011,9 @@ impl Stream { client_send_context: *const c_void, ) -> Result<(), u32> { let status = unsafe { - ((*self.table).stream_send)( + ((*APITABLE).stream_send)( self.handle, - *&buffer, + buffer, buffer_count, flags, client_send_context, //(self as *const Stream) as *const c_void, @@ -1836,14 +2027,35 @@ impl Stream { pub fn set_callback_handler(&self, handler: StreamEventHandler, context: *const c_void) { unsafe { - ((*self.table).set_callback_handler)(self.handle, handler as *const c_void, context) + ((*APITABLE).set_callback_handler)(self.handle, handler as *const c_void, context) }; } + + pub fn get_param( + &self, + param: u32, + buffer_length: *mut u32, + buffer: *const c_void, + ) -> Result<(), u32> { + let status = unsafe { ((*APITABLE).get_param)(self.handle, param, buffer_length, buffer) }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } + + pub fn receive_complete(&self, buffer_length: u64) -> Result<(), u32> { + let status = unsafe { ((*APITABLE).stream_receive_complete)(self.handle, buffer_length) }; + if Status::failed(status) { + return Err(status); + } + Ok(()) + } } impl Drop for Stream { fn drop(&mut self) { - unsafe { ((*self.table).stream_close)(self.handle) }; + unsafe { ((*APITABLE).stream_close)(self.handle) }; } } @@ -1859,7 +2071,11 @@ extern "C" fn test_conn_callback( ) -> u32 { let connection = unsafe { &*(context as *const Connection) }; match event.event_type { - CONNECTION_EVENT_CONNECTED => println!("Connected"), + CONNECTION_EVENT_CONNECTED => { + let local_addr = connection.get_local_addr().unwrap().as_socket().unwrap(); + let remote_addr = connection.get_remote_addr().unwrap().as_socket().unwrap(); + println!("Connected({}, {})", local_addr, remote_addr); + } CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT => { println!("Transport shutdown 0x{:x}", unsafe { event.payload.shutdown_initiated_by_transport.status @@ -1914,39 +2130,61 @@ extern "C" fn test_stream_callback( #[test] fn test_module() { - let res = Api::new(); - assert!(res.is_ok(), "Failed to open API: 0x{:x}", res.err().unwrap()); - let api = res.unwrap(); - - let res = Registration::new(&api, ptr::null()); - assert!(res.is_ok(), "Failed to open registration: 0x{:x}", res.err().unwrap()); + let res = Registration::new(ptr::null()); + assert!( + res.is_ok(), + "Failed to open registration: 0x{:x}", + res.err().unwrap() + ); let registration = res.unwrap(); let alpn = [Buffer::from("h3")]; let res = Configuration::new( ®istration, &alpn, + #[cfg(feature = "preview-api")] + Settings::new() + .set_peer_bidi_stream_count(100) + .set_peer_unidi_stream_count(3) + .set_stream_multi_receive_enabled(true), + #[cfg(not(feature = "preview-api"))] Settings::new() .set_peer_bidi_stream_count(100) .set_peer_unidi_stream_count(3), ); - assert!(res.is_ok(), "Failed to open configuration: 0x{:x}", res.err().unwrap()); + assert!( + res.is_ok(), + "Failed to open configuration: 0x{:x}", + res.err().unwrap() + ); let configuration = res.unwrap(); let cred_config = CredentialConfig::new_client(); let res = configuration.load_credential(&cred_config); - assert!(res.is_ok(), "Failed to load credential: 0x{:x}", res.err().unwrap()); + assert!( + res.is_ok(), + "Failed to load credential: 0x{:x}", + res.err().unwrap() + ); - let connection = Connection::new(®istration); + let connection = Connection::new(); let res = connection.open( ®istration, test_conn_callback, &connection as *const Connection as *const c_void, ); - assert!(res.is_ok(), "Failed to open connection: 0x{:x}", res.err().unwrap()); + assert!( + res.is_ok(), + "Failed to open connection: 0x{:x}", + res.err().unwrap() + ); let res = connection.start(&configuration, "www.cloudflare.com", 443); - assert!(res.is_ok(), "Failed to start connection: 0x{:x}", res.err().unwrap()); + assert!( + res.is_ok(), + "Failed to start connection: 0x{:x}", + res.err().unwrap() + ); let duration = std::time::Duration::from_millis(1000); std::thread::sleep(duration); diff --git a/src/test/lib/DataTest.cpp b/src/test/lib/DataTest.cpp index 52e4206919..2e4d91bdc3 100644 --- a/src/test/lib/DataTest.cpp +++ b/src/test/lib/DataTest.cpp @@ -3627,9 +3627,9 @@ void QuicTestConnectionPriority() const uint8_t NumConnections = 8; { - MsQuicConnection *Connections[NumConnections] = {0}; + UniquePtr Connections[NumConnections]; for (uint8_t i = 0; i < NumConnections; ++i) { - Connections[i] = new(std::nothrow) MsQuicConnection(Registration); + Connections[i].reset(new(std::nothrow) MsQuicConnection(Registration)); TEST_QUIC_SUCCEEDED(Connections[i]->GetInitStatus()); TEST_QUIC_SUCCEEDED(Connections[i]->Start(ClientConfiguration, ServerLocalAddr.GetFamily(), QUIC_TEST_LOOPBACK_FOR_AF(ServerLocalAddr.GetFamily()), ServerLocalAddr.GetPort())); TEST_TRUE(Connections[i]->HandshakeCompleteEvent.WaitTimeout(TestWaitTimeout)); @@ -3824,10 +3824,6 @@ void QuicTestConnectionPriority() TEST_TRUE(memcmp(Context.StartOrder, ExpectedStartOrder, sizeof(ExpectedStartOrder)) == 0); TEST_TRUE(memcmp(Context.SendOrder, ExpectedSendOrder, sizeof(ExpectedSendOrder)) == 0); } - - for (uint8_t i = 0; i < NumConnections; ++i) { - delete Connections[i]; - } } }