tokio-rustls-0.24.1/.cargo_vcs_info.json0000644000000001360000000000100135770ustar { "git": { "sha1": "15020d31e50ada3995f0842434f60dc9243a9c76" }, "path_in_vcs": "" }tokio-rustls-0.24.1/.github/workflows/CI.yml000064400000000000000000000026041046102023000170040ustar 00000000000000name: CI on: push: branches: - master pull_request: {} schedule: - cron: "33 4 * * 5" jobs: check: runs-on: ubuntu-latest env: RUSTFLAGS: "-D warnings" steps: - name: Checkout sources uses: actions/checkout@v3 - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable - name: Check run: cargo check --all --all-features --all-targets test: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] rust: [stable] env: RUSTFLAGS: "-D warnings" steps: - name: Checkout sources uses: actions/checkout@v3 - name: Install rust toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Test run: | cargo test --all cargo test -p tokio-rustls --features early-data --test early-data lints: name: Lints runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v3 - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy - name: Run cargo fmt run: cargo fmt --all --check - name: Run cargo clippy if: always() run: cargo clippy --all-features -- -D warnings tokio-rustls-0.24.1/.gitignore000064400000000000000000000000501046102023000143520ustar 00000000000000/target **/*.rs.bk Cargo.lock .DS_Store tokio-rustls-0.24.1/Cargo.lock0000644000000364430000000000100115640ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "argh" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e" dependencies = [ "argh_derive", "argh_shared", ] [[package]] name = "argh_derive" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6" dependencies = [ "argh_shared", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "argh_shared" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "futures-core" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-macro" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", "syn 2.0.18", ] [[package]] name = "futures-task" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "hermit-abi" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "js-sys" version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "lock_api" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "mio" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", "windows-sys", ] [[package]] name = "num_cpus" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets", ] [[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "ring" version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", "once_cell", "spin", "untrusted", "web-sys", "winapi", ] [[package]] name = "rustls" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" dependencies = [ "log", "ring", "rustls-webpki", "sct", ] [[package]] name = "rustls-pemfile" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ "base64", ] [[package]] name = "rustls-webpki" version = "0.100.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" dependencies = [ "ring", "untrusted", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sct" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ "ring", "untrusted", ] [[package]] name = "signal-hook-registry" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", ] [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tokio" version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", "libc", "mio", "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", "syn 2.0.18", ] [[package]] name = "tokio-rustls" version = "0.24.1" dependencies = [ "argh", "futures-util", "lazy_static", "rustls", "rustls-pemfile", "rustls-webpki", "tokio", "webpki-roots", ] [[package]] name = "unicode-ident" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" dependencies = [ "proc-macro2", "quote", "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" [[package]] name = "web-sys" version = "0.3.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "webpki-roots" version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" dependencies = [ "rustls-webpki", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" tokio-rustls-0.24.1/Cargo.toml0000644000000031770000000000100116050ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.56" name = "tokio-rustls" version = "0.24.1" description = "Asynchronous TLS/SSL streams for Tokio using Rustls." homepage = "https://github.com/rustls/tokio-rustls" documentation = "https://docs.rs/tokio-rustls" readme = "README.md" categories = [ "asynchronous", "cryptography", "network-programming", ] license = "MIT/Apache-2.0" repository = "https://github.com/rustls/tokio-rustls" [dependencies.rustls] version = "0.21.0" default-features = false [dependencies.tokio] version = "1.0" [dev-dependencies.argh] version = "0.1" [dev-dependencies.futures-util] version = "0.3.1" [dev-dependencies.lazy_static] version = "1" [dev-dependencies.rustls-pemfile] version = "1" [dev-dependencies.tokio] version = "1.0" features = ["full"] [dev-dependencies.webpki] version = "0.100.0" features = [ "alloc", "std", ] package = "rustls-webpki" [dev-dependencies.webpki-roots] version = "0.23.1" [features] dangerous_configuration = ["rustls/dangerous_configuration"] default = [ "logging", "tls12", ] early-data = [] logging = ["rustls/logging"] secret_extraction = ["rustls/secret_extraction"] tls12 = ["rustls/tls12"] tokio-rustls-0.24.1/Cargo.toml.orig000064400000000000000000000017451046102023000152650ustar 00000000000000[package] name = "tokio-rustls" version = "0.24.1" license = "MIT/Apache-2.0" repository = "https://github.com/rustls/tokio-rustls" homepage = "https://github.com/rustls/tokio-rustls" documentation = "https://docs.rs/tokio-rustls" readme = "README.md" description = "Asynchronous TLS/SSL streams for Tokio using Rustls." categories = ["asynchronous", "cryptography", "network-programming"] edition = "2018" rust-version = "1.56" [dependencies] tokio = "1.0" rustls = { version = "0.21.0", default-features = false } [features] default = ["logging", "tls12"] dangerous_configuration = ["rustls/dangerous_configuration"] early-data = [] logging = ["rustls/logging"] secret_extraction = ["rustls/secret_extraction"] tls12 = ["rustls/tls12"] [dev-dependencies] argh = "0.1" tokio = { version = "1.0", features = ["full"] } futures-util = "0.3.1" lazy_static = "1" webpki-roots = "0.23.1" rustls-pemfile = "1" webpki = { package = "rustls-webpki", version = "0.100.0", features = ["alloc", "std"] } tokio-rustls-0.24.1/LICENSE-APACHE000064400000000000000000000251201046102023000143130ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2017 quininer kel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. tokio-rustls-0.24.1/LICENSE-MIT000064400000000000000000000020401046102023000140170ustar 00000000000000Copyright (c) 2017 quininer kel Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tokio-rustls-0.24.1/README.md000064400000000000000000000047621046102023000136570ustar 00000000000000# tokio-rustls [![github actions](https://github.com/rustls/tokio-rustls/workflows/CI/badge.svg)](https://github.com/rustls/tokio-rustls/actions) [![crates](https://img.shields.io/crates/v/tokio-rustls.svg)](https://crates.io/crates/tokio-rustls) [![license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/rustls/tokio-rustls/blob/main/LICENSE-MIT) [![license](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/rustls/tokio-rustls/blob/main/LICENSE-APACHE) [![docs.rs](https://docs.rs/tokio-rustls/badge.svg)](https://docs.rs/tokio-rustls) Asynchronous TLS/SSL streams for [Tokio](https://tokio.rs/) using [Rustls](https://github.com/rustls/rustls). ### Basic Structure of a Client ```rust use std::sync::Arc; use tokio::net::TcpStream; use tokio_rustls::rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore, ServerName}; use tokio_rustls::TlsConnector; // ... let mut root_cert_store = RootCertStore::empty(); root_cert_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) })); let config = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_cert_store) .with_no_client_auth(); let connector = TlsConnector::from(Arc::new(config)); let dnsname = ServerName::try_from("www.rust-lang.org").unwrap(); let stream = TcpStream::connect(&addr).await?; let mut stream = connector.connect(dnsname, stream).await?; // ... ``` ### Client Example Program See [examples/client](examples/client/src/main.rs). You can run it with: ```sh cd examples/client cargo run -- hsts.badssl.com ``` ### Server Example Program See [examples/server](examples/server/src/main.rs). You can run it with: ```sh cd examples/server cargo run -- 127.0.0.1:8000 --cert mycert.der --key mykey.der ``` ### License & Origin This project is licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) at your option. This started as a fork of [tokio-tls](https://github.com/tokio-rs/tokio-tls). ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in tokio-rustls by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. tokio-rustls-0.24.1/examples/client.rs000064400000000000000000000057071046102023000160420ustar 00000000000000use argh::FromArgs; use std::convert::TryFrom; use std::fs::File; use std::io; use std::io::BufReader; use std::net::ToSocketAddrs; use std::path::PathBuf; use std::sync::Arc; use tokio::io::{copy, split, stdin as tokio_stdin, stdout as tokio_stdout, AsyncWriteExt}; use tokio::net::TcpStream; use tokio_rustls::rustls::{self, OwnedTrustAnchor}; use tokio_rustls::TlsConnector; /// Tokio Rustls client example #[derive(FromArgs)] struct Options { /// host #[argh(positional)] host: String, /// port #[argh(option, short = 'p', default = "443")] port: u16, /// domain #[argh(option, short = 'd')] domain: Option, /// cafile #[argh(option, short = 'c')] cafile: Option, } #[tokio::main] async fn main() -> io::Result<()> { let options: Options = argh::from_env(); let addr = (options.host.as_str(), options.port) .to_socket_addrs()? .next() .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))?; let domain = options.domain.unwrap_or(options.host); let content = format!("GET / HTTP/1.0\r\nHost: {}\r\n\r\n", domain); let mut root_cert_store = rustls::RootCertStore::empty(); if let Some(cafile) = &options.cafile { let mut pem = BufReader::new(File::open(cafile)?); let certs = rustls_pemfile::certs(&mut pem)?; let trust_anchors = certs.iter().map(|cert| { let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) }); root_cert_store.add_server_trust_anchors(trust_anchors); } else { root_cert_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map( |ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) }, )); } let config = rustls::ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_cert_store) .with_no_client_auth(); // i guess this was previously the default? let connector = TlsConnector::from(Arc::new(config)); let stream = TcpStream::connect(&addr).await?; let (mut stdin, mut stdout) = (tokio_stdin(), tokio_stdout()); let domain = rustls::ServerName::try_from(domain.as_str()) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid dnsname"))?; let mut stream = connector.connect(domain, stream).await?; stream.write_all(content.as_bytes()).await?; let (mut reader, mut writer) = split(stream); tokio::select! { ret = copy(&mut reader, &mut stdout) => { ret?; }, ret = copy(&mut stdin, &mut writer) => { ret?; writer.shutdown().await? } } Ok(()) } tokio-rustls-0.24.1/examples/server.rs000064400000000000000000000061271046102023000160670ustar 00000000000000use argh::FromArgs; use rustls_pemfile::{certs, rsa_private_keys}; use std::fs::File; use std::io::{self, BufReader}; use std::net::ToSocketAddrs; use std::path::{Path, PathBuf}; use std::sync::Arc; use tokio::io::{copy, sink, split, AsyncWriteExt}; use tokio::net::TcpListener; use tokio_rustls::rustls::{self, Certificate, PrivateKey}; use tokio_rustls::TlsAcceptor; /// Tokio Rustls server example #[derive(FromArgs)] struct Options { /// bind addr #[argh(positional)] addr: String, /// cert file #[argh(option, short = 'c')] cert: PathBuf, /// key file #[argh(option, short = 'k')] key: PathBuf, /// echo mode #[argh(switch, short = 'e')] echo_mode: bool, } fn load_certs(path: &Path) -> io::Result> { certs(&mut BufReader::new(File::open(path)?)) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid cert")) .map(|mut certs| certs.drain(..).map(Certificate).collect()) } fn load_keys(path: &Path) -> io::Result> { rsa_private_keys(&mut BufReader::new(File::open(path)?)) .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid key")) .map(|mut keys| keys.drain(..).map(PrivateKey).collect()) } #[tokio::main] async fn main() -> io::Result<()> { let options: Options = argh::from_env(); let addr = options .addr .to_socket_addrs()? .next() .ok_or_else(|| io::Error::from(io::ErrorKind::AddrNotAvailable))?; let certs = load_certs(&options.cert)?; let mut keys = load_keys(&options.key)?; let flag_echo = options.echo_mode; let config = rustls::ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(certs, keys.remove(0)) .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?; let acceptor = TlsAcceptor::from(Arc::new(config)); let listener = TcpListener::bind(&addr).await?; loop { let (stream, peer_addr) = listener.accept().await?; let acceptor = acceptor.clone(); let fut = async move { let mut stream = acceptor.accept(stream).await?; if flag_echo { let (mut reader, mut writer) = split(stream); let n = copy(&mut reader, &mut writer).await?; writer.flush().await?; println!("Echo: {} - {}", peer_addr, n); } else { let mut output = sink(); stream .write_all( &b"HTTP/1.0 200 ok\r\n\ Connection: close\r\n\ Content-length: 12\r\n\ \r\n\ Hello world!"[..], ) .await?; stream.shutdown().await?; copy(&mut stream, &mut output).await?; println!("Hello: {}", peer_addr); } Ok(()) as io::Result<()> }; tokio::spawn(async move { if let Err(err) = fut.await { eprintln!("{:?}", err); } }); } } tokio-rustls-0.24.1/scripts/generate-certificate.sh000075500000000000000000000030551046102023000204720ustar 00000000000000#!/bin/bash set -e cd $1 # prepare config file for root CA generation cat <> root.cnf [ req ] distinguished_name = req_dn [ req_dn ] [ v3_ca ] basicConstraints = CA:TRUE keyUsage = digitalSignature, nonRepudiation, keyCertSign, cRLSign subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always EOF ROOT_CA_KEY=root-ca.key.pem ROOT_CA=root-ca.pem ROOT_CA_DER=root-ca.der echo "Generate root CA key" openssl genrsa -out $ROOT_CA_KEY 4096 echo "Generate root CA certificate" openssl req -x509 -new -key $ROOT_CA_KEY -out $ROOT_CA -days 365 -SHA256 -subj "/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd" -config root.cnf -extensions v3_ca openssl x509 -outform der -in $ROOT_CA -out $ROOT_CA_DER rm root.cnf # prepare config file for server certificate generation cat <> server.cnf extendedKeyUsage=serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = foobar.com EOF SERVER_KEY=server.key.pem SERVER_CERT=cert.pem SERVER_CERT_DER=cert.der IDENTITY=identity.p12 PASSPHRASE=mypass echo "Generate server key" openssl genrsa -out $SERVER_KEY 4096 echo "Generate server certificate" openssl req -out server.csr -key $SERVER_KEY -new -days 365 -SHA256 -subj "/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=foobar.com" openssl x509 -req -days 365 -SHA256 -in server.csr -CA $ROOT_CA -CAkey $ROOT_CA_KEY -CAcreateserial -out $SERVER_CERT -extfile server.cnf openssl x509 -outform der -in $SERVER_CERT -out $SERVER_CERT_DER openssl pkcs12 -export -out $IDENTITY -inkey $SERVER_KEY -in $SERVER_CERT -passout pass:$PASSPHRASE rm server.csr rm server.cnf tokio-rustls-0.24.1/src/client.rs000064400000000000000000000165301046102023000150070ustar 00000000000000use super::*; use crate::common::IoSession; #[cfg(unix)] use std::os::unix::io::{AsRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, RawSocket}; /// A wrapper around an underlying raw stream which implements the TLS or SSL /// protocol. #[derive(Debug)] pub struct TlsStream { pub(crate) io: IO, pub(crate) session: ClientConnection, pub(crate) state: TlsState, #[cfg(feature = "early-data")] pub(crate) early_waker: Option, } impl TlsStream { #[inline] pub fn get_ref(&self) -> (&IO, &ClientConnection) { (&self.io, &self.session) } #[inline] pub fn get_mut(&mut self) -> (&mut IO, &mut ClientConnection) { (&mut self.io, &mut self.session) } #[inline] pub fn into_inner(self) -> (IO, ClientConnection) { (self.io, self.session) } } #[cfg(unix)] impl AsRawFd for TlsStream where S: AsRawFd, { fn as_raw_fd(&self) -> RawFd { self.get_ref().0.as_raw_fd() } } #[cfg(windows)] impl AsRawSocket for TlsStream where S: AsRawSocket, { fn as_raw_socket(&self) -> RawSocket { self.get_ref().0.as_raw_socket() } } impl IoSession for TlsStream { type Io = IO; type Session = ClientConnection; #[inline] fn skip_handshake(&self) -> bool { self.state.is_early_data() } #[inline] fn get_mut(&mut self) -> (&mut TlsState, &mut Self::Io, &mut Self::Session) { (&mut self.state, &mut self.io, &mut self.session) } #[inline] fn into_io(self) -> Self::Io { self.io } } impl AsyncRead for TlsStream where IO: AsyncRead + AsyncWrite + Unpin, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { match self.state { #[cfg(feature = "early-data")] TlsState::EarlyData(..) => { let this = self.get_mut(); // In the EarlyData state, we have not really established a Tls connection. // Before writing data through `AsyncWrite` and completing the tls handshake, // we ignore read readiness and return to pending. // // In order to avoid event loss, // we need to register a waker and wake it up after tls is connected. if this .early_waker .as_ref() .filter(|waker| cx.waker().will_wake(waker)) .is_none() { this.early_waker = Some(cx.waker().clone()); } Poll::Pending } TlsState::Stream | TlsState::WriteShutdown => { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); let prev = buf.remaining(); match stream.as_mut_pin().poll_read(cx, buf) { Poll::Ready(Ok(())) => { if prev == buf.remaining() || stream.eof { this.state.shutdown_read(); } Poll::Ready(Ok(())) } Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::ConnectionAborted => { this.state.shutdown_read(); Poll::Ready(Err(err)) } output => output, } } TlsState::ReadShutdown | TlsState::FullyShutdown => Poll::Ready(Ok(())), } } } impl AsyncWrite for TlsStream where IO: AsyncRead + AsyncWrite + Unpin, { /// Note: that it does not guarantee the final data to be sent. /// To be cautious, you must manually call `flush`. fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); #[allow(clippy::match_single_binding)] match this.state { #[cfg(feature = "early-data")] TlsState::EarlyData(ref mut pos, ref mut data) => { use std::io::Write; // write early data if let Some(mut early_data) = stream.session.early_data() { let len = match early_data.write(buf) { Ok(n) => n, Err(err) => return Poll::Ready(Err(err)), }; if len != 0 { data.extend_from_slice(&buf[..len]); return Poll::Ready(Ok(len)); } } // complete handshake while stream.session.is_handshaking() { ready!(stream.handshake(cx))?; } // write early data (fallback) if !stream.session.is_early_data_accepted() { while *pos < data.len() { let len = ready!(stream.as_mut_pin().poll_write(cx, &data[*pos..]))?; *pos += len; } } // end this.state = TlsState::Stream; if let Some(waker) = this.early_waker.take() { waker.wake(); } stream.as_mut_pin().poll_write(cx, buf) } _ => stream.as_mut_pin().poll_write(cx, buf), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); #[cfg(feature = "early-data")] { if let TlsState::EarlyData(ref mut pos, ref mut data) = this.state { // complete handshake while stream.session.is_handshaking() { ready!(stream.handshake(cx))?; } // write early data (fallback) if !stream.session.is_early_data_accepted() { while *pos < data.len() { let len = ready!(stream.as_mut_pin().poll_write(cx, &data[*pos..]))?; *pos += len; } } this.state = TlsState::Stream; if let Some(waker) = this.early_waker.take() { waker.wake(); } } } stream.as_mut_pin().poll_flush(cx) } fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { #[cfg(feature = "early-data")] { // complete handshake if matches!(self.state, TlsState::EarlyData(..)) { ready!(self.as_mut().poll_flush(cx))?; } } if self.state.writeable() { self.session.send_close_notify(); self.state.shutdown_write(); } let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); stream.as_mut_pin().poll_shutdown(cx) } } tokio-rustls-0.24.1/src/common/handshake.rs000064400000000000000000000043301046102023000167420ustar 00000000000000use crate::common::{Stream, TlsState}; use rustls::{ConnectionCommon, SideData}; use std::future::Future; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::task::{Context, Poll}; use std::{io, mem}; use tokio::io::{AsyncRead, AsyncWrite}; pub(crate) trait IoSession { type Io; type Session; fn skip_handshake(&self) -> bool; fn get_mut(&mut self) -> (&mut TlsState, &mut Self::Io, &mut Self::Session); fn into_io(self) -> Self::Io; } pub(crate) enum MidHandshake { Handshaking(IS), End, Error { io: IS::Io, error: io::Error }, } impl Future for MidHandshake where IS: IoSession + Unpin, IS::Io: AsyncRead + AsyncWrite + Unpin, IS::Session: DerefMut + Deref> + Unpin, SD: SideData, { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); let mut stream = match mem::replace(this, MidHandshake::End) { MidHandshake::Handshaking(stream) => stream, // Starting the handshake returned an error; fail the future immediately. MidHandshake::Error { io, error } => return Poll::Ready(Err((error, io))), _ => panic!("unexpected polling after handshake"), }; if !stream.skip_handshake() { let (state, io, session) = stream.get_mut(); let mut tls_stream = Stream::new(io, session).set_eof(!state.readable()); macro_rules! try_poll { ( $e:expr ) => { match $e { Poll::Ready(Ok(_)) => (), Poll::Ready(Err(err)) => return Poll::Ready(Err((err, stream.into_io()))), Poll::Pending => { *this = MidHandshake::Handshaking(stream); return Poll::Pending; } } }; } while tls_stream.session.is_handshaking() { try_poll!(tls_stream.handshake(cx)); } try_poll!(Pin::new(&mut tls_stream).poll_flush(cx)); } Poll::Ready(Ok(stream)) } } tokio-rustls-0.24.1/src/common/mod.rs000064400000000000000000000265741046102023000156110ustar 00000000000000mod handshake; pub(crate) use handshake::{IoSession, MidHandshake}; use rustls::{ConnectionCommon, SideData}; use std::io::{self, IoSlice, Read, Write}; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::task::{Context, Poll}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; #[derive(Debug)] pub enum TlsState { #[cfg(feature = "early-data")] EarlyData(usize, Vec), Stream, ReadShutdown, WriteShutdown, FullyShutdown, } impl TlsState { #[inline] pub fn shutdown_read(&mut self) { match *self { TlsState::WriteShutdown | TlsState::FullyShutdown => *self = TlsState::FullyShutdown, _ => *self = TlsState::ReadShutdown, } } #[inline] pub fn shutdown_write(&mut self) { match *self { TlsState::ReadShutdown | TlsState::FullyShutdown => *self = TlsState::FullyShutdown, _ => *self = TlsState::WriteShutdown, } } #[inline] pub fn writeable(&self) -> bool { !matches!(*self, TlsState::WriteShutdown | TlsState::FullyShutdown) } #[inline] pub fn readable(&self) -> bool { !matches!(*self, TlsState::ReadShutdown | TlsState::FullyShutdown) } #[inline] #[cfg(feature = "early-data")] pub fn is_early_data(&self) -> bool { matches!(self, TlsState::EarlyData(..)) } #[inline] #[cfg(not(feature = "early-data"))] pub const fn is_early_data(&self) -> bool { false } } pub struct Stream<'a, IO, C> { pub io: &'a mut IO, pub session: &'a mut C, pub eof: bool, } impl<'a, IO: AsyncRead + AsyncWrite + Unpin, C, SD> Stream<'a, IO, C> where C: DerefMut + Deref>, SD: SideData, { pub fn new(io: &'a mut IO, session: &'a mut C) -> Self { Stream { io, session, // The state so far is only used to detect EOF, so either Stream // or EarlyData state should both be all right. eof: false, } } pub fn set_eof(mut self, eof: bool) -> Self { self.eof = eof; self } pub fn as_mut_pin(&mut self) -> Pin<&mut Self> { Pin::new(self) } pub fn read_io(&mut self, cx: &mut Context) -> Poll> { let mut reader = SyncReadAdapter { io: self.io, cx }; let n = match self.session.read_tls(&mut reader) { Ok(n) => n, Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => return Poll::Pending, Err(err) => return Poll::Ready(Err(err)), }; let stats = self.session.process_new_packets().map_err(|err| { // In case we have an alert to send describing this error, // try a last-gasp write -- but don't predate the primary // error. let _ = self.write_io(cx); io::Error::new(io::ErrorKind::InvalidData, err) })?; if stats.peer_has_closed() && self.session.is_handshaking() { return Poll::Ready(Err(io::Error::new( io::ErrorKind::UnexpectedEof, "tls handshake alert", ))); } Poll::Ready(Ok(n)) } pub fn write_io(&mut self, cx: &mut Context) -> Poll> { struct Writer<'a, 'b, T> { io: &'a mut T, cx: &'a mut Context<'b>, } impl<'a, 'b, T: Unpin> Writer<'a, 'b, T> { #[inline] fn poll_with( &mut self, f: impl FnOnce(Pin<&mut T>, &mut Context<'_>) -> Poll>, ) -> io::Result { match f(Pin::new(self.io), self.cx) { Poll::Ready(result) => result, Poll::Pending => Err(io::ErrorKind::WouldBlock.into()), } } } impl<'a, 'b, T: AsyncWrite + Unpin> Write for Writer<'a, 'b, T> { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { self.poll_with(|io, cx| io.poll_write(cx, buf)) } #[inline] fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.poll_with(|io, cx| io.poll_write_vectored(cx, bufs)) } fn flush(&mut self) -> io::Result<()> { self.poll_with(|io, cx| io.poll_flush(cx)) } } let mut writer = Writer { io: self.io, cx }; match self.session.write_tls(&mut writer) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Poll::Pending, result => Poll::Ready(result), } } pub fn handshake(&mut self, cx: &mut Context) -> Poll> { let mut wrlen = 0; let mut rdlen = 0; loop { let mut write_would_block = false; let mut read_would_block = false; let mut need_flush = false; while self.session.wants_write() { match self.write_io(cx) { Poll::Ready(Ok(n)) => { wrlen += n; need_flush = true; } Poll::Pending => { write_would_block = true; break; } Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), } } if need_flush { match Pin::new(&mut self.io).poll_flush(cx) { Poll::Ready(Ok(())) => (), Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), Poll::Pending => write_would_block = true, } } while !self.eof && self.session.wants_read() { match self.read_io(cx) { Poll::Ready(Ok(0)) => self.eof = true, Poll::Ready(Ok(n)) => rdlen += n, Poll::Pending => { read_would_block = true; break; } Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), } } return match (self.eof, self.session.is_handshaking()) { (true, true) => { let err = io::Error::new(io::ErrorKind::UnexpectedEof, "tls handshake eof"); Poll::Ready(Err(err)) } (_, false) => Poll::Ready(Ok((rdlen, wrlen))), (_, true) if write_would_block || read_would_block => { if rdlen != 0 || wrlen != 0 { Poll::Ready(Ok((rdlen, wrlen))) } else { Poll::Pending } } (..) => continue, }; } } } impl<'a, IO: AsyncRead + AsyncWrite + Unpin, C, SD> AsyncRead for Stream<'a, IO, C> where C: DerefMut + Deref>, SD: SideData, { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { let mut io_pending = false; // read a packet while !self.eof && self.session.wants_read() { match self.read_io(cx) { Poll::Ready(Ok(0)) => { break; } Poll::Ready(Ok(_)) => (), Poll::Pending => { io_pending = true; break; } Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), } } match self.session.reader().read(buf.initialize_unfilled()) { // If Rustls returns `Ok(0)` (while `buf` is non-empty), the peer closed the // connection with a `CloseNotify` message and no more data will be forthcoming. // // Rustls yielded more data: advance the buffer, then see if more data is coming. // // We don't need to modify `self.eof` here, because it is only a temporary mark. // rustls will only return 0 if is has received `CloseNotify`, // in which case no additional processing is required. Ok(n) => { buf.advance(n); Poll::Ready(Ok(())) } // Rustls doesn't have more data to yield, but it believes the connection is open. Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { if !io_pending { // If `wants_read()` is satisfied, rustls will not return `WouldBlock`. // but if it does, we can try again. // // If the rustls state is abnormal, it may cause a cyclic wakeup. // but tokio's cooperative budget will prevent infinite wakeup. cx.waker().wake_by_ref(); } Poll::Pending } Err(err) => Poll::Ready(Err(err)), } } } impl<'a, IO: AsyncRead + AsyncWrite + Unpin, C, SD> AsyncWrite for Stream<'a, IO, C> where C: DerefMut + Deref>, SD: SideData, { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context, buf: &[u8], ) -> Poll> { let mut pos = 0; while pos != buf.len() { let mut would_block = false; match self.session.writer().write(&buf[pos..]) { Ok(n) => pos += n, Err(err) => return Poll::Ready(Err(err)), }; while self.session.wants_write() { match self.write_io(cx) { Poll::Ready(Ok(0)) | Poll::Pending => { would_block = true; break; } Poll::Ready(Ok(_)) => (), Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), } } return match (pos, would_block) { (0, true) => Poll::Pending, (n, true) => Poll::Ready(Ok(n)), (_, false) => continue, }; } Poll::Ready(Ok(pos)) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { self.session.writer().flush()?; while self.session.wants_write() { ready!(self.write_io(cx))?; } Pin::new(&mut self.io).poll_flush(cx) } fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { while self.session.wants_write() { ready!(self.write_io(cx))?; } Pin::new(&mut self.io).poll_shutdown(cx) } } /// An adapter that implements a [`Read`] interface for [`AsyncRead`] types and an /// associated [`Context`]. /// /// Turns `Poll::Pending` into `WouldBlock`. pub struct SyncReadAdapter<'a, 'b, T> { pub io: &'a mut T, pub cx: &'a mut Context<'b>, } impl<'a, 'b, T: AsyncRead + Unpin> Read for SyncReadAdapter<'a, 'b, T> { #[inline] fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut buf = ReadBuf::new(buf); match Pin::new(&mut self.io).poll_read(self.cx, &mut buf) { Poll::Ready(Ok(())) => Ok(buf.filled().len()), Poll::Ready(Err(err)) => Err(err), Poll::Pending => Err(io::ErrorKind::WouldBlock.into()), } } } #[cfg(test)] mod test_stream; tokio-rustls-0.24.1/src/common/test_stream.rs000064400000000000000000000207411046102023000173520ustar 00000000000000use super::Stream; use futures_util::future::poll_fn; use futures_util::task::noop_waker_ref; use rustls::{ClientConnection, Connection, ServerConnection}; use std::io::{self, Cursor, Read, Write}; use std::pin::Pin; use std::task::{Context, Poll}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}; struct Good<'a>(&'a mut Connection); impl<'a> AsyncRead for Good<'a> { fn poll_read( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { let mut buf2 = buf.initialize_unfilled(); Poll::Ready(match self.0.write_tls(buf2.by_ref()) { Ok(n) => { buf.advance(n); Ok(()) } Err(err) => Err(err), }) } } impl<'a> AsyncWrite for Good<'a> { fn poll_write( mut self: Pin<&mut Self>, _cx: &mut Context<'_>, mut buf: &[u8], ) -> Poll> { let len = self.0.read_tls(buf.by_ref())?; self.0 .process_new_packets() .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; Poll::Ready(Ok(len)) } fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { self.0 .process_new_packets() .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; Poll::Ready(Ok(())) } fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.0.send_close_notify(); dbg!("sent close notify"); self.poll_flush(cx) } } struct Pending; impl AsyncRead for Pending { fn poll_read( self: Pin<&mut Self>, _cx: &mut Context<'_>, _: &mut ReadBuf<'_>, ) -> Poll> { Poll::Pending } } impl AsyncWrite for Pending { fn poll_write( self: Pin<&mut Self>, _cx: &mut Context<'_>, _buf: &[u8], ) -> Poll> { Poll::Pending } fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } struct Expected(Cursor>); impl AsyncRead for Expected { fn poll_read( self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { let this = self.get_mut(); let n = std::io::Read::read(&mut this.0, buf.initialize_unfilled())?; buf.advance(n); Poll::Ready(Ok(())) } } impl AsyncWrite for Expected { fn poll_write( self: Pin<&mut Self>, _cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { Poll::Ready(Ok(buf.len())) } fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } } #[tokio::test] async fn stream_good() -> io::Result<()> { const FILE: &[u8] = include_bytes!("../../README.md"); let (server, mut client) = make_pair(); let mut server = Connection::from(server); poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?; io::copy(&mut Cursor::new(FILE), &mut server.writer())?; server.send_close_notify(); { let mut good = Good(&mut server); let mut stream = Stream::new(&mut good, &mut client); let mut buf = Vec::new(); dbg!(stream.read_to_end(&mut buf).await)?; assert_eq!(buf, FILE); dbg!(stream.write_all(b"Hello World!").await)?; stream.session.send_close_notify(); dbg!(stream.shutdown().await)?; } let mut buf = String::new(); dbg!(server.process_new_packets()).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; dbg!(server.reader().read_to_string(&mut buf))?; assert_eq!(buf, "Hello World!"); Ok(()) as io::Result<()> } #[tokio::test] async fn stream_bad() -> io::Result<()> { let (server, mut client) = make_pair(); let mut server = Connection::from(server); poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?; client.set_buffer_limit(Some(1024)); let mut bad = Pending; let mut stream = Stream::new(&mut bad, &mut client); assert_eq!( poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x42; 8])).await?, 8 ); assert_eq!( poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x42; 8])).await?, 8 ); let r = poll_fn(|cx| stream.as_mut_pin().poll_write(cx, &[0x00; 1024])).await?; // fill buffer assert!(r < 1024); let mut cx = Context::from_waker(noop_waker_ref()); let ret = stream.as_mut_pin().poll_write(&mut cx, &[0x01]); assert!(ret.is_pending()); Ok(()) as io::Result<()> } #[tokio::test] async fn stream_handshake() -> io::Result<()> { let (server, mut client) = make_pair(); let mut server = Connection::from(server); { let mut good = Good(&mut server); let mut stream = Stream::new(&mut good, &mut client); let (r, w) = poll_fn(|cx| stream.handshake(cx)).await?; assert!(r > 0); assert!(w > 0); poll_fn(|cx| stream.handshake(cx)).await?; // finish server handshake } assert!(!server.is_handshaking()); assert!(!client.is_handshaking()); Ok(()) as io::Result<()> } #[tokio::test] async fn stream_buffered_handshake() -> io::Result<()> { use tokio::io::BufWriter; let (server, mut client) = make_pair(); let mut server = Connection::from(server); { let mut good = BufWriter::new(Good(&mut server)); let mut stream = Stream::new(&mut good, &mut client); let (r, w) = poll_fn(|cx| stream.handshake(cx)).await?; assert!(r > 0); assert!(w > 0); poll_fn(|cx| stream.handshake(cx)).await?; // finish server handshake } assert!(!server.is_handshaking()); assert!(!client.is_handshaking()); Ok(()) as io::Result<()> } #[tokio::test] async fn stream_handshake_eof() -> io::Result<()> { let (_, mut client) = make_pair(); let mut bad = Expected(Cursor::new(Vec::new())); let mut stream = Stream::new(&mut bad, &mut client); let mut cx = Context::from_waker(noop_waker_ref()); let r = stream.handshake(&mut cx); assert_eq!( r.map_err(|err| err.kind()), Poll::Ready(Err(io::ErrorKind::UnexpectedEof)) ); Ok(()) as io::Result<()> } // see https://github.com/tokio-rs/tls/issues/77 #[tokio::test] async fn stream_handshake_regression_issues_77() -> io::Result<()> { let (_, mut client) = make_pair(); let mut bad = Expected(Cursor::new(b"\x15\x03\x01\x00\x02\x02\x00".to_vec())); let mut stream = Stream::new(&mut bad, &mut client); let mut cx = Context::from_waker(noop_waker_ref()); let r = stream.handshake(&mut cx); assert_eq!( r.map_err(|err| err.kind()), Poll::Ready(Err(io::ErrorKind::UnexpectedEof)) ); Ok(()) as io::Result<()> } #[tokio::test] async fn stream_eof() -> io::Result<()> { let (server, mut client) = make_pair(); let mut server = Connection::from(server); poll_fn(|cx| do_handshake(&mut client, &mut server, cx)).await?; let mut bad = Expected(Cursor::new(Vec::new())); let mut stream = Stream::new(&mut bad, &mut client); let mut buf = Vec::new(); let result = stream.read_to_end(&mut buf).await; assert_eq!( result.err().map(|e| e.kind()), Some(io::ErrorKind::UnexpectedEof) ); Ok(()) as io::Result<()> } fn make_pair() -> (ServerConnection, ClientConnection) { use std::convert::TryFrom; let (sconfig, cconfig) = utils::make_configs(); let server = ServerConnection::new(sconfig).unwrap(); let domain = rustls::ServerName::try_from("foobar.com").unwrap(); let client = ClientConnection::new(cconfig, domain).unwrap(); (server, client) } fn do_handshake( client: &mut ClientConnection, server: &mut Connection, cx: &mut Context<'_>, ) -> Poll> { let mut good = Good(server); let mut stream = Stream::new(&mut good, client); while stream.session.is_handshaking() { ready!(stream.handshake(cx))?; } while stream.session.wants_write() { ready!(stream.write_io(cx))?; } Poll::Ready(Ok(())) } // Share `utils` module with integration tests include!("../../tests/utils.rs"); tokio-rustls-0.24.1/src/lib.rs000064400000000000000000000410271046102023000142760ustar 00000000000000//! Asynchronous TLS/SSL streams for Tokio using [Rustls](https://github.com/rustls/rustls). //! //! # Why do I need to call `poll_flush`? //! //! Most TLS implementations will have an internal buffer to improve throughput, //! and rustls is no exception. //! //! When we write data to `TlsStream`, we always write rustls buffer first, //! then take out rustls encrypted data packet, and write it to data channel (like TcpStream). //! When data channel is pending, some data may remain in rustls buffer. //! //! `tokio-rustls` To keep it simple and correct, [TlsStream] will behave like `BufWriter`. //! For `TlsStream`, this means that data written by `poll_write` is not guaranteed to be written to `TcpStream`. //! You must call `poll_flush` to ensure that it is written to `TcpStream`. //! //! You should call `poll_flush` at the appropriate time, //! such as when a period of `poll_write` write is complete and there is no more data to write. //! //! ## Why don't we write during `poll_read`? //! //! We did this in the early days of `tokio-rustls`, but it caused some bugs. //! We can solve these bugs through some solutions, but this will cause performance degradation (reverse false wakeup). //! //! And reverse write will also prevent us implement full duplex in the future. //! //! see //! //! ## Why can't we handle it like `native-tls`? //! //! When data channel returns to pending, `native-tls` will falsely report the number of bytes it consumes. //! This means that if data written by `poll_write` is not actually written to data channel, it will not return `Ready`. //! Thus avoiding the call of `poll_flush`. //! //! but which does not conform to convention of `AsyncWrite` trait. //! This means that if you give inconsistent data in two `poll_write`, it may cause unexpected behavior. //! //! see macro_rules! ready { ( $e:expr ) => { match $e { std::task::Poll::Ready(t) => t, std::task::Poll::Pending => return std::task::Poll::Pending, } }; } pub mod client; mod common; pub mod server; use common::{MidHandshake, Stream, TlsState}; use rustls::{ClientConfig, ClientConnection, CommonState, ServerConfig, ServerConnection}; use std::future::Future; use std::io; #[cfg(unix)] use std::os::unix::io::{AsRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, RawSocket}; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; pub use rustls; /// A wrapper around a `rustls::ClientConfig`, providing an async `connect` method. #[derive(Clone)] pub struct TlsConnector { inner: Arc, #[cfg(feature = "early-data")] early_data: bool, } /// A wrapper around a `rustls::ServerConfig`, providing an async `accept` method. #[derive(Clone)] pub struct TlsAcceptor { inner: Arc, } impl From> for TlsConnector { fn from(inner: Arc) -> TlsConnector { TlsConnector { inner, #[cfg(feature = "early-data")] early_data: false, } } } impl From> for TlsAcceptor { fn from(inner: Arc) -> TlsAcceptor { TlsAcceptor { inner } } } impl TlsConnector { /// Enable 0-RTT. /// /// If you want to use 0-RTT, /// You must also set `ClientConfig.enable_early_data` to `true`. #[cfg(feature = "early-data")] pub fn early_data(mut self, flag: bool) -> TlsConnector { self.early_data = flag; self } #[inline] pub fn connect(&self, domain: rustls::ServerName, stream: IO) -> Connect where IO: AsyncRead + AsyncWrite + Unpin, { self.connect_with(domain, stream, |_| ()) } pub fn connect_with(&self, domain: rustls::ServerName, stream: IO, f: F) -> Connect where IO: AsyncRead + AsyncWrite + Unpin, F: FnOnce(&mut ClientConnection), { let mut session = match ClientConnection::new(self.inner.clone(), domain) { Ok(session) => session, Err(error) => { return Connect(MidHandshake::Error { io: stream, // TODO(eliza): should this really return an `io::Error`? // Probably not... error: io::Error::new(io::ErrorKind::Other, error), }); } }; f(&mut session); Connect(MidHandshake::Handshaking(client::TlsStream { io: stream, #[cfg(not(feature = "early-data"))] state: TlsState::Stream, #[cfg(feature = "early-data")] state: if self.early_data && session.early_data().is_some() { TlsState::EarlyData(0, Vec::new()) } else { TlsState::Stream }, #[cfg(feature = "early-data")] early_waker: None, session, })) } } impl TlsAcceptor { #[inline] pub fn accept(&self, stream: IO) -> Accept where IO: AsyncRead + AsyncWrite + Unpin, { self.accept_with(stream, |_| ()) } pub fn accept_with(&self, stream: IO, f: F) -> Accept where IO: AsyncRead + AsyncWrite + Unpin, F: FnOnce(&mut ServerConnection), { let mut session = match ServerConnection::new(self.inner.clone()) { Ok(session) => session, Err(error) => { return Accept(MidHandshake::Error { io: stream, // TODO(eliza): should this really return an `io::Error`? // Probably not... error: io::Error::new(io::ErrorKind::Other, error), }); } }; f(&mut session); Accept(MidHandshake::Handshaking(server::TlsStream { session, io: stream, state: TlsState::Stream, })) } } pub struct LazyConfigAcceptor { acceptor: rustls::server::Acceptor, io: Option, } impl LazyConfigAcceptor where IO: AsyncRead + AsyncWrite + Unpin, { #[inline] pub fn new(acceptor: rustls::server::Acceptor, io: IO) -> Self { Self { acceptor, io: Some(io), } } /// Takes back the client connection. Will return `None` if called more than once or if the /// connection has been accepted. /// /// # Example /// /// ```no_run /// # fn choose_server_config( /// # _: rustls::server::ClientHello, /// # ) -> std::sync::Arc { /// # unimplemented!(); /// # } /// # #[allow(unused_variables)] /// # async fn listen() { /// use tokio::io::AsyncWriteExt; /// let listener = tokio::net::TcpListener::bind("127.0.0.1:4443").await.unwrap(); /// let (stream, _) = listener.accept().await.unwrap(); /// /// let acceptor = tokio_rustls::LazyConfigAcceptor::new(rustls::server::Acceptor::default(), stream); /// futures_util::pin_mut!(acceptor); /// /// match acceptor.as_mut().await { /// Ok(start) => { /// let clientHello = start.client_hello(); /// let config = choose_server_config(clientHello); /// let stream = start.into_stream(config).await.unwrap(); /// // Proceed with handling the ServerConnection... /// } /// Err(err) => { /// if let Some(mut stream) = acceptor.take_io() { /// stream /// .write_all( /// format!("HTTP/1.1 400 Invalid Input\r\n\r\n\r\n{:?}\n", err) /// .as_bytes() /// ) /// .await /// .unwrap(); /// } /// } /// } /// # } /// ``` pub fn take_io(&mut self) -> Option { self.io.take() } } impl Future for LazyConfigAcceptor where IO: AsyncRead + AsyncWrite + Unpin, { type Output = Result, io::Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); loop { let io = match this.io.as_mut() { Some(io) => io, None => { return Poll::Ready(Err(io::Error::new( io::ErrorKind::Other, "acceptor cannot be polled after acceptance", ))) } }; let mut reader = common::SyncReadAdapter { io, cx }; match this.acceptor.read_tls(&mut reader) { Ok(0) => return Err(io::ErrorKind::UnexpectedEof.into()).into(), Ok(_) => {} Err(e) if e.kind() == io::ErrorKind::WouldBlock => return Poll::Pending, Err(e) => return Err(e).into(), } match this.acceptor.accept() { Ok(Some(accepted)) => { let io = this.io.take().unwrap(); return Poll::Ready(Ok(StartHandshake { accepted, io })); } Ok(None) => continue, Err(err) => { return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, err))) } } } } } pub struct StartHandshake { accepted: rustls::server::Accepted, io: IO, } impl StartHandshake where IO: AsyncRead + AsyncWrite + Unpin, { pub fn client_hello(&self) -> rustls::server::ClientHello<'_> { self.accepted.client_hello() } pub fn into_stream(self, config: Arc) -> Accept { self.into_stream_with(config, |_| ()) } pub fn into_stream_with(self, config: Arc, f: F) -> Accept where F: FnOnce(&mut ServerConnection), { let mut conn = match self.accepted.into_connection(config) { Ok(conn) => conn, Err(error) => { return Accept(MidHandshake::Error { io: self.io, // TODO(eliza): should this really return an `io::Error`? // Probably not... error: io::Error::new(io::ErrorKind::Other, error), }); } }; f(&mut conn); Accept(MidHandshake::Handshaking(server::TlsStream { session: conn, io: self.io, state: TlsState::Stream, })) } } /// Future returned from `TlsConnector::connect` which will resolve /// once the connection handshake has finished. pub struct Connect(MidHandshake>); /// Future returned from `TlsAcceptor::accept` which will resolve /// once the accept handshake has finished. pub struct Accept(MidHandshake>); /// Like [Connect], but returns `IO` on failure. pub struct FallibleConnect(MidHandshake>); /// Like [Accept], but returns `IO` on failure. pub struct FallibleAccept(MidHandshake>); impl Connect { #[inline] pub fn into_fallible(self) -> FallibleConnect { FallibleConnect(self.0) } pub fn get_ref(&self) -> Option<&IO> { match &self.0 { MidHandshake::Handshaking(sess) => Some(sess.get_ref().0), MidHandshake::Error { io, .. } => Some(io), MidHandshake::End => None, } } pub fn get_mut(&mut self) -> Option<&mut IO> { match &mut self.0 { MidHandshake::Handshaking(sess) => Some(sess.get_mut().0), MidHandshake::Error { io, .. } => Some(io), MidHandshake::End => None, } } } impl Accept { #[inline] pub fn into_fallible(self) -> FallibleAccept { FallibleAccept(self.0) } pub fn get_ref(&self) -> Option<&IO> { match &self.0 { MidHandshake::Handshaking(sess) => Some(sess.get_ref().0), MidHandshake::Error { io, .. } => Some(io), MidHandshake::End => None, } } pub fn get_mut(&mut self) -> Option<&mut IO> { match &mut self.0 { MidHandshake::Handshaking(sess) => Some(sess.get_mut().0), MidHandshake::Error { io, .. } => Some(io), MidHandshake::End => None, } } } impl Future for Connect { type Output = io::Result>; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.0).poll(cx).map_err(|(err, _)| err) } } impl Future for Accept { type Output = io::Result>; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.0).poll(cx).map_err(|(err, _)| err) } } impl Future for FallibleConnect { type Output = Result, (io::Error, IO)>; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.0).poll(cx) } } impl Future for FallibleAccept { type Output = Result, (io::Error, IO)>; #[inline] fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.0).poll(cx) } } /// Unified TLS stream type /// /// This abstracts over the inner `client::TlsStream` and `server::TlsStream`, so you can use /// a single type to keep both client- and server-initiated TLS-encrypted connections. #[allow(clippy::large_enum_variant)] // https://github.com/rust-lang/rust-clippy/issues/9798 #[derive(Debug)] pub enum TlsStream { Client(client::TlsStream), Server(server::TlsStream), } impl TlsStream { pub fn get_ref(&self) -> (&T, &CommonState) { use TlsStream::*; match self { Client(io) => { let (io, session) = io.get_ref(); (io, session) } Server(io) => { let (io, session) = io.get_ref(); (io, session) } } } pub fn get_mut(&mut self) -> (&mut T, &mut CommonState) { use TlsStream::*; match self { Client(io) => { let (io, session) = io.get_mut(); (io, &mut *session) } Server(io) => { let (io, session) = io.get_mut(); (io, &mut *session) } } } } impl From> for TlsStream { fn from(s: client::TlsStream) -> Self { Self::Client(s) } } impl From> for TlsStream { fn from(s: server::TlsStream) -> Self { Self::Server(s) } } #[cfg(unix)] impl AsRawFd for TlsStream where S: AsRawFd, { fn as_raw_fd(&self) -> RawFd { self.get_ref().0.as_raw_fd() } } #[cfg(windows)] impl AsRawSocket for TlsStream where S: AsRawSocket, { fn as_raw_socket(&self) -> RawSocket { self.get_ref().0.as_raw_socket() } } impl AsyncRead for TlsStream where T: AsyncRead + AsyncWrite + Unpin, { #[inline] fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { match self.get_mut() { TlsStream::Client(x) => Pin::new(x).poll_read(cx, buf), TlsStream::Server(x) => Pin::new(x).poll_read(cx, buf), } } } impl AsyncWrite for TlsStream where T: AsyncRead + AsyncWrite + Unpin, { #[inline] fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { match self.get_mut() { TlsStream::Client(x) => Pin::new(x).poll_write(cx, buf), TlsStream::Server(x) => Pin::new(x).poll_write(cx, buf), } } #[inline] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match self.get_mut() { TlsStream::Client(x) => Pin::new(x).poll_flush(cx), TlsStream::Server(x) => Pin::new(x).poll_flush(cx), } } #[inline] fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match self.get_mut() { TlsStream::Client(x) => Pin::new(x).poll_shutdown(cx), TlsStream::Server(x) => Pin::new(x).poll_shutdown(cx), } } } tokio-rustls-0.24.1/src/server.rs000064400000000000000000000100411046102023000150260ustar 00000000000000#[cfg(unix)] use std::os::unix::io::{AsRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, RawSocket}; use super::*; use crate::common::IoSession; /// A wrapper around an underlying raw stream which implements the TLS or SSL /// protocol. #[derive(Debug)] pub struct TlsStream { pub(crate) io: IO, pub(crate) session: ServerConnection, pub(crate) state: TlsState, } impl TlsStream { #[inline] pub fn get_ref(&self) -> (&IO, &ServerConnection) { (&self.io, &self.session) } #[inline] pub fn get_mut(&mut self) -> (&mut IO, &mut ServerConnection) { (&mut self.io, &mut self.session) } #[inline] pub fn into_inner(self) -> (IO, ServerConnection) { (self.io, self.session) } } impl IoSession for TlsStream { type Io = IO; type Session = ServerConnection; #[inline] fn skip_handshake(&self) -> bool { false } #[inline] fn get_mut(&mut self) -> (&mut TlsState, &mut Self::Io, &mut Self::Session) { (&mut self.state, &mut self.io, &mut self.session) } #[inline] fn into_io(self) -> Self::Io { self.io } } impl AsyncRead for TlsStream where IO: AsyncRead + AsyncWrite + Unpin, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); match &this.state { TlsState::Stream | TlsState::WriteShutdown => { let prev = buf.remaining(); match stream.as_mut_pin().poll_read(cx, buf) { Poll::Ready(Ok(())) => { if prev == buf.remaining() || stream.eof { this.state.shutdown_read(); } Poll::Ready(Ok(())) } Poll::Ready(Err(err)) if err.kind() == io::ErrorKind::UnexpectedEof => { this.state.shutdown_read(); Poll::Ready(Err(err)) } output => output, } } TlsState::ReadShutdown | TlsState::FullyShutdown => Poll::Ready(Ok(())), #[cfg(feature = "early-data")] s => unreachable!("server TLS can not hit this state: {:?}", s), } } } impl AsyncWrite for TlsStream where IO: AsyncRead + AsyncWrite + Unpin, { /// Note: that it does not guarantee the final data to be sent. /// To be cautious, you must manually call `flush`. fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); stream.as_mut_pin().poll_write(cx, buf) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); stream.as_mut_pin().poll_flush(cx) } fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.state.writeable() { self.session.send_close_notify(); self.state.shutdown_write(); } let this = self.get_mut(); let mut stream = Stream::new(&mut this.io, &mut this.session).set_eof(!this.state.readable()); stream.as_mut_pin().poll_shutdown(cx) } } #[cfg(unix)] impl AsRawFd for TlsStream where IO: AsRawFd, { fn as_raw_fd(&self) -> RawFd { self.get_ref().0.as_raw_fd() } } #[cfg(windows)] impl AsRawSocket for TlsStream where IO: AsRawSocket, { fn as_raw_socket(&self) -> RawSocket { self.get_ref().0.as_raw_socket() } } tokio-rustls-0.24.1/tests/badssl.rs000064400000000000000000000054701046102023000153550ustar 00000000000000use std::convert::TryFrom; use std::io; use std::net::ToSocketAddrs; use std::sync::Arc; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; use tokio_rustls::{ client::TlsStream, rustls::{self, ClientConfig, OwnedTrustAnchor}, TlsConnector, }; async fn get( config: Arc, domain: &str, port: u16, ) -> io::Result<(TlsStream, String)> { let connector = TlsConnector::from(config); let input = format!("GET / HTTP/1.0\r\nHost: {}\r\n\r\n", domain); let addr = (domain, port).to_socket_addrs()?.next().unwrap(); let domain = rustls::ServerName::try_from(domain).unwrap(); let mut buf = Vec::new(); let stream = TcpStream::connect(&addr).await?; let mut stream = connector.connect(domain, stream).await?; stream.write_all(input.as_bytes()).await?; stream.flush().await?; stream.read_to_end(&mut buf).await?; Ok((stream, String::from_utf8(buf).unwrap())) } #[tokio::test] async fn test_tls12() -> io::Result<()> { let mut root_store = rustls::RootCertStore::empty(); root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) })); let config = rustls::ClientConfig::builder() .with_safe_default_cipher_suites() .with_safe_default_kx_groups() .with_protocol_versions(&[&rustls::version::TLS12]) .unwrap() .with_root_certificates(root_store) .with_no_client_auth(); let config = Arc::new(config); let domain = "tls-v1-2.badssl.com"; let (_, output) = get(config.clone(), domain, 1012).await?; assert!( output.contains("tls-v1-2.badssl.com"), "failed badssl test, output: {}", output ); Ok(()) } #[ignore] #[should_panic] #[test] fn test_tls13() { unimplemented!("todo https://github.com/chromium/badssl.com/pull/373"); } #[tokio::test] async fn test_modern() -> io::Result<()> { let mut root_store = rustls::RootCertStore::empty(); root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) })); let config = rustls::ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(); let config = Arc::new(config); let domain = "mozilla-modern.badssl.com"; let (_, output) = get(config.clone(), domain, 443).await?; assert!( output.contains("mozilla-modern.badssl.com"), "failed badssl test, output: {}", output ); Ok(()) } tokio-rustls-0.24.1/tests/early-data.rs000064400000000000000000000125311046102023000161240ustar 00000000000000#![cfg(feature = "early-data")] use futures_util::{future, future::Future, ready}; use rustls::RootCertStore; use std::convert::TryFrom; use std::io::{self, BufRead, BufReader, Cursor}; use std::net::SocketAddr; use std::pin::Pin; use std::process::{Child, Command, Stdio}; use std::sync::Arc; use std::task::{Context, Poll}; use std::thread; use std::time::Duration; use tokio::io::{split, AsyncRead, AsyncWriteExt, ReadBuf}; use tokio::net::TcpStream; use tokio::sync::oneshot; use tokio::time::sleep; use tokio_rustls::{ client::TlsStream, rustls::{self, ClientConfig, OwnedTrustAnchor}, TlsConnector, }; struct Read1(T); impl Future for Read1 { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut buf = [0]; let mut buf = ReadBuf::new(&mut buf); ready!(Pin::new(&mut self.0).poll_read(cx, &mut buf))?; if buf.filled().is_empty() { Poll::Ready(Ok(())) } else { cx.waker().wake_by_ref(); Poll::Pending } } } async fn send( config: Arc, addr: SocketAddr, data: &[u8], ) -> io::Result> { let connector = TlsConnector::from(config).early_data(true); let stream = TcpStream::connect(&addr).await?; let domain = rustls::ServerName::try_from("foobar.com").unwrap(); let stream = connector.connect(domain, stream).await?; let (mut rd, mut wd) = split(stream); let (notify, wait) = oneshot::channel(); let j = tokio::spawn(async move { // read to eof // // see https://www.mail-archive.com/openssl-users@openssl.org/msg84451.html let mut read_task = Read1(&mut rd); let mut notify = Some(notify); // read once, then write // // this is a regression test, see https://github.com/tokio-rs/tls/issues/54 future::poll_fn(|cx| { let ret = Pin::new(&mut read_task).poll(cx)?; assert_eq!(ret, Poll::Pending); notify.take().unwrap().send(()).unwrap(); Poll::Ready(Ok(())) as Poll> }) .await?; match read_task.await { Ok(()) => (), Err(ref err) if err.kind() == io::ErrorKind::UnexpectedEof => (), Err(err) => return Err(err), } Ok(rd) as io::Result<_> }); wait.await.unwrap(); wd.write_all(data).await?; wd.flush().await?; wd.shutdown().await?; let rd: tokio::io::ReadHalf<_> = j.await??; Ok(rd.unsplit(wd)) } struct DropKill(Child); impl Drop for DropKill { fn drop(&mut self) { self.0.kill().unwrap(); } } async fn wait_for_server(addr: &str) { let tries = 10; for i in 0..tries { if let Ok(_) = TcpStream::connect(addr).await { return; } sleep(Duration::from_millis(i * 100)).await; } panic!("failed to connect to {:?} after {} tries", addr, tries) } #[tokio::test] async fn test_0rtt() -> io::Result<()> { let server_port = 12354; let mut handle = Command::new("openssl") .arg("s_server") .arg("-early_data") .arg("-tls1_3") .args(["-cert", "./tests/end.cert"]) .args(["-key", "./tests/end.rsa"]) .args(["-port", &server_port.to_string()]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .map(DropKill)?; // wait openssl server wait_for_server(format!("127.0.0.1:{}", server_port).as_str()).await; let mut chain = BufReader::new(Cursor::new(include_str!("end.chain"))); let certs = rustls_pemfile::certs(&mut chain).unwrap(); let trust_anchors = certs.iter().map(|cert| { let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) }); let mut root_store = RootCertStore::empty(); root_store.add_server_trust_anchors(trust_anchors); let mut config = rustls::ClientConfig::builder() .with_safe_default_cipher_suites() .with_safe_default_kx_groups() .with_protocol_versions(&[&rustls::version::TLS13]) .unwrap() .with_root_certificates(root_store) .with_no_client_auth(); config.enable_early_data = true; let config = Arc::new(config); let addr = SocketAddr::from(([127, 0, 0, 1], server_port)); // workaround: write to openssl s_server standard input periodically, to // get it unstuck on Windows let stdin = handle.0.stdin.take().unwrap(); thread::spawn(move || { let mut stdin = stdin; loop { thread::sleep(std::time::Duration::from_secs(5)); std::io::Write::write_all(&mut stdin, b"\n").unwrap(); } }); let io = send(config.clone(), addr, b"hello").await?; assert!(!io.get_ref().1.is_early_data_accepted()); let io = send(config, addr, b"world!").await?; assert!(io.get_ref().1.is_early_data_accepted()); let stdout = handle.0.stdout.as_mut().unwrap(); let mut lines = BufReader::new(stdout).lines(); let has_msg1 = lines.by_ref().any(|line| line.unwrap().contains("hello")); let has_msg2 = lines.by_ref().any(|line| line.unwrap().contains("world!")); assert!(has_msg1 && has_msg2); Ok(()) } tokio-rustls-0.24.1/tests/end.cert000064400000000000000000000035571046102023000151700ustar 00000000000000-----BEGIN CERTIFICATE----- MIIFUDCCAzigAwIBAgIJAJe5BNyTuxCoMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwHhcNMjIwNzI4MTAyMjU1WhcNMjMwNzI4MTAyMjU1WjBa MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDDApmb29iYXIuY29tMIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1KWn07gxqHJyLMdxjfqggubG76Us WUN+I2dlyCV/P18sFb7phY4u3AAlI+RDUr26Klb5K3DJEApdyzRrfhBxTDSlVEI1 7incvH6eQiy58z4k5jDV9WMbPsYvZ0QEUZzZWWJhmmRFgSTS0rkV4S9JTv39fbwY eDOVrJSKSOWSoKT6aq1y7DrRP1t0xBvXbTZVJXz78hUAvMz0QLiH4R7GI70ZM/bE 4ldbME4tqN74Rsno2AX9QsymF6Bu9GDGXXT/Z0UZ5E9imHWx+lQvPAmQDI8XE55B OGo4LizUB6w4zV7mMrVX4gz0UFZNwlugLkWsC8yXhE2m/oQP5qkh+KvqyfWuPmjw olzpo26M6uPYy+Xywp760hsp4cZZ+CfdTL3bTgeiEgLm/OQd+vvbbtM/u7lRpGgZ wxMQawRH0o8FL4nDMq+P+SMydMBMfgzIZP6Wdn/qkHBLjV22Y4/GcnxwVS1NIKJm zfFRnyp0LqaP8KDjZhtif6IiqtejANqeDZQL8ZLYb3K04kkHSqi9cLoNcxtgsyc0 Uh7TPZ6yP7wcOoEiKewA/VHRCctsP0ZAScixQrUqFMF0MbKu1XU0vF5gZslV13W6 3nbVHFWplCz6lCEebO2XmMaqZhX8aYq3rSGcUyCU61WxWrTF2RruxsaZit/jCF7s l+i8r0elruwpiZECAwEAAaMuMCwwEwYDVR0lBAwwCgYIKwYBBQUHAwEwFQYDVR0R BA4wDIIKZm9vYmFyLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEALF2VLSijCs3aAcLp UbtpKW7xzkAwCT5wnLwqrEq7fdPiKX0RRQPF9+ClDPsvvt9XrhTr/UZ/A0vufknh Jhthq2tj/QvvgasxIgw76yJLAxuWuCNpg512Xq52XtkSba73Z9fEbz27LstQ4LHd EjA23Q7mqvLf/cm/nJHAtSuglegCBjAsLdz3p/6VdZOn49dLuXpiPw/uu43sMQsb gzTBCXv01yR4p6GeI4vxDKiryQEHhq0ipcQWnhm6HTeGM8mgswTyV7iS0zhbcsZO BZKo8onGDy5kMqi5tV5sZTtT5jpcd2aZDVhdX1VtfLjS8QwPkIFyDQyAnusRtfOa PC1oiz+z9JgkUEqVx5m1HZTvDuJ7jw3erGqAo/fmitHk4LQYDFtGbKKUDV120N0l AqGqvz6CW1ZsXvxf1lkgoYluUzBJJZN0poV65wQhBSG//ZmHBPMWE1cxs0oHWlMB kBqvgR4PLdO0h6NbD0FcttdIwMa9SAHJXe5Ba0sg3OWs+dp494kYXr2GRmYeGtuW UfEsK1iQAPhAv2Rq3BUkX2YPZyzh57WolDdB+R4k/aGgbGpcTabyZbdaU9+clOow NU4iTyn2BOm1vX6PMmMBQOiV+O8SpwsIdonQ/84VCI2mjJeGl3kX5akLMwyz4rpn MkK20x8h4oIYiTMT349sj/73v0I= -----END CERTIFICATE----- tokio-rustls-0.24.1/tests/end.chain000064400000000000000000000074021046102023000153060ustar 00000000000000-----BEGIN CERTIFICATE----- MIIFUDCCAzigAwIBAgIJAJe5BNyTuxCoMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwHhcNMjIwNzI4MTAyMjU1WhcNMjMwNzI4MTAyMjU1WjBa MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRMwEQYDVQQDDApmb29iYXIuY29tMIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1KWn07gxqHJyLMdxjfqggubG76Us WUN+I2dlyCV/P18sFb7phY4u3AAlI+RDUr26Klb5K3DJEApdyzRrfhBxTDSlVEI1 7incvH6eQiy58z4k5jDV9WMbPsYvZ0QEUZzZWWJhmmRFgSTS0rkV4S9JTv39fbwY eDOVrJSKSOWSoKT6aq1y7DrRP1t0xBvXbTZVJXz78hUAvMz0QLiH4R7GI70ZM/bE 4ldbME4tqN74Rsno2AX9QsymF6Bu9GDGXXT/Z0UZ5E9imHWx+lQvPAmQDI8XE55B OGo4LizUB6w4zV7mMrVX4gz0UFZNwlugLkWsC8yXhE2m/oQP5qkh+KvqyfWuPmjw olzpo26M6uPYy+Xywp760hsp4cZZ+CfdTL3bTgeiEgLm/OQd+vvbbtM/u7lRpGgZ wxMQawRH0o8FL4nDMq+P+SMydMBMfgzIZP6Wdn/qkHBLjV22Y4/GcnxwVS1NIKJm zfFRnyp0LqaP8KDjZhtif6IiqtejANqeDZQL8ZLYb3K04kkHSqi9cLoNcxtgsyc0 Uh7TPZ6yP7wcOoEiKewA/VHRCctsP0ZAScixQrUqFMF0MbKu1XU0vF5gZslV13W6 3nbVHFWplCz6lCEebO2XmMaqZhX8aYq3rSGcUyCU61WxWrTF2RruxsaZit/jCF7s l+i8r0elruwpiZECAwEAAaMuMCwwEwYDVR0lBAwwCgYIKwYBBQUHAwEwFQYDVR0R BA4wDIIKZm9vYmFyLmNvbTANBgkqhkiG9w0BAQsFAAOCAgEALF2VLSijCs3aAcLp UbtpKW7xzkAwCT5wnLwqrEq7fdPiKX0RRQPF9+ClDPsvvt9XrhTr/UZ/A0vufknh Jhthq2tj/QvvgasxIgw76yJLAxuWuCNpg512Xq52XtkSba73Z9fEbz27LstQ4LHd EjA23Q7mqvLf/cm/nJHAtSuglegCBjAsLdz3p/6VdZOn49dLuXpiPw/uu43sMQsb gzTBCXv01yR4p6GeI4vxDKiryQEHhq0ipcQWnhm6HTeGM8mgswTyV7iS0zhbcsZO BZKo8onGDy5kMqi5tV5sZTtT5jpcd2aZDVhdX1VtfLjS8QwPkIFyDQyAnusRtfOa PC1oiz+z9JgkUEqVx5m1HZTvDuJ7jw3erGqAo/fmitHk4LQYDFtGbKKUDV120N0l AqGqvz6CW1ZsXvxf1lkgoYluUzBJJZN0poV65wQhBSG//ZmHBPMWE1cxs0oHWlMB kBqvgR4PLdO0h6NbD0FcttdIwMa9SAHJXe5Ba0sg3OWs+dp494kYXr2GRmYeGtuW UfEsK1iQAPhAv2Rq3BUkX2YPZyzh57WolDdB+R4k/aGgbGpcTabyZbdaU9+clOow NU4iTyn2BOm1vX6PMmMBQOiV+O8SpwsIdonQ/84VCI2mjJeGl3kX5akLMwyz4rpn MkK20x8h4oIYiTMT349sj/73v0I= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFajCCA1KgAwIBAgIJANSWIN+/hifHMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwHhcNMjIwNzI4MTAyMjU0WhcNMjMwNzI4MTAyMjU0WjBF MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC CgKCAgEA/BOyu73gyVuKs6IIUP1wfhNSE/yeTkCaqZ33M1Nv3WkBFoCXS4n3oLbX 1XmUdmC1ECsgIIM9DBinEfEGU3AAAYzuEieOuCESNw5MAsB4Ft2EvJe/0n5WiQCy EZ9PLxY4pWRb8C95AZH25sEVlZ8PZsdpQs28geeYtKJjLMrwA0wXrhkxMizZXz0G 27ETciLv7uHVaC66bglGVzD0E5zhPOrj5wVXSqPuqbohun/DpT51lbd3y15cy8+k TeaFI8vEpmYnOLm0LpdErHtdKqAgQ//yO4kSA7YmOWhy4T94ZF5uhxXL7bCJ9I3y 9tx4mnZ7WpFyDtOEwn463f2u0SWyT6gb2i/RyC7jpbtyxSkPenzcXEOIcGMuDsPt Nu1Y3X98HzdBkc8q3jPXKuHDgQJuH4gcEQFsyNCS6IT1h3EEJCHHOLn/OrRuSgv5 oIM6+kqOKeTSGYCSBTYbhaCQQxDzPtlpFu/u236Gv0F/btEio/OW0SZmsUFmdhhy o2E9nquETuPd9Ku9jkSI81LVDFMzKdipX/YS51JKSKnPK60EFoEb4Gbmr6Hex4rF sE58Jj56HMjGeJRSyI5BbthUgsWk2qBIFz0zgqQ1jTPCueQtpsFBN8XWP+gRdViJ 5xxZyDSSWx6l4Exciuen/YF5tio/05S2F2xkeKdgjVxUsYrW9UcCAwEAAaNdMFsw DAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAcYwHQYDVR0OBBYEFMghuWQPHguF/7Yl GHVjCMgXtWM6MB8GA1UdIwQYMBaAFMghuWQPHguF/7YlGHVjCMgXtWM6MA0GCSqG SIb3DQEBCwUAA4ICAQA5MXm4h6ocl4W28DS/PHj/I+AZ4NGP9M0bsUSyejFMV/E6 0jBkkmC7HUTXQXv3C6nl0XI1nnAC8v3ZdOvIlaEbhDV+I5ovHHDQc9qsHM3bg4C6 XTftYL2jwIxu6Zc3BA12Zna4ATIIuzF33aKI+SxPYdLm+rKdjl5vg7EthYPJf8W+ gUBKDmMtsVvPj5bzjm53mPwCZURAMige9msTtOljw1xaIHfiHQRr3AqD2a1SrOZn XsVSz6rV0p2DpgOLm6n0nsFQJ2xfVZ9hY7rmrAkVUYgHjJv5occmydWoQ3UVusCf CpsXjFXp1AM6R3YIQXDNqwpsOJ8fQdc3y9+RNJXTenDDx4QH8SLijagvyJZujeiD clIWwAzczbKgACA6UlR5lYm6lkxYN8z0xcG8MqyCZW1I4a/TIbdkFvuCIo5f7Xqv llw0c78GIrbB30zZxnJGndglCqqWcXojHJ+LZAgjwv4iivCvDg2pQ6RKK+BWuRIL Moa0k1tc8nLcwHxb76FIXttaLN9gYL4cQWJmtORNsuO5HSMT9dU3vfQjzZ5ElHuW y2e5ZR+1jr45t6QSUAHrCT5I4GAx6jjDhyF2o9PGg1uY2/JFpemMx1qkPCBBAWSy qClGAAD9qhq+aj8UuFt8tM1sBYFcBgjPSjC+ncSDZVEGhk8WAC8ciYRiY/wirQ== -----END CERTIFICATE----- tokio-rustls-0.24.1/tests/end.rsa000064400000000000000000000062531046102023000150140ustar 00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEA1KWn07gxqHJyLMdxjfqggubG76UsWUN+I2dlyCV/P18sFb7p hY4u3AAlI+RDUr26Klb5K3DJEApdyzRrfhBxTDSlVEI17incvH6eQiy58z4k5jDV 9WMbPsYvZ0QEUZzZWWJhmmRFgSTS0rkV4S9JTv39fbwYeDOVrJSKSOWSoKT6aq1y 7DrRP1t0xBvXbTZVJXz78hUAvMz0QLiH4R7GI70ZM/bE4ldbME4tqN74Rsno2AX9 QsymF6Bu9GDGXXT/Z0UZ5E9imHWx+lQvPAmQDI8XE55BOGo4LizUB6w4zV7mMrVX 4gz0UFZNwlugLkWsC8yXhE2m/oQP5qkh+KvqyfWuPmjwolzpo26M6uPYy+Xywp76 0hsp4cZZ+CfdTL3bTgeiEgLm/OQd+vvbbtM/u7lRpGgZwxMQawRH0o8FL4nDMq+P +SMydMBMfgzIZP6Wdn/qkHBLjV22Y4/GcnxwVS1NIKJmzfFRnyp0LqaP8KDjZhti f6IiqtejANqeDZQL8ZLYb3K04kkHSqi9cLoNcxtgsyc0Uh7TPZ6yP7wcOoEiKewA /VHRCctsP0ZAScixQrUqFMF0MbKu1XU0vF5gZslV13W63nbVHFWplCz6lCEebO2X mMaqZhX8aYq3rSGcUyCU61WxWrTF2RruxsaZit/jCF7sl+i8r0elruwpiZECAwEA AQKCAgBkzlMNDxibXgW6LKIsSE+nPne4S3kGp/Q4HGAnzX7RL/DaLjLN2WVlbbwt k5xGsSJ7x9c/PKxUKlXkauk6Tdkn4x0tIGYojTfTM8MaY9GS+jbL2QLU3TKhQ5pd PeU+OzjeIsFaS4aXfjljI9K3eY8rH0T4Qh7nfKzLr28Ot6YW+Z6sy6TbT9a6TkTA YuJxJ3yd1O4rCMHcvtxbgh8IHWqYrqAv/h+/nOHhuG30zB6ukDENl2lELXffY3K1 7tqam0goUJ3BmdCHreO9ZAMwVuguy/aImlEYyWHwodex+3bCBObjywvqYXHAU+lw 5ba9uNGilk6NrbIonbpcAnnURMt8LCXXzU7UlbUfk1j/2FcKRnvMj6szsX+kl3fx qjjwbCip8W67Abu26u80egA0XpGXlrv8PRqOU21SyezMlUCGwaIo6suFx8CKfDp7 XBy7iXgk9a7IVd6mRBfNjy77Upm7VaixUNwfl18GDM3mpbXnEUO3oGCknXmz6sBR iSsQQOxu8D3alPLKJ79MRwrv0IkpecXh1Y2F9LPZhlGVAWU9kToTAD+2I1H9/GTX 7pG1t/RYE8mZmpGlyv+uvk74RawELUVgwIUJcK1GQ1+yWftv9Q7FgUVO9iK5ZT9H m8iQGouGGdMkG8RGd0vrXmWJKvttUCDIfNdfVXte1zovIwoU8QKCAQEA/r/T3rZU FIrjtzDvMyKazNR5vHjBAnv7EqbKaO06hUIJ0xJHK23ixIA8zPYIk/wTdIemWFp9 prAiGTHDiFFfYWa4mzo/NDWoinvJ+hXEWBsw3YvUI+uOeCzBGmENsQJ8fzsMHUqS G5g86UASPzVU4ep4I5UWbvqN5h7bcf+BKukB0xDc7x8gyp3PAC2aBgq2a3eb8Uj/ beSmQ/sL22FCcLVjHuUGJ62erh44iqEm09ifyISCMWb5dmmlTdHYZPvBc40XAkHx 6aoVsQCNTjuBon2yJMeps8U+VFjeD1VAJyA3yUVM0YI5ptYum3/XKVNFg/tXtCww r5rqm39DcBtcLwKCAQEA1bDpzitkbWctEtGGaF+u7TtDrndcLGUTTdlnJO1pnzGw XNVNIOsa7PjhzY9J2IMN8o60t3UVQKQhkf+w8QnfGosgjWSnBT/R33wyjU7cUpnf Lf2V/lWL2AHLAfdA/LIOvUPTPxdq+MWiGXy57C2UI5vxMoB5Zg+L4gXoOQUskDA4 dAfz66lGiEFN/VdjP6/b8STAXaQqoF8IQd8GGtpr3XjFqVNJHG0KSpn2ns0UknoK hDc+ev8TrXFOoBrAvBXSxrmjscB/NYnC+JfXKcgN6LyG1tR6/lBxnrIbKYKxxfMe sR3d39ezbuJ+NqZ6JAr5OaRyW/p6Kwkk/jZBDhdGPwKCAQEAoXF2VvUbse0XRRhY 7wImMlsRTzUDtIxX4Ekdi4OUC7F/QmmZ+tbn2Hogjg/5/bbJqZbx/5pReRq0cV0N Os2+8Z5ErfMq7O54glLS/I9g4ClTPYQJDD6TCmvqcMpDAAPAXG1STxncIGrJZ1S4 e6BTy9xpCLvJ/MkCtmyly8gN1uDVzCoqqcwDXwTFk6pSqzOMyeyCQlZtsfouuHRX 3k7z6TO6vnCMBwdLwbLbSFe6oJTvJgd77s0AmV94aCntxomZ3p3yj/a01c15c7QS 2RpzHEQmapKyZIBC8PdkuzjesC0FzaMCfN/Xo3inDtrkw4bHTk4yFbfPnupBdgwf 8+MS9wKCAQBjUYKZrFlU7+tnH7MUt8QZxr3CTP+uMpwyRqVF6IJ61yFdRFZAwoUV NufcHoj39JF69xDPY7+o96ASt4CJx0jGkXtjvDBUpEDrWlI0kz6btaChQ4d/WktQ 7iRomX6+9BMdrHR2km/JiDG7HtlbCCQeGNCV5FiIMxmUx6ITPnBj48WZSEj6cwax NYkGAqPCaf4Tqj0uSKr3NrQjyYCQ4ovXt9ZGyMrmR1fNLJoPXMn++nIA2ZxUllGS /2LHzyddQ9dfPdDzQMDfJVRVLl12seClF1qkZsVzhfgJBkbRmEj/8+uD7pm8/AgN tX9r7xw9vEvxmpj8XwKBFhL3hEfgQLkJAoIBAQDsw6FU4JthWAV0GLWwMIXSZ4Mc uNio8154wAJMrAS9384ZAmDTwJy5aTo8AFREzQXpusLQXWioLiOVamvda8jy5OFH r+IDnMiagT8gfHnLD+pVgq2/77zO1ioX3iuDJRN4l7IRWlNdaBvmEjZk2T52hPN+ taD+aWnep6GnWFEYPt6obWfomm7TfIpMnuQ6cSxRiOyNyTGfej/8jJQZnLr1E3f4 sFjuGLWJObeEgb+hsVavo9o9PthNnpYF5rgrOE67FxKu7OBIS7KM8GfMFavBzAAB tHatAVyLj9B3VWgdrYIPn3/FnV8AiBphu9tFf0lRwCya8wu3FQ03WDhn0t1E -----END RSA PRIVATE KEY----- tokio-rustls-0.24.1/tests/test.rs000064400000000000000000000200021046102023000150500ustar 00000000000000use futures_util::future::TryFutureExt; use lazy_static::lazy_static; use rustls::{ClientConfig, OwnedTrustAnchor}; use rustls_pemfile::{certs, rsa_private_keys}; use std::convert::TryFrom; use std::io::{BufReader, Cursor, ErrorKind}; use std::net::SocketAddr; use std::sync::mpsc::channel; use std::sync::Arc; use std::time::Duration; use std::{io, thread}; use tokio::io::{copy, split, AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::oneshot; use tokio::{runtime, time}; use tokio_rustls::{LazyConfigAcceptor, TlsAcceptor, TlsConnector}; const CERT: &str = include_str!("end.cert"); const CHAIN: &[u8] = include_bytes!("end.chain"); const RSA: &str = include_str!("end.rsa"); lazy_static! { static ref TEST_SERVER: (SocketAddr, &'static str, &'static [u8]) = { let cert = certs(&mut BufReader::new(Cursor::new(CERT))) .unwrap() .drain(..) .map(rustls::Certificate) .collect(); let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap(); let mut keys = keys.drain(..).map(rustls::PrivateKey); let config = rustls::ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(cert, keys.next().unwrap()) .unwrap(); let acceptor = TlsAcceptor::from(Arc::new(config)); let (send, recv) = channel(); thread::spawn(move || { let runtime = runtime::Builder::new_current_thread() .enable_io() .build() .unwrap(); let runtime = Arc::new(runtime); let runtime2 = runtime.clone(); let done = async move { let addr = SocketAddr::from(([127, 0, 0, 1], 0)); let listener = TcpListener::bind(&addr).await?; send.send(listener.local_addr()?).unwrap(); loop { let (stream, _) = listener.accept().await?; let acceptor = acceptor.clone(); let fut = async move { let stream = acceptor.accept(stream).await?; let (mut reader, mut writer) = split(stream); copy(&mut reader, &mut writer).await?; Ok(()) as io::Result<()> } .unwrap_or_else(|err| eprintln!("server: {:?}", err)); runtime2.spawn(fut); } } .unwrap_or_else(|err: io::Error| eprintln!("server: {:?}", err)); runtime.block_on(done); }); let addr = recv.recv().unwrap(); (addr, "foobar.com", CHAIN) }; } fn start_server() -> &'static (SocketAddr, &'static str, &'static [u8]) { &TEST_SERVER } async fn start_client(addr: SocketAddr, domain: &str, config: Arc) -> io::Result<()> { const FILE: &[u8] = include_bytes!("../README.md"); let domain = rustls::ServerName::try_from(domain).unwrap(); let config = TlsConnector::from(config); let mut buf = vec![0; FILE.len()]; let stream = TcpStream::connect(&addr).await?; let mut stream = config.connect(domain, stream).await?; stream.write_all(FILE).await?; stream.flush().await?; stream.read_exact(&mut buf).await?; assert_eq!(buf, FILE); Ok(()) } #[tokio::test] async fn pass() -> io::Result<()> { let (addr, domain, chain) = start_server(); // TODO: not sure how to resolve this right now but since // TcpStream::bind now returns a future it creates a race // condition until its ready sometimes. use std::time::*; tokio::time::sleep(Duration::from_secs(1)).await; let chain = certs(&mut std::io::Cursor::new(*chain)).unwrap(); let mut root_store = rustls::RootCertStore::empty(); root_store.add_server_trust_anchors(chain.iter().map(|cert| { let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) })); let config = rustls::ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(); let config = Arc::new(config); start_client(*addr, domain, config).await?; Ok(()) } #[tokio::test] async fn fail() -> io::Result<()> { let (addr, domain, chain) = start_server(); let chain = certs(&mut std::io::Cursor::new(*chain)).unwrap(); let mut root_store = rustls::RootCertStore::empty(); root_store.add_server_trust_anchors(chain.iter().map(|cert| { let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) })); let config = rustls::ClientConfig::builder() .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(); let config = Arc::new(config); assert_ne!(domain, &"google.com"); let ret = start_client(*addr, "google.com", config).await; assert!(ret.is_err()); Ok(()) } #[tokio::test] async fn test_lazy_config_acceptor() -> io::Result<()> { let (sconfig, cconfig) = utils::make_configs(); use std::convert::TryFrom; let (cstream, sstream) = tokio::io::duplex(1200); let domain = rustls::ServerName::try_from("foobar.com").unwrap(); tokio::spawn(async move { let connector = crate::TlsConnector::from(cconfig); let mut client = connector.connect(domain, cstream).await.unwrap(); client.write_all(b"hello, world!").await.unwrap(); let mut buf = Vec::new(); client.read_to_end(&mut buf).await.unwrap(); }); let acceptor = LazyConfigAcceptor::new(rustls::server::Acceptor::default(), sstream); let start = acceptor.await.unwrap(); let ch = start.client_hello(); assert_eq!(ch.server_name(), Some("foobar.com")); assert_eq!( ch.alpn() .map(|protos| protos.collect::>()) .unwrap_or_default(), Vec::<&[u8]>::new() ); let mut stream = start.into_stream(sconfig).await.unwrap(); let mut buf = [0; 13]; stream.read_exact(&mut buf).await.unwrap(); assert_eq!(&buf[..], b"hello, world!"); stream.write_all(b"bye").await.unwrap(); Ok(()) } // This test is a follow-up from https://github.com/tokio-rs/tls/issues/85 #[tokio::test] async fn lazy_config_acceptor_eof() { let buf = Cursor::new(Vec::new()); let acceptor = LazyConfigAcceptor::new(rustls::server::Acceptor::default(), buf); let accept_result = match time::timeout(Duration::from_secs(3), acceptor).await { Ok(res) => res, Err(_elapsed) => panic!("timeout"), }; match accept_result { Ok(_) => panic!("accepted a connection from zero bytes of data"), Err(e) if e.kind() == ErrorKind::UnexpectedEof => {} Err(e) => panic!("unexpected error: {:?}", e), } } #[tokio::test] async fn lazy_config_acceptor_take_io() -> Result<(), rustls::Error> { let (mut cstream, sstream) = tokio::io::duplex(1200); let (tx, rx) = oneshot::channel(); tokio::spawn(async move { cstream.write_all(b"hello, world!").await.unwrap(); let mut buf = Vec::new(); cstream.read_to_end(&mut buf).await.unwrap(); tx.send(buf).unwrap(); }); let acceptor = LazyConfigAcceptor::new(rustls::server::Acceptor::default(), sstream); futures_util::pin_mut!(acceptor); if (acceptor.as_mut().await).is_ok() { panic!("Expected Err(err)"); } let server_msg = b"message from server"; let some_io = acceptor.take_io(); assert!(some_io.is_some(), "Expected Some(io)"); some_io.unwrap().write_all(server_msg).await.unwrap(); assert_eq!(rx.await.unwrap(), server_msg); assert!( acceptor.take_io().is_none(), "Should not be able to take twice" ); Ok(()) } // Include `utils` module include!("utils.rs"); tokio-rustls-0.24.1/tests/utils.rs000064400000000000000000000033111046102023000152350ustar 00000000000000mod utils { use std::io::{BufReader, Cursor}; use std::sync::Arc; use rustls::{ClientConfig, OwnedTrustAnchor, PrivateKey, RootCertStore, ServerConfig}; use rustls_pemfile::{certs, rsa_private_keys}; #[allow(dead_code)] pub fn make_configs() -> (Arc, Arc) { const CERT: &str = include_str!("end.cert"); const CHAIN: &str = include_str!("end.chain"); const RSA: &str = include_str!("end.rsa"); let cert = certs(&mut BufReader::new(Cursor::new(CERT))) .unwrap() .drain(..) .map(rustls::Certificate) .collect(); let mut keys = rsa_private_keys(&mut BufReader::new(Cursor::new(RSA))).unwrap(); let mut keys = keys.drain(..).map(PrivateKey); let sconfig = ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(cert, keys.next().unwrap()) .unwrap(); let mut client_root_cert_store = RootCertStore::empty(); let mut chain = BufReader::new(Cursor::new(CHAIN)); let certs = certs(&mut chain).unwrap(); client_root_cert_store.add_server_trust_anchors(certs.iter().map(|cert| { let ta = webpki::TrustAnchor::try_from_cert_der(&cert[..]).unwrap(); OwnedTrustAnchor::from_subject_spki_name_constraints( ta.subject, ta.spki, ta.name_constraints, ) })); let cconfig = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(client_root_cert_store) .with_no_client_auth(); (Arc::new(sconfig), Arc::new(cconfig)) } }