tokio-rustls-0.26.1/.cargo_vcs_info.json0000644000000001360000000000100136010ustar { "git": { "sha1": "93dbdf0a0cec5be58ba24181ae2a9b523ed1be85" }, "path_in_vcs": "" }tokio-rustls-0.26.1/.gitignore000064400000000000000000000000441046102023000143570ustar 00000000000000/target **/*.rs.bk .DS_Store /.idea tokio-rustls-0.26.1/Cargo.lock0000644000000574340000000000100115710ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "addr2line" version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "argh" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" dependencies = [ "argh_derive", "argh_shared", ] [[package]] name = "argh_derive" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" dependencies = [ "argh_shared", "proc-macro2", "quote", "syn", ] [[package]] name = "argh_shared" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" dependencies = [ "serde", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-fips-sys" version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec4795bbabc13643a8b3532184041ab41dec5740046aa15734428219cb9a0bfc" dependencies = [ "bindgen", "cmake", "dunce", "fs_extra", "libc", "paste", ] [[package]] name = "aws-lc-rs" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdd82dba44d209fddb11c190e0a94b78651f95299598e472215667417a03ff1d" dependencies = [ "aws-lc-fips-sys", "aws-lc-sys", "mirai-annotations", "paste", "zeroize", ] [[package]] name = "aws-lc-sys" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" dependencies = [ "bindgen", "cc", "cmake", "dunce", "fs_extra", "libc", "paste", ] [[package]] name = "backtrace" version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-targets", ] [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bindgen" version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags", "cexpr", "clang-sys", "itertools", "lazy_static", "lazycell", "log", "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", "which", ] [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bytes" version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cc" version = "1.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58e804ac3194a48bb129643eb1d62fcc20d18c6b8c181704489353d13120bcd1" dependencies = [ "jobserver", "libc", "shlex", ] [[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", "libloading", ] [[package]] name = "cmake" version = "0.1.51" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" dependencies = [ "cc", ] [[package]] name = "deranged" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "errno" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fs_extra" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-macro" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "home" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ "windows-sys", ] [[package]] name = "itertools" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "jobserver" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libloading" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", "windows-targets", ] [[package]] name = "linux-raw-sys" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", "wasi", "windows-sys", ] [[package]] name = "mirai-annotations" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "object" version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "parking_lot" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-targets", ] [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem" version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ "base64", "serde", ] [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "prettyplease" version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ "proc-macro2", "syn", ] [[package]] name = "proc-macro2" version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "rcgen" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54077e1872c46788540de1ea3d7f4ccb1983d12f9aa909b234468676c1a36779" dependencies = [ "pem", "ring", "rustls-pki-types", "time", "yasna", ] [[package]] name = "redox_syscall" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] [[package]] name = "regex" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "ring" version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", "getrandom", "libc", "spin", "untrusted", "windows-sys", ] [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "rustls" version = "0.23.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fbb44d7acc4e873d613422379f69f237a1b141928c02f6bc6ccfddddc2d7993" dependencies = [ "aws-lc-rs", "log", "once_cell", "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys", ] [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "time" version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "num-conv", "powerfmt", "serde", "time-core", ] [[package]] name = "time-core" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tokio" version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tokio-rustls" version = "0.26.1" dependencies = [ "argh", "futures-util", "lazy_static", "rcgen", "rustls", "tokio", "webpki-roots", ] [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "webpki-roots" version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" dependencies = [ "rustls-pki-types", ] [[package]] name = "which" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", "home", "once_cell", "rustix", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "yasna" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ "time", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" tokio-rustls-0.26.1/Cargo.toml0000644000000040370000000000100116030ustar # 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 = "2021" rust-version = "1.70" name = "tokio-rustls" version = "0.26.1" build = false exclude = [ "/.github", "/examples", "/scripts", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false 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 OR Apache-2.0" repository = "https://github.com/rustls/tokio-rustls" [lib] name = "tokio_rustls" path = "src/lib.rs" [[test]] name = "badssl" path = "tests/badssl.rs" [[test]] name = "certs" path = "tests/certs/main.rs" [[test]] name = "early-data" path = "tests/early-data.rs" [[test]] name = "test" path = "tests/test.rs" [[test]] name = "utils" path = "tests/utils.rs" [dependencies.rustls] version = "0.23.15" features = ["std"] default-features = false [dependencies.tokio] version = "1.0" [dev-dependencies.argh] version = "0.1.1" [dev-dependencies.futures-util] version = "0.3.1" [dev-dependencies.lazy_static] version = "1.1" [dev-dependencies.rcgen] version = "0.13" features = ["pem"] [dev-dependencies.tokio] version = "1.0" features = ["full"] [dev-dependencies.webpki-roots] version = "0.26" [features] aws-lc-rs = ["aws_lc_rs"] aws_lc_rs = ["rustls/aws_lc_rs"] default = [ "logging", "tls12", "aws_lc_rs", ] early-data = [] fips = ["rustls/fips"] logging = ["rustls/logging"] ring = ["rustls/ring"] tls12 = ["rustls/tls12"] tokio-rustls-0.26.1/Cargo.toml.orig000064400000000000000000000020521046102023000152570ustar 00000000000000[package] name = "tokio-rustls" version = "0.26.1" license = "MIT OR 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 = "2021" rust-version = "1.70" exclude = ["/.github", "/examples", "/scripts"] [dependencies] rustls = { version = "0.23.15", default-features = false, features = ["std"] } tokio = "1.0" [features] default = ["logging", "tls12", "aws_lc_rs"] aws_lc_rs = ["rustls/aws_lc_rs"] aws-lc-rs = ["aws_lc_rs"] # Alias because Cargo features commonly use `-` early-data = [] fips = ["rustls/fips"] logging = ["rustls/logging"] ring = ["rustls/ring"] tls12 = ["rustls/tls12"] [dev-dependencies] argh = "0.1.1" futures-util = "0.3.1" lazy_static = "1.1" rcgen = { version = "0.13", features = ["pem"] } tokio = { version = "1.0", features = ["full"] } webpki-roots = "0.26" tokio-rustls-0.26.1/LICENSE-APACHE000064400000000000000000000251201046102023000143150ustar 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.26.1/LICENSE-MIT000064400000000000000000000020401046102023000140210ustar 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.26.1/README.md000064400000000000000000000044561046102023000136610ustar 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 rustls_pki_types::ServerName; use std::sync::Arc; use tokio::net::TcpStream; use tokio_rustls::rustls::{ClientConfig, RootCertStore}; use tokio_rustls::TlsConnector; // ... let mut root_cert_store = RootCertStore::empty(); root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); let config = ClientConfig::builder() .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.rs](examples/client.rs). You can run it with: ```sh cargo run --example client -- hsts.badssl.com ``` ### Server Example Program See [examples/server.rs](examples/server.rs). You can run it with: ```sh cargo run --example server -- 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.26.1/src/client.rs000064400000000000000000000207661046102023000150170ustar 00000000000000use std::io; #[cfg(unix)] use std::os::unix::io::{AsRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, RawSocket}; use std::pin::Pin; #[cfg(feature = "early-data")] use std::task::Waker; use std::task::{Context, Poll}; use rustls::ClientConnection; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use crate::common::{IoSession, Stream, TlsState}; /// 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()); #[cfg(feature = "early-data")] { let bufs = [io::IoSlice::new(buf)]; let written = ready!(poll_handle_early_data( &mut this.state, &mut stream, &mut this.early_waker, cx, &bufs ))?; if written != 0 { return Poll::Ready(Ok(written)); } } stream.as_mut_pin().poll_write(cx, buf) } /// Note: that it does not guarantee the final data to be sent. /// To be cautious, you must manually call `flush`. fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[io::IoSlice<'_>], ) -> 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")] { let written = ready!(poll_handle_early_data( &mut this.state, &mut stream, &mut this.early_waker, cx, bufs ))?; if written != 0 { return Poll::Ready(Ok(written)); } } stream.as_mut_pin().poll_write_vectored(cx, bufs) } #[inline] fn is_write_vectored(&self) -> bool { true } 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")] ready!(poll_handle_early_data( &mut this.state, &mut stream, &mut this.early_waker, cx, &[] ))?; 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) } } #[cfg(feature = "early-data")] fn poll_handle_early_data( state: &mut TlsState, stream: &mut Stream, early_waker: &mut Option, cx: &mut Context<'_>, bufs: &[io::IoSlice<'_>], ) -> Poll> where IO: AsyncRead + AsyncWrite + Unpin, { if let TlsState::EarlyData(pos, data) = state { use std::io::Write; // write early data if let Some(mut early_data) = stream.session.early_data() { let mut written = 0; for buf in bufs { if buf.is_empty() { continue; } let len = match early_data.write(buf) { Ok(0) => break, Ok(n) => n, Err(err) => return Poll::Ready(Err(err)), }; written += len; data.extend_from_slice(&buf[..len]); if len < buf.len() { break; } } if written != 0 { return Poll::Ready(Ok(written)); } } // 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 *state = TlsState::Stream; if let Some(waker) = early_waker.take() { waker.wake(); } } Poll::Ready(Ok(0)) } tokio-rustls-0.26.1/src/common/handshake.rs000064400000000000000000000057071046102023000167550ustar 00000000000000use std::future::Future; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::task::{Context, Poll}; use std::{io, mem}; use rustls::server::AcceptedAlert; use rustls::{ConnectionCommon, SideData}; use tokio::io::{AsyncRead, AsyncWrite}; use crate::common::{Stream, SyncWriteAdapter, TlsState}; 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, SendAlert { io: IS::Io, alert: AcceptedAlert, error: io::Error, }, 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, MidHandshake::SendAlert { mut io, mut alert, error, } => loop { match alert.write(&mut SyncWriteAdapter { io: &mut io, cx }) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { *this = MidHandshake::SendAlert { io, error, alert }; return Poll::Pending; } Err(_) | Ok(0) => return Poll::Ready(Err((error, io))), Ok(_) => {} }; }, // 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.26.1/src/common/mod.rs000064400000000000000000000312511046102023000155770ustar 00000000000000use std::io::{self, IoSlice, Read, Write}; use std::ops::{Deref, DerefMut}; use std::pin::Pin; use std::task::{Context, Poll}; use rustls::{ConnectionCommon, SideData}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; mod handshake; pub(crate) use handshake::{IoSession, MidHandshake}; #[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)), }; 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) })?; Poll::Ready(Ok(n)) } pub fn write_io(&mut self, cx: &mut Context) -> Poll> { let mut writer = SyncWriteAdapter { 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(0)) => return Poll::Ready(Err(io::ErrorKind::WriteZero.into())), 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 AsyncRead for Stream<'_, 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 AsyncWrite for Stream<'_, 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_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { if bufs.iter().all(|buf| buf.is_empty()) { return Poll::Ready(Ok(0)); } loop { let mut would_block = false; let written = self.session.writer().write_vectored(bufs)?; 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 (written, would_block) { (0, true) => Poll::Pending, (0, false) => continue, (n, _) => Poll::Ready(Ok(n)), }; } } #[inline] fn is_write_vectored(&self) -> bool { true } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { self.session.writer().flush()?; while self.session.wants_write() { if ready!(self.write_io(cx))? == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } } 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() { if ready!(self.write_io(cx))? == 0 { return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); } } Poll::Ready(match ready!(Pin::new(&mut self.io).poll_shutdown(cx)) { Ok(()) => Ok(()), // When trying to shutdown, not being connected seems fine Err(err) if err.kind() == io::ErrorKind::NotConnected => Ok(()), Err(err) => Err(err), }) } } /// 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 Read for SyncReadAdapter<'_, '_, 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()), } } } /// An adapter that implements a [`Write`] interface for [`AsyncWrite`] types and an /// associated [`Context`]. /// /// Turns `Poll::Pending` into `WouldBlock`. pub struct SyncWriteAdapter<'a, 'b, T> { pub io: &'a mut T, pub cx: &'a mut Context<'b>, } impl SyncWriteAdapter<'_, '_, 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 Write for SyncWriteAdapter<'_, '_, 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)) } } #[cfg(test)] mod test_stream; tokio-rustls-0.26.1/src/common/test_stream.rs000064400000000000000000000244501046102023000173550ustar 00000000000000use std::io::{self, Cursor, Read, Write}; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use futures_util::future::poll_fn; use futures_util::task::noop_waker_ref; use rustls::pki_types::ServerName; use rustls::{ClientConnection, Connection, ServerConnection}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf}; use super::Stream; struct Good<'a>(&'a mut Connection); impl AsyncRead for Good<'_> { 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 AsyncWrite for Good<'_> { 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(())) } } struct Eof; impl AsyncRead for Eof { fn poll_read( self: Pin<&mut Self>, _cx: &mut Context<'_>, _buf: &mut ReadBuf<'_>, ) -> Poll> { Poll::Ready(Ok(())) } } impl AsyncWrite for Eof { fn poll_write( self: Pin<&mut Self>, _cx: &mut Context<'_>, _buf: &[u8], ) -> Poll> { Poll::Ready(Ok(0)) } 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<()> { stream_good_impl(false).await } #[tokio::test] async fn stream_good_vectored() -> io::Result<()> { stream_good_impl(true).await } async fn stream_good_impl(vectored: bool) -> 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!(utils::write(&mut stream, b"Hello World!", vectored).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<()> } #[tokio::test] async fn stream_handshake_write_eof() -> io::Result<()> { let (_, mut client) = make_pair(); let mut io = Eof; let mut stream = Stream::new(&mut io, &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::WriteZero)) ); 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::InvalidData)) ); 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<()> } #[tokio::test] async fn stream_write_zero() -> 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 io = Eof; let mut stream = Stream::new(&mut io, &mut client); stream.write(b"1").await.unwrap(); let result = stream.flush().await; assert_eq!( result.err().map(|e| e.kind()), Some(io::ErrorKind::WriteZero) ); Ok(()) as io::Result<()> } fn make_pair() -> (ServerConnection, ClientConnection) { let (sconfig, cconfig) = utils::make_configs(); let server = ServerConnection::new(Arc::new(sconfig)).unwrap(); let domain = ServerName::try_from("foobar.com").unwrap(); let client = ClientConnection::new(Arc::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.26.1/src/lib.rs000064400000000000000000000442021046102023000142760ustar 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 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}; pub use rustls; use rustls::pki_types::ServerName; use rustls::server::AcceptedAlert; use rustls::{ClientConfig, ClientConnection, CommonState, ServerConfig, ServerConnection}; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; 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; use common::{MidHandshake, TlsState}; pub mod server; /// 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: ServerName<'static>, stream: IO) -> Connect where IO: AsyncRead + AsyncWrite + Unpin, { self.connect_with(domain, stream, |_| ()) } pub fn connect_with(&self, domain: ServerName<'static>, 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, alert: Option<(rustls::Error, AcceptedAlert)>, } impl LazyConfigAcceptor where IO: AsyncRead + AsyncWrite + Unpin, { #[inline] pub fn new(acceptor: rustls::server::Acceptor, io: IO) -> Self { Self { acceptor, io: Some(io), alert: None, } } /// 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); /// tokio::pin!(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", ))) } }; if let Some((err, mut alert)) = this.alert.take() { match alert.write(&mut common::SyncWriteAdapter { io, cx }) { Err(e) if e.kind() == io::ErrorKind::WouldBlock => { this.alert = Some((err, alert)); return Poll::Pending; } Ok(0) | Err(_) => { return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidData, err))) } Ok(_) => { this.alert = Some((err, alert)); continue; } }; } 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) => {} Err((err, alert)) => { this.alert = Some((err, alert)); } } } } } 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, alert)) => { return Accept(MidHandshake::SendAlert { io: self.io, alert, // TODO(eliza): should this really return an `io::Error`? // Probably not... error: io::Error::new(io::ErrorKind::InvalidData, 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::SendAlert { io, .. } => Some(io), 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::SendAlert { io, .. } => Some(io), 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::SendAlert { io, .. } => Some(io), 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::SendAlert { io, .. } => Some(io), 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_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[io::IoSlice<'_>], ) -> Poll> { match self.get_mut() { TlsStream::Client(x) => Pin::new(x).poll_write_vectored(cx, bufs), TlsStream::Server(x) => Pin::new(x).poll_write_vectored(cx, bufs), } } #[inline] fn is_write_vectored(&self) -> bool { match self { TlsStream::Client(x) => x.is_write_vectored(), TlsStream::Server(x) => x.is_write_vectored(), } } #[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.26.1/src/server.rs000064400000000000000000000113631046102023000150400ustar 00000000000000use 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::task::{Context, Poll}; use rustls::ServerConnection; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use crate::common::{IoSession, Stream, TlsState}; /// 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) } /// Note: that it does not guarantee the final data to be sent. /// To be cautious, you must manually call `flush`. fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[io::IoSlice<'_>], ) -> 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_vectored(cx, bufs) } #[inline] fn is_write_vectored(&self) -> bool { true } 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.26.1/tests/badssl.rs000064400000000000000000000054701046102023000153570ustar 00000000000000use std::io; use std::net::ToSocketAddrs; use std::sync::Arc; use rustls::pki_types::ServerName; use rustls::ClientConfig; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; use tokio_rustls::client::TlsStream; use tokio_rustls::TlsConnector; async fn get( config: Arc, domain: &str, port: u16, vectored: bool, ) -> 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 = ServerName::try_from(domain).unwrap().to_owned(); let mut buf = Vec::new(); let stream = TcpStream::connect(&addr).await?; let mut stream = connector.connect(domain, stream).await?; utils::write(&mut stream, input.as_bytes(), vectored).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<()> { test_tls12_impl(false).await } #[tokio::test] async fn test_tls12_vectored() -> io::Result<()> { test_tls12_impl(true).await } async fn test_tls12_impl(vectored: bool) -> io::Result<()> { let mut root_store = rustls::RootCertStore::empty(); root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); let config = rustls::ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS12]) .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, vectored).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<()> { test_modern_impl(false).await } #[tokio::test] async fn test_modern_vectored() -> io::Result<()> { test_modern_impl(true).await } async fn test_modern_impl(vectored: bool) -> io::Result<()> { let mut root_store = rustls::RootCertStore::empty(); root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); let config = rustls::ClientConfig::builder() .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, vectored).await?; assert!( output.contains("mozilla-modern.badssl.com"), "failed badssl test, output: {}", output ); Ok(()) } // Include `utils` module include!("utils.rs"); tokio-rustls-0.26.1/tests/certs/chain.pem000064400000000000000000000023271046102023000164440ustar 00000000000000-----BEGIN CERTIFICATE----- MIIBsjCCAVmgAwIBAgIUB4Geg6rz4UzdIkSmPjAxGgVhu4MwCgYIKoZIzj0EAwIw JjEkMCIGA1UEAwwbUnVzdGxzIFJvYnVzdCBSb290IC0gUnVuZyAyMCAXDTc1MDEw MTAwMDAwMFoYDzQwOTYwMTAxMDAwMDAwWjAhMR8wHQYDVQQDDBZyY2dlbiBzZWxm IHNpZ25lZCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV2z0vS2Nvj1X k2ZkZNimz/tpEyFIHqHBAMu1ok1q6rioZm0wfKgaVfo2E+/PccibK6AuiK1ZnQ5L Wr3avkB+bqNoMGYwFQYDVR0RBA4wDIIKZm9vYmFyLmNvbTAdBgNVHSUEFjAUBggr BgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFJ8xoDmF470si+tMAE2wYQMHHdOT MA8GA1UdEwEB/wQFMAMBAQAwCgYIKoZIzj0EAwIDRwAwRAIgCEDfPgdEtKoUYtOp YUd7uSDv2VJd749Avwls04C1MaUCIGTikBJzN3dnQbRARkzdOY4gFp4nczCiYaZZ ucFJ3PiC -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBiDCCAS+gAwIBAgIUIKoi4tHahiNaO6Vuw5V97xyOVXQwCgYIKoZIzj0EAwIw HTEbMBkGA1UEAwwSUnVzdGxzIFJvYnVzdCBSb290MCAXDTc1MDEwMTAwMDAwMFoY DzQwOTYwMTAxMDAwMDAwWjAmMSQwIgYDVQQDDBtSdXN0bHMgUm9idXN0IFJvb3Qg LSBSdW5nIDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASJs6dcYkh6yXeD72J3 1JJWfiNkNL4DGhWj5LZhwtq5NxrE2sK/TnQdUHYMhVxKXN0RaRcBZRxoUFD4UFkm mdIKo0IwQDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0OBBYEFOhbF/Vi9OjAC+bv6NTU JMLLV621MA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDRwAwRAIgWtRDzAcl DpVplxAT6/ZmSmYtjttIFs2fM65z6H+LpOQCIB/PcAK3NZ+Mjs3rtVMV5UmXW3Jf UaorChZwaCiO3vT8 -----END CERTIFICATE----- tokio-rustls-0.26.1/tests/certs/end.key000064400000000000000000000003611046102023000161330ustar 00000000000000-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg1UjNBQsUBVfNWWtI uwNhUpyPeV1e3IjRm41VQauX1XOhRANCAARXbPS9LY2+PVeTZmRk2KbP+2kTIUge ocEAy7WiTWrquKhmbTB8qBpV+jYT789xyJsroC6IrVmdDktavdq+QH5u -----END PRIVATE KEY----- tokio-rustls-0.26.1/tests/certs/main.rs000064400000000000000000000045161046102023000161530ustar 00000000000000//! An ignored-by-default integration test that regenerates vendored certs. //! Run with `cargo test -- --ignored` when test certificates need updating. //! Suitable for test certificates only. Not a production CA ;-) use rcgen::{ BasicConstraints, CertificateParams, DistinguishedName, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, }; use std::fs::File; use std::io::Write; #[test] #[ignore] fn regenerate_certs() { let root_key = KeyPair::generate().unwrap(); let root_ca = issuer_params("Rustls Robust Root") .self_signed(&root_key) .unwrap(); let mut root_file = File::create("tests/certs/root.pem").unwrap(); root_file.write_all(root_ca.pem().as_bytes()).unwrap(); let intermediate_key = KeyPair::generate().unwrap(); let intermediate_ca = issuer_params("Rustls Robust Root - Rung 2") .signed_by(&intermediate_key, &root_ca, &root_key) .unwrap(); let end_entity_key = KeyPair::generate().unwrap(); let mut end_entity_params = CertificateParams::new(vec![utils::TEST_SERVER_DOMAIN.to_string()]).unwrap(); end_entity_params.is_ca = IsCa::ExplicitNoCa; end_entity_params.extended_key_usages = vec![ ExtendedKeyUsagePurpose::ServerAuth, ExtendedKeyUsagePurpose::ClientAuth, ]; let end_entity = end_entity_params .signed_by(&end_entity_key, &intermediate_ca, &intermediate_key) .unwrap(); let mut chain_file = File::create("tests/certs/chain.pem").unwrap(); chain_file.write_all(end_entity.pem().as_bytes()).unwrap(); chain_file .write_all(intermediate_ca.pem().as_bytes()) .unwrap(); let mut key_file = File::create("tests/certs/end.key").unwrap(); key_file .write_all(end_entity_key.serialize_pem().as_bytes()) .unwrap(); } fn issuer_params(common_name: &str) -> CertificateParams { let mut issuer_name = DistinguishedName::new(); issuer_name.push(DnType::CommonName, common_name); let mut issuer_params = CertificateParams::default(); issuer_params.distinguished_name = issuer_name; issuer_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); issuer_params.key_usages = vec![ KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature, ]; issuer_params } // For the server name constant. include!("../utils.rs"); tokio-rustls-0.26.1/tests/certs/root.pem000064400000000000000000000011071046102023000163400ustar 00000000000000-----BEGIN CERTIFICATE----- MIIBgDCCASagAwIBAgIUDKVcG8WKAVxMrpkvWBsSKu6G9swwCgYIKoZIzj0EAwIw HTEbMBkGA1UEAwwSUnVzdGxzIFJvYnVzdCBSb290MCAXDTc1MDEwMTAwMDAwMFoY DzQwOTYwMTAxMDAwMDAwWjAdMRswGQYDVQQDDBJSdXN0bHMgUm9idXN0IFJvb3Qw WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQjrQmsnBwZUT8iraiF5EAJFMZE3rgA oqDL6clNl7YtjKqH/E/BiVs+k+70Dz74Ibrm/z80f51fK/Ug2h5pSOp5o0IwQDAO BgNVHQ8BAf8EBAMCAoQwHQYDVR0OBBYEFMwwAap72bFsxZxK0ThGymdrjBfYMA8G A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAJR/PB88zHsy0iotwCcG SPPOowWXb0Uzj6CPHBks25woAiB5Bg4+395Lr2K4UIh3zv0BFuSyXrFqvj+WMhUy 4Z+WRw== -----END CERTIFICATE----- tokio-rustls-0.26.1/tests/early-data.rs000064400000000000000000000073441046102023000161340ustar 00000000000000#![cfg(feature = "early-data")] use std::io::{self, Read, Write}; use std::net::{SocketAddr, TcpListener}; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use std::thread; use futures_util::{future::Future, ready}; use rustls::pki_types::ServerName; use rustls::{self, ClientConfig, ServerConnection, Stream}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWriteExt, ReadBuf}; use tokio::net::TcpStream; use tokio_rustls::client::TlsStream; use tokio_rustls::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], vectored: bool, ) -> io::Result<(TlsStream, Vec)> { let connector = TlsConnector::from(config).early_data(true); let stream = TcpStream::connect(&addr).await?; let domain = ServerName::try_from("foobar.com").unwrap(); let mut stream = connector.connect(domain, stream).await?; utils::write(&mut stream, data, vectored).await?; stream.flush().await?; stream.shutdown().await?; let mut buf = Vec::new(); stream.read_to_end(&mut buf).await?; Ok((stream, buf)) } #[tokio::test] async fn test_0rtt() -> io::Result<()> { test_0rtt_impl(false).await } #[tokio::test] async fn test_0rtt_vectored() -> io::Result<()> { test_0rtt_impl(true).await } async fn test_0rtt_impl(vectored: bool) -> io::Result<()> { let (mut server, mut client) = utils::make_configs(); server.max_early_data_size = 8192; let server = Arc::new(server); let listener = TcpListener::bind("127.0.0.1:0")?; let server_port = listener.local_addr().unwrap().port(); thread::spawn(move || loop { let (mut sock, _addr) = listener.accept().unwrap(); let server = Arc::clone(&server); thread::spawn(move || { let mut conn = ServerConnection::new(server).unwrap(); conn.complete_io(&mut sock).unwrap(); if let Some(mut early_data) = conn.early_data() { let mut buf = Vec::new(); early_data.read_to_end(&mut buf).unwrap(); let mut stream = Stream::new(&mut conn, &mut sock); stream.write_all(b"EARLY:").unwrap(); stream.write_all(&buf).unwrap(); } let mut stream = Stream::new(&mut conn, &mut sock); stream.write_all(b"LATE:").unwrap(); loop { let mut buf = [0; 1024]; let n = stream.read(&mut buf).unwrap(); if n == 0 { conn.send_close_notify(); conn.complete_io(&mut sock).unwrap(); break; } stream.write_all(&buf[..n]).unwrap(); } }); }); client.enable_early_data = true; let client = Arc::new(client); let addr = SocketAddr::from(([127, 0, 0, 1], server_port)); let (io, buf) = send(client.clone(), addr, b"hello", vectored).await?; assert!(!io.get_ref().1.is_early_data_accepted()); assert_eq!("LATE:hello", String::from_utf8_lossy(&buf)); let (io, buf) = send(client, addr, b"world!", vectored).await?; assert!(io.get_ref().1.is_early_data_accepted()); assert_eq!("EARLY:world!LATE:", String::from_utf8_lossy(&buf)); Ok(()) } // Include `utils` module include!("utils.rs"); tokio-rustls-0.26.1/tests/test.rs000064400000000000000000000231441046102023000150640ustar 00000000000000use std::io::{Cursor, ErrorKind}; use std::net::SocketAddr; use std::sync::mpsc::channel; use std::sync::Arc; use std::time::Duration; use std::{io, thread}; use futures_util::future::TryFutureExt; use lazy_static::lazy_static; use rustls::pki_types::ServerName; use rustls::ClientConfig; 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}; lazy_static! { static ref TEST_SERVER: SocketAddr = { let (config, _) = utils::make_configs(); 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); }); recv.recv().unwrap() }; } async fn start_client(addr: SocketAddr, domain: &str, config: Arc) -> io::Result<()> { const FILE: &[u8] = include_bytes!("../README.md"); let domain = ServerName::try_from(domain).unwrap().to_owned(); 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<()> { // 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 (_, config) = utils::make_configs(); let config = Arc::new(config); start_client(*TEST_SERVER, utils::TEST_SERVER_DOMAIN, config).await?; Ok(()) } #[tokio::test] async fn fail() -> io::Result<()> { let (_, config) = utils::make_configs(); let config = Arc::new(config); assert_ne!(utils::TEST_SERVER_DOMAIN, "google.com"); let ret = start_client(*TEST_SERVER, "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(); let (cstream, sstream) = tokio::io::duplex(1200); let domain = ServerName::try_from("foobar.com").unwrap().to_owned(); tokio::spawn(async move { let connector = crate::TlsConnector::from(Arc::new(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(Arc::new(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 fatal_alert_decode_error = b"\x15\x03\x03\x00\x02\x02\x32"; 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(), [&fatal_alert_decode_error[..], &server_msg[..]].concat() ); assert!( acceptor.take_io().is_none(), "Should not be able to take twice" ); Ok(()) } #[tokio::test] async fn acceptor_alert() { let (sconfig, _) = utils::make_configs(); // this is the client hello from https://tls12.xargs.org/#client-hello/annotated with the minor // version byte changed let bad_hello = [ 0x16, 0x03, 0x01, 0x00, 0xa5, 0x01, 0x00, 0x00, 0xa1, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x00, 0x20, 0xcc, 0xa8, 0xcc, 0xa9, 0xc0, 0x2f, 0xc0, 0x30, 0xc0, 0x2b, 0xc0, 0x2c, 0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x14, 0xc0, 0x0a, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0xc0, 0x12, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x18, 0x00, 0x16, 0x00, 0x00, 0x13, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x75, 0x6c, 0x66, 0x68, 0x65, 0x69, 0x6d, 0x2e, 0x6e, 0x65, 0x74, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x12, 0x00, 0x10, 0x04, 0x01, 0x04, 0x03, 0x05, 0x01, 0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x02, 0x01, 0x02, 0x03, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, ]; // Intentionally small so that we have to call alert.write several times let (mut cstream, sstream) = tokio::io::duplex(2); let (tx, rx) = oneshot::channel(); tokio::spawn(async move { cstream.write_all(&bad_hello).await.unwrap(); let mut buf = Vec::new(); cstream.read_to_end(&mut buf).await.unwrap(); tx.send(buf).unwrap(); }); let accept = LazyConfigAcceptor::new(rustls::server::Acceptor::default(), sstream); let Ok(Ok(start_handshake)) = time::timeout(Duration::from_secs(3), accept).await else { panic!("timeout"); }; let err = start_handshake .into_stream(Arc::new(sconfig)) .await .unwrap_err(); assert_eq!(err.to_string(), "peer is incompatible: Tls12NotOffered"); let Ok(Ok(received)) = time::timeout(Duration::from_secs(3), rx).await else { panic!("failed to receive"); }; assert_eq!(received, [0x15, 0x03, 0x03, 0x00, 0x02, 0x02, 0x46]); } #[tokio::test] async fn lazy_config_acceptor_alert() { // Intentionally small so that we have to call alert.write several times let (mut cstream, sstream) = tokio::io::duplex(2); let (tx, rx) = oneshot::channel(); tokio::spawn(async move { // This is write instead of write_all because of the short duplex size, which is necessarily // symmetrical. We never finish writing because the LazyConfigAcceptor returns an error let _ = cstream.write(b"not tls").await; 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); let Ok(accept_result) = time::timeout(Duration::from_secs(3), acceptor).await else { panic!("timeout"); }; assert!(accept_result.is_err()); let Ok(Ok(received)) = time::timeout(Duration::from_secs(3), rx).await else { panic!("failed to receive"); }; let fatal_alert_decode_error = b"\x15\x03\x03\x00\x02\x02\x32"; assert_eq!(received, fatal_alert_decode_error) } // Include `utils` module include!("utils.rs"); tokio-rustls-0.26.1/tests/utils.rs000064400000000000000000000044121046102023000152420ustar 00000000000000mod utils { use std::io::IoSlice; use rustls::{ pki_types::{pem::PemObject, CertificateDer, PrivateKeyDer}, ClientConfig, RootCertStore, ServerConfig, }; use tokio::io::{self, AsyncWrite, AsyncWriteExt}; #[allow(dead_code)] pub fn make_configs() -> (ServerConfig, ClientConfig) { // A test root certificate that is the trust anchor for the CHAIN. const ROOT: &str = include_str!("certs/root.pem"); // A server certificate chain that includes both an end-entity server certificate // and the intermediate certificate that issued it. The ROOT is configured // out-of-band. const CHAIN: &str = include_str!("certs/chain.pem"); // A private key corresponding to the end-entity server certificate in CHAIN. const EE_KEY: &str = include_str!("certs/end.key"); let cert = CertificateDer::pem_slice_iter(CHAIN.as_bytes()) .collect::, _>>() .unwrap(); let key = PrivateKeyDer::from_pem_slice(EE_KEY.as_bytes()).unwrap(); let sconfig = ServerConfig::builder() .with_no_client_auth() .with_single_cert(cert, key) .unwrap(); let mut client_root_cert_store = RootCertStore::empty(); for root in CertificateDer::pem_slice_iter(ROOT.as_bytes()) { client_root_cert_store.add(root.unwrap()).unwrap(); } let cconfig = ClientConfig::builder() .with_root_certificates(client_root_cert_store) .with_no_client_auth(); (sconfig, cconfig) } #[allow(dead_code)] pub async fn write( w: &mut W, data: &[u8], vectored: bool, ) -> io::Result<()> { if !vectored { return w.write_all(data).await; } let mut data = data; while !data.is_empty() { let chunk_size = (data.len() / 4).max(1); let vectors = data .chunks(chunk_size) .map(IoSlice::new) .collect::>(); let written = w.write_vectored(&vectors).await?; data = &data[written..]; } Ok(()) } #[allow(dead_code)] pub const TEST_SERVER_DOMAIN: &str = "foobar.com"; }