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 7b65871bdf..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 @@ -87,11 +91,11 @@ jobs: shell: pwsh run: scripts/build.ps1 -Config ${{ inputs.config }} -Platform ${{ inputs.plat }} -Arch ${{ inputs.arch }} -Tls ${{ inputs.tls }} -DisablePerf -DynamicCRT ${{ inputs.sanitize }} - name: Build External Platform Test - if: inputs.build == '-Test' && inputs.sanitize != '-Sanitize' + if: inputs.build == '-Test' shell: pwsh run: | cmake --install build\${{ inputs.plat }}\${{ inputs.arch }}_${{ inputs.tls }} --config ${{ inputs.config }} - cmake src/platform/unittest/external -G "Visual Studio 17 2022" -A ${{ inputs.arch }} -B build_external "-DCMAKE_INSTALL_PREFIX:PATH=C:/Program Files/msquic" -DQUIC_TLS=${{ inputs.tls }} + cmake src/platform/unittest/external -G "Visual Studio 17 2022" -A ${{ inputs.arch }} -B build_external "-DCMAKE_INSTALL_PREFIX:PATH=C:/Program Files/msquic" cmake --build build_external --config ${{ inputs.config }} - name: Build For Perf if: inputs.build == '-Perf' 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 c5ec3b0480..689676fdbe 100644 --- a/.github/workflows/cargo.yml +++ b/.github/workflows/cargo.yml @@ -17,11 +17,12 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest, macos-latest-xlarge] + features: ["", "--features static"] runs-on: ${{ matrix.os }} name: Cargo steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f with: egress-policy: audit - name: Checkout repository @@ -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 739b7b9430..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 @@ -233,7 +234,7 @@ jobs: with: name: merged - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a + uses: codecov/codecov-action@7f8b4b4bde536c465e797be725718b88c5d95e0e with: fail_ci_if_error: true files: msquiccoverage.xml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 27efa8a943..67bb9f63c6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f with: egress-policy: audit - name: Checkout repository @@ -51,7 +51,7 @@ jobs: cmake --build . --target OpenSSL_Target - name: Initialize CodeQL - uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 + uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae with: languages: cpp config-file: ./.github/codeql/codeql-config.yml @@ -62,4 +62,4 @@ jobs: cmake --build . - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 + uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae diff --git a/.github/workflows/docker-publish-xcomp.yml b/.github/workflows/docker-publish-xcomp.yml index 6f6d3db4db..f4cf2e2da2 100644 --- a/.github/workflows/docker-publish-xcomp.yml +++ b/.github/workflows/docker-publish-xcomp.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f with: egress-policy: audit diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index d32091ccea..7d13243e01 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 + uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f with: egress-policy: audit - name: Checkout repository 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/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 5d7c19c32b..dbd6cfb323 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -51,6 +51,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 + uses: github/codeql-action/upload-sarif@df409f7d9260372bd5f19e5b04e83cb3c43714ae with: sarif_file: results.sarif 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-down-level.yml b/.github/workflows/test-down-level.yml index 54f236ef9a..d110ff1b6e 100644 --- a/.github/workflows/test-down-level.yml +++ b/.github/workflows/test-down-level.yml @@ -31,17 +31,17 @@ jobs: { release: "2.2.6", os: "windows-2022", arch: "x64", tls: "openssl" }, { release: "2.2.6", os: "windows-2022", arch: "x64", tls: "openssl3" }, # v2.3 - { release: "2.3.6", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, - { release: "2.3.6", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, - { release: "2.3.6", os: "windows-2022", arch: "x64", tls: "schannel" }, - { release: "2.3.6", os: "windows-2022", arch: "x64", tls: "openssl" }, - { release: "2.3.6", os: "windows-2022", arch: "x64", tls: "openssl3" }, + { release: "2.3.8", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, + { release: "2.3.8", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, + { release: "2.3.8", os: "windows-2022", arch: "x64", tls: "schannel" }, + { release: "2.3.8", os: "windows-2022", arch: "x64", tls: "openssl" }, + { release: "2.3.8", os: "windows-2022", arch: "x64", tls: "openssl3" }, # v2.4 - { release: "2.4.5", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, - { release: "2.4.5", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, - { release: "2.4.5", os: "windows-2022", arch: "x64", tls: "schannel" }, - { release: "2.4.5", os: "windows-2022", arch: "x64", tls: "openssl" }, - { release: "2.4.5", os: "windows-2022", arch: "x64", tls: "openssl3" }, + { release: "2.4.7", os: "ubuntu-20.04", arch: "x64", tls: "openssl" }, + { release: "2.4.7", os: "ubuntu-22.04", arch: "x64", tls: "openssl3" }, + { release: "2.4.7", os: "windows-2022", arch: "x64", tls: "schannel" }, + { release: "2.4.7", os: "windows-2022", arch: "x64", tls: "openssl" }, + { release: "2.4.7", os: "windows-2022", arch: "x64", tls: "openssl3" }, ] runs-on: ${{ matrix.vec.os }} name: Test 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/docs/Release.md b/docs/Release.md index c5549a0ddf..b43e1c97c2 100644 --- a/docs/Release.md +++ b/docs/Release.md @@ -106,7 +106,9 @@ When testing the pipeline, please make sure to comment out the PMC cli commands Prerequisites: - Docker +- Powershell +1. Checkout to release tag. (e.g. `git checkout v2.4.7`) 1. Run `generate-alpine-packaging-file.ps1` script on host computer to create `APKBUILD` file for the release. (This script can run on any Linux distro, and this script will create a docker alpine container to calculate hash keys in APKBUILD file) 1. If you don't have account for [AlpineLinux GitLab](https://gitlab.alpinelinux.org). Create an account and [configure your SSH](https://docs.gitlab.com/ee/user/ssh.html). 1. If you didn't fork `aports` repository yet, Fork `https://gitlab.alpinelinux.org/alpine/aports`. 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 1262cb20b4..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,12 +270,12 @@ 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) if(BUILD_SHARED_LIBS) - install(EXPORT msquic DESTINATION share/msquic) + install(EXPORT msquic NAMESPACE msquic:: DESTINATION share/msquic) endif() if (MSVC AND NOT QUIC_ENABLE_SANITIZERS AND BUILD_SHARED_LIBS) 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 dd676243fe..e61e63feab 100644 --- a/src/bin/msquic-config.cmake.in +++ b/src/bin/msquic-config.cmake.in @@ -1,4 +1,9 @@ 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}) + add_library(${_t} ALIAS msquic::${_t}) + endif() +endforeach() diff --git a/src/core/settings.c b/src/core/settings.c index a3942f691d..40dd41245f 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -431,6 +431,9 @@ QuicSettingApply( Destination->IsSet.DatagramReceiveEnabled = TRUE; } if (Source->IsSet.MaxOperationsPerDrain && (!Destination->IsSet.MaxOperationsPerDrain || OverWrite)) { + if (Source->MaxOperationsPerDrain == 0) { + return FALSE; + } Destination->MaxOperationsPerDrain = Source->MaxOperationsPerDrain; Destination->IsSet.MaxOperationsPerDrain = TRUE; } @@ -795,7 +798,7 @@ QuicSettingsLoad( QUIC_SETTING_MAX_OPERATIONS_PER_DRAIN, (uint8_t*)&Value, &ValueLen); - if (Value <= UINT8_MAX) { + if (Value > 0 && Value <= UINT8_MAX) { Settings->MaxOperationsPerDrain = (uint8_t)Value; } } diff --git a/src/lib.rs b/src/lib.rs index 4ba2a4629b..9a5e00b1d3 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +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; @@ -32,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)] @@ -66,40 +56,187 @@ 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; + pub const QUIC_STATUS_PENDING: u32 = 0x703e5; + pub const QUIC_STATUS_CONTINUE: u32 = 0x704de; + pub const QUIC_STATUS_OUT_OF_MEMORY: u32 = 0x8007000e; + pub const QUIC_STATUS_INVALID_PARAMETER: u32 = 0x80070057; + pub const QUIC_STATUS_INVALID_STATE: u32 = 0x8007139f; + pub const QUIC_STATUS_NOT_SUPPORTED: u32 = 0x80004002; + pub const QUIC_STATUS_NOT_FOUND: u32 = 0x80070490; + pub const QUIC_STATUS_BUFFER_TOO_SMALL: u32 = 0x8007007a; + pub const QUIC_STATUS_HANDSHAKE_FAILURE: u32 = 0x80410000; + pub const QUIC_STATUS_ABORTED: u32 = 0x80004004; + pub const QUIC_STATUS_ADDRESS_IN_USE: u32 = 0x80072740; + pub const QUIC_STATUS_INVALID_ADDRESS: u32 = 0x80072741; + pub const QUIC_STATUS_CONNECTION_TIMEOUT: u32 = 0x80410006; + pub const QUIC_STATUS_CONNECTION_IDLE: u32 = 0x80410005; + pub const QUIC_STATUS_UNREACHABLE: u32 = 0x800704d0; + pub const QUIC_STATUS_INTERNAL_ERROR: u32 = 0x80410003; + pub const QUIC_STATUS_CONNECTION_REFUSED: u32 = 0x800704c9; + pub const QUIC_STATUS_PROTOCOL_ERROR: u32 = 0x80410004; + pub const QUIC_STATUS_VER_NEG_ERROR: u32 = 0x80410001; + pub const QUIC_STATUS_TLS_ERROR: u32 = 0x80072b18; + pub const QUIC_STATUS_USER_CANCELED: u32 = 0x80410002; + 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; + 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; + pub const QUIC_STATUS_EXPIRED_CERTIFICATE: u32 = 0x80410100 | 45; + pub const QUIC_STATUS_UNKNOWN_CERTIFICATE: u32 = 0x80410100 | 46; + pub const QUIC_STATUS_REQUIRED_CERTIFICATE: u32 = 0x80410100 | 116; + pub const QUIC_STATUS_CERT_EXPIRED: u32 = 0x800B0101; + pub const QUIC_STATUS_CERT_UNTRUSTED_ROOT: u32 = 0x800B0109; + pub const QUIC_STATUS_CERT_NO_CERT: u32 = 0x8009030E; +} + +#[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_OUT_OF_MEMORY: u32 = 12; + pub const QUIC_STATUS_INVALID_PARAMETER: u32 = 22; + pub const QUIC_STATUS_INVALID_STATE: u32 = 1; + pub const QUIC_STATUS_NOT_SUPPORTED: u32 = 95; + pub const QUIC_STATUS_NOT_FOUND: u32 = 2; + pub const QUIC_STATUS_BUFFER_TOO_SMALL: u32 = 75; + pub const QUIC_STATUS_HANDSHAKE_FAILURE: u32 = 103; + pub const QUIC_STATUS_ABORTED: u32 = 125; + pub const QUIC_STATUS_ADDRESS_IN_USE: u32 = 98; + pub const QUIC_STATUS_INVALID_ADDRESS: u32 = 97; + pub const QUIC_STATUS_CONNECTION_TIMEOUT: u32 = 110; + pub const QUIC_STATUS_CONNECTION_IDLE: u32 = 62; + pub const QUIC_STATUS_INTERNAL_ERROR: u32 = 5; + pub const QUIC_STATUS_CONNECTION_REFUSED: u32 = 111; + pub const QUIC_STATUS_PROTOCOL_ERROR: u32 = 71; + pub const QUIC_STATUS_VER_NEG_ERROR: u32 = 93; + pub const QUIC_STATUS_UNREACHABLE: u32 = 113; + pub const QUIC_STATUS_TLS_ERROR: u32 = 126; + pub const QUIC_STATUS_USER_CANCELED: u32 = 130; + pub const QUIC_STATUS_ALPN_NEG_FAILURE: u32 = 92; + pub const QUIC_STATUS_STREAM_LIMIT_REACHED: u32 = 86; + pub const QUIC_STATUS_ALPN_IN_USE: u32 = 91; + pub const QUIC_STATUS_ADDRESS_NOT_AVAILABLE: u32 = 99; + pub const QUIC_STATUS_CLOSE_NOTIFY: u32 = 0xBEBC300; + pub const QUIC_STATUS_BAD_CERTIFICATE: u32 = 0xBEBC32A; + pub const QUIC_STATUS_UNSUPPORTED_CERTIFICATE: u32 = 0xBEBC32B; + pub const QUIC_STATUS_REVOKED_CERTIFICATE: u32 = 0xBEBC32C; + pub const QUIC_STATUS_EXPIRED_CERTIFICATE: u32 = 0xBEBC32D; + pub const QUIC_STATUS_UNKNOWN_CERTIFICATE: u32 = 0xBEBC32E; + pub const QUIC_STATUS_REQUIRED_CERTIFICATE: u32 = 0xBEBC374; + pub const QUIC_STATUS_CERT_EXPIRED: u32 = 0xBEBC401; + pub const QUIC_STATUS_CERT_UNTRUSTED_ROOT: u32 = 0xBEBC402; + pub const QUIC_STATUS_CERT_NO_CERT: u32 = 0xBEBC403; +} + +#[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_OUT_OF_MEMORY: u32 = 12; + pub const QUIC_STATUS_INVALID_PARAMETER: u32 = 22; + pub const QUIC_STATUS_INVALID_STATE: u32 = 1; + pub const QUIC_STATUS_NOT_SUPPORTED: u32 = 102; + pub const QUIC_STATUS_NOT_FOUND: u32 = 2; + pub const QUIC_STATUS_BUFFER_TOO_SMALL: u32 = 84; + pub const QUIC_STATUS_HANDSHAKE_FAILURE: u32 = 53; + pub const QUIC_STATUS_ABORTED: u32 = 89; + pub const QUIC_STATUS_ADDRESS_IN_USE: u32 = 48; + pub const QUIC_STATUS_INVALID_ADDRESS: u32 = 47; + pub const QUIC_STATUS_CONNECTION_TIMEOUT: u32 = 60; + pub const QUIC_STATUS_CONNECTION_IDLE: u32 = 101; + pub const QUIC_STATUS_INTERNAL_ERROR: u32 = 5; + pub const QUIC_STATUS_CONNECTION_REFUSED: u32 = 61; + pub const QUIC_STATUS_PROTOCOL_ERROR: u32 = 100; + pub const QUIC_STATUS_VER_NEG_ERROR: u32 = 43; + pub const QUIC_STATUS_UNREACHABLE: u32 = 65; + pub const QUIC_STATUS_TLS_ERROR: u32 = 126; + pub const QUIC_STATUS_USER_CANCELED: u32 = 105; + pub const QUIC_STATUS_ALPN_NEG_FAILURE: u32 = 42; + pub const QUIC_STATUS_STREAM_LIMIT_REACHED: u32 = 86; + pub const QUIC_STATUS_ALPN_IN_USE: u32 = 41; + pub const QUIC_STATUS_ADDRESS_NOT_AVAILABLE: u32 = 47; + pub const QUIC_STATUS_CLOSE_NOTIFY: u32 = 0xBEBC300; + pub const QUIC_STATUS_BAD_CERTIFICATE: u32 = 0xBEBC32A; + pub const QUIC_STATUS_UNSUPPORTED_CERTIFICATE: u32 = 0xBEBC32B; + pub const QUIC_STATUS_REVOKED_CERTIFICATE: u32 = 0xBEBC32C; + pub const QUIC_STATUS_EXPIRED_CERTIFICATE: u32 = 0xBEBC32D; + pub const QUIC_STATUS_UNKNOWN_CERTIFICATE: u32 = 0xBEBC32E; + pub const QUIC_STATUS_REQUIRED_CERTIFICATE: u32 = 0xBEBC374; + pub const QUIC_STATUS_CERT_EXPIRED: u32 = 0xBEBC401; + pub const QUIC_STATUS_CERT_UNTRUSTED_ROOT: u32 = 0xBEBC402; + pub const QUIC_STATUS_CERT_NO_CERT: u32 = 0xBEBC403; +} + +pub use status::*; + /// Helper for processing MsQuic return statuses. pub struct Status {} @@ -129,7 +266,7 @@ impl Status { /// The different possible TLS providers used by MsQuic. pub type TlsProvider = u32; pub const TLS_PROVIDER_SCHANNEL: TlsProvider = 0; -pub const TLS_PROVIDER_OPENSSL : TlsProvider = 1; +pub const TLS_PROVIDER_OPENSSL: TlsProvider = 1; /// Configures how to process a registration's workload. pub type ExecutionProfile = u32; @@ -658,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, @@ -689,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; @@ -773,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)] @@ -837,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)] @@ -850,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 { @@ -864,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 { @@ -871,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 { @@ -878,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)] @@ -912,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)] @@ -941,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)] @@ -956,21 +1187,24 @@ pub struct StreamEventSendShutdownComplete { pub graceful: bool, } - 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)] @@ -979,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 { @@ -990,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)] @@ -1095,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")] @@ -1116,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 { @@ -1175,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 + } } } @@ -1200,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; @@ -1253,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 { @@ -1271,29 +1489,43 @@ impl CredentialConfig { } } +impl Default for Api { + fn default() -> Self { + Self::new() + } +} + impl Api { - pub fn new() -> Api { - let new_table: *const ApiTable = ptr::null(); - let status = unsafe { MsQuicOpenVersion(2, &new_table) }; - if Status::failed(status) { - panic!("MsQuicOpenVersion failure 0x{:x}", 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, } - 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); } } @@ -1303,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, @@ -1319,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) -> Registration { + 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) { - panic!("RegistrationOpen failure 0x{:x}", status); + return Err(status); } - Registration { - table: api.table, + Ok(Registration { 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) }; } } @@ -1358,15 +1604,15 @@ impl Configuration { registration: &Registration, alpn: &[Buffer], settings: *const Settings, - ) -> Configuration { + ) -> Result { 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, @@ -1377,42 +1623,44 @@ impl Configuration { ) }; if Status::failed(status) { - panic!("ConfigurationOpen failure 0x{:x}", status); + return Err(status); } - Configuration { - table: registration.table, + Ok(Configuration { handle: new_configuration, - } + }) } - pub fn load_credential(&self, cred_config: &CredentialConfig) { + 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) { - panic!("ConfigurationLoadCredential failure 0x{:x}", status); + return Err(status); } + Ok(()) } } 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( @@ -1420,50 +1668,57 @@ impl Connection { registration: &Registration, handler: ConnectionEventHandler, 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) { - panic!("ConnectionOpen failure 0x{:x}", status); + return Err(status); } + Ok(()) } - pub fn start(&self, configuration: &Configuration, server_name: &str, server_port: u16) { + 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, ) }; if Status::failed(status) { - panic!("ConnectionStart failure 0x{:x}", status); + return Err(status); } + Ok(()) } 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); } } @@ -1472,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, @@ -1488,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, @@ -1499,18 +1754,19 @@ impl Connection { unsafe { *(stat_buffer.as_ptr() as *const c_void as *const QuicStatisticsV2) } } - pub fn set_configuration(&self, configuration: &Configuration) { + 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) { - panic!("ConnectionSetConfiguration failure 0x{:x}", status); + return Err(status); } + Ok(()) } 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) }; } @@ -1521,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) }; } @@ -1531,126 +1787,182 @@ impl Connection { buffer_count: u32, flags: SendFlags, 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, ) }; if Status::failed(status) { - panic!("DatagramSend failure 0x{:x}", status); + return Err(status); + } + Ok(()) + } + + 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 resumption_ticket_validation_complete( + 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) { - panic!("ticket validation completion failure 0x{:x}", status); + return Err(status); } + Ok(unsafe { *(addr_buffer.as_ptr() as *const c_void as *const Addr) }) } - pub fn certificate_validation_complete( - &self, - result: BOOLEAN, - tls_alert: TlsAlertCode, - ) { + 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) { - panic!("ticket validation completion failure 0x{:x}", status); + return Err(status); } + 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, - ) -> Listener { - 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(()) + } + + pub fn start(&self, alpn: &[Buffer], local_address: Option<&Addr>) -> Result<(), u32> { + let status = unsafe { + ((*APITABLE).listener_start)( + self.handle, + alpn.as_ptr(), + alpn.len() as u32, + local_address + .map(|addr| addr as *const _) + .unwrap_or(ptr::null()), ) }; if Status::failed(status) { - panic!("ListenerOpen failed, {:x}!\n", status); + return Err(status); } + Ok(()) + } - Listener { - table: registration.table, - handle: new_listener, + pub fn stop(&self) { + unsafe { + ((*APITABLE).listener_stop)(self.handle); } } - pub fn start(&self, alpn: &[Buffer], local_address: &Addr) { + 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 { - ((*self.table).listener_start)( + ((*APITABLE).get_param)( self.handle, - alpn.as_ptr(), - alpn.len() as u32, - *&local_address, + 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) { - panic!("ListenerStart failed, {:x}!\n", 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( @@ -1659,25 +1971,35 @@ impl Stream { flags: StreamOpenFlags, handler: StreamEventHandler, 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) { - panic!("StreamOpen failure 0x{:x}", status); + return Err(status); + } + Ok(()) + } + + pub fn start(&self, flags: StreamStartFlags) -> Result<(), u32> { + let status = unsafe { ((*APITABLE).stream_start)(self.handle, flags) }; + if Status::failed(status) { + return Err(status); } + Ok(()) } - pub fn start(&self, flags: StreamStartFlags) { - let status = unsafe { ((*self.table).stream_start)(self.handle, flags) }; + 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) { - panic!("StreamStart failure 0x{:x}", status); + return Err(status); } + Ok(()) } pub fn close(&self) { unsafe { - ((*self.table).stream_close)(self.handle); + ((*APITABLE).stream_close)(self.handle); } } @@ -1687,31 +2009,53 @@ impl Stream { buffer_count: u32, flags: SendFlags, 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, ) }; if Status::failed(status) { - panic!("StreamSend failure 0x{:x}", status); + return Err(status); } + Ok(()) } 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) }; } } @@ -1727,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 @@ -1782,27 +2130,61 @@ extern "C" fn test_stream_callback( #[test] fn test_module() { - let api = Api::new(); - let registration = Registration::new(&api, ptr::null()); + 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 configuration = Configuration::new( + 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() + ); + let configuration = res.unwrap(); + let cred_config = CredentialConfig::new_client(); - configuration.load_credential(&cred_config); + let res = configuration.load_credential(&cred_config); + assert!( + res.is_ok(), + "Failed to load credential: 0x{:x}", + res.err().unwrap() + ); - let connection = Connection::new(®istration); - connection.open( + let connection = Connection::new(); + let res = connection.open( ®istration, test_conn_callback, &connection as *const Connection as *const c_void, ); - connection.start(&configuration, "www.cloudflare.com", 443); + 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() + ); let duration = std::time::Duration::from_millis(1000); std::thread::sleep(duration); diff --git a/src/platform/datapath_raw_xdp_win.c b/src/platform/datapath_raw_xdp_win.c index 461d4fcdec..370b53a529 100644 --- a/src/platform/datapath_raw_xdp_win.c +++ b/src/platform/datapath_raw_xdp_win.c @@ -578,10 +578,11 @@ CxPlatDpRawInterfaceInitialize( if (!SetFileCompletionNotificationModes( (HANDLE)Queue->RxXsk, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE)) { + Status = QUIC_STATUS_INTERNAL_ERROR; QuicTraceEvent( LibraryErrorStatus, "[ lib] ERROR, %u, %s.", - Status, + GetLastError(), "SetFileCompletionNotificationModes"); goto Error; } @@ -700,10 +701,11 @@ CxPlatDpRawInterfaceInitialize( if (!SetFileCompletionNotificationModes( (HANDLE)Queue->TxXsk, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE)) { + Status = QUIC_STATUS_INTERNAL_ERROR; QuicTraceEvent( LibraryErrorStatus, "[ lib] ERROR, %u, %s.", - Status, + GetLastError(), "SetFileCompletionNotificationModes"); goto Error; } diff --git a/src/platform/unittest/external/CMakeLists.txt b/src/platform/unittest/external/CMakeLists.txt index 805b18ff40..cfb1ccba9b 100644 --- a/src/platform/unittest/external/CMakeLists.txt +++ b/src/platform/unittest/external/CMakeLists.txt @@ -5,12 +5,9 @@ cmake_minimum_required(VERSION 3.16) project(msquic_platform_external) -find_package(msquic REQUIRED) -find_library(MSPLATFORM_LIBRARIES - NAMES msquic_platform) +find_package(msquic CONFIG REQUIRED) message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}") -message(STATUS "MSPLATFORM_LIBRARIES: ${MSPLATFORM_LIBRARIES}") include(FetchContent) FetchContent_Declare( @@ -28,11 +25,12 @@ set(SOURCES ../TlsTest.cpp ) -if(QUIC_TLS STREQUAL "openssl" OR QUIC_TLS STREQUAL "openssl3") - list(APPEND OTHER_TERGETS OpenSSL) -endif() +# Remove QUIC_EVENTS_MANIFEST_ETW and QUIC_LOGS_MANIFEST_ETW definitions. These definitions for internal use in msquic +get_target_property(defs msquic::inc INTERFACE_COMPILE_DEFINITIONS) +list(FILTER defs EXCLUDE REGEX "^(QUIC_EVENTS_MANIFEST_ETW|QUIC_LOGS_MANIFEST_ETW)$") +set_target_properties(msquic::inc PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${defs}") add_executable(msquicplatformtest ${SOURCES}) target_include_directories(msquicplatformtest PRIVATE ${CMAKE_INSTALL_PREFIX}/include) -target_link_libraries(msquicplatformtest PRIVATE msquic ${MSPLATFORM_LIBRARIES} ${OTHER_TERGETS} gtest ws2_32 schannel ntdll bcrypt ncrypt crypt32 iphlpapi advapi32 secur32 userenv onecore winmm wbemuuid clang_rt.asan_dbg_dynamic-x86_64 clang_rt.asan_dynamic-x86_64) +target_link_libraries(msquicplatformtest PRIVATE gtest msquic::msquic msquic::msquic_platform ws2_32 ntdll ncrypt crypt32 iphlpapi) target_compile_definitions(msquicplatformtest PRIVATE QUIC_EVENTS_STUB QUIC_LOGS_STUB _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION) diff --git a/src/test/bin/quic_gtest.cpp b/src/test/bin/quic_gtest.cpp index 12a2987548..2010fb93bd 100644 --- a/src/test/bin/quic_gtest.cpp +++ b/src/test/bin/quic_gtest.cpp @@ -89,7 +89,11 @@ class QuicTestEnvironment : public ::testing::Environment { ASSERT_TRUE(DriverService.Start()); ASSERT_TRUE(DriverClient.Initialize(&CertParams, DriverName)); - Config.Flags |= UseDuoNic ? QUIC_EXECUTION_CONFIG_FLAG_XDP : QUIC_EXECUTION_CONFIG_FLAG_NONE; +#if defined(QUIC_API_ENABLE_PREVIEW_FEATURES) + if (UseDuoNic) { + Config.Flags |= QUIC_EXECUTION_CONFIG_FLAG_XDP; + } +#endif QUIC_TEST_CONFIGURATION_PARAMS Params { UseDuoNic, Config, diff --git a/src/test/lib/ApiTest.cpp b/src/test/lib/ApiTest.cpp index 4935cf1d65..71bda9c8ef 100644 --- a/src/test/lib/ApiTest.cpp +++ b/src/test/lib/ApiTest.cpp @@ -2090,6 +2090,32 @@ void SettingApplyTests(HQUIC Handle, uint32_t Param, bool AllowMtuEcnChanges = t sizeof(QUIC_SETTINGS), &Settings)); } + + // + // MaxOperationsPerDrain + // + { + QUIC_SETTINGS Settings{0}; + Settings.IsSet.MaxOperationsPerDrain = TRUE; + + Settings.MaxOperationsPerDrain = 0; // Not allowed + TEST_QUIC_STATUS( + QUIC_STATUS_INVALID_PARAMETER, + MsQuic->SetParam( + Handle, + Param, + sizeof(QUIC_SETTINGS), + &Settings)); + + Settings.MaxOperationsPerDrain = 255; // Max allowed + TEST_QUIC_STATUS( + QUIC_STATUS_SUCCESS, + MsQuic->SetParam( + Handle, + Param, + sizeof(QUIC_SETTINGS), + &Settings)); + } } void QuicTestStatefulGlobalSetParam() 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]; - } } } diff --git a/submodules/googletest b/submodules/googletest index 35d0c36560..7d76a231b0 160000 --- a/submodules/googletest +++ b/submodules/googletest @@ -1 +1 @@ -Subproject commit 35d0c365609296fa4730d62057c487e3cfa030ff +Subproject commit 7d76a231b0e29caf86e68d1df858308cd53b2a66