Compare commits
No commits in common. "dec39cf2e3f9ce6412130ce8cb8e47ab54c42d44" and "c38b24a8edd82c3da5d93754da6ce05ea1e856c2" have entirely different histories.
dec39cf2e3
...
c38b24a8ed
10 changed files with 1359 additions and 1515 deletions
657
Cargo.lock
generated
657
Cargo.lock
generated
|
|
@ -2,16 +2,6 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
|
|
@ -161,18 +151,6 @@ dependencies = [
|
|||
"fs_extra",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.72.1"
|
||||
|
|
@ -193,77 +171,23 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitfield-struct"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2869c63ccf4f8bf0d485070b880e60e097fb7aeea80ee82a0a94a957e372a0b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "boring"
|
||||
version = "4.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9078ed42084a408d2a1c33bb9dde775721227f1673e4daa81e7f74017b412ce3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"boring-sys",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
"openssl-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boring-additions"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/janrueth/boring-rustls-provider.git?rev=490340afa77e2c08fc45853124f99d49f4f9f8a0#490340afa77e2c08fc45853124f99d49f4f9f8a0"
|
||||
dependencies = [
|
||||
"boring",
|
||||
"boring-sys",
|
||||
"foreign-types 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boring-rustls-provider"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/janrueth/boring-rustls-provider.git?rev=490340afa77e2c08fc45853124f99d49f4f9f8a0#490340afa77e2c08fc45853124f99d49f4f9f8a0"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"boring",
|
||||
"boring-additions",
|
||||
"boring-sys",
|
||||
"boring-sys-additions",
|
||||
"foreign-types 0.5.0",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boring-sys"
|
||||
version = "4.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4f033d6ba6dee8b5322f5504acf758c29317790883bac078e7949dd3d670269"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cmake",
|
||||
"fs_extra",
|
||||
"fslock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "boring-sys-additions"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/janrueth/boring-rustls-provider.git?rev=490340afa77e2c08fc45853124f99d49f4f9f8a0#490340afa77e2c08fc45853124f99d49f4f9f8a0"
|
||||
dependencies = [
|
||||
"boring-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
|
|
@ -323,49 +247,12 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "core-models"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0940496e5c83c54f3b753d5317daec82e8edac71c33aaa1f666d76f518de2444"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"pastey",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der-parser"
|
||||
version = "10.0.0"
|
||||
|
|
@ -435,6 +322,21 @@ dependencies = [
|
|||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fast-tlsh"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d24a9974c3be467166fe631c0a9e804e028ee7525ed27300dfa7915cc6e36311"
|
||||
dependencies = [
|
||||
"bitfield-struct",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"hex-simd",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
|
|
@ -447,28 +349,7 @@ version = "0.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||
dependencies = [
|
||||
"foreign-types-macros",
|
||||
"foreign-types-shared 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-macros"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -477,28 +358,12 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||
|
||||
[[package]]
|
||||
name = "fslock"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
|
|
@ -542,16 +407,6 @@ dependencies = [
|
|||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.24"
|
||||
|
|
@ -591,50 +446,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "graviola"
|
||||
version = "0.3.0"
|
||||
name = "hex-simd"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1662fcff7237fbe8c91ff2800fcce9435af25b7f0cb580f5679b31c3a1f1e7a"
|
||||
checksum = "1f7685beb53fc20efc2605f32f5d51e9ba18b8ef237961d1760169d2290d3bee"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74d9ba66d1739c68e0219b2b2238b5c4145f491ebf181b9c6ab561a19352ae86"
|
||||
dependencies = [
|
||||
"hax-lib-macros",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib-macros"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24ba777a231a58d1bce1d68313fa6b6afcc7966adef23d60f45b8a2b9b688bf1"
|
||||
dependencies = [
|
||||
"hax-lib-macros-types",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hax-lib-macros-types"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "867e19177d7425140b417cd27c2e05320e727ee682e98368f88b7194e80ad515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"uuid",
|
||||
"outref",
|
||||
"vsimd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -692,16 +510,6 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
|
|
@ -714,70 +522,6 @@ version = "0.2.177"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-intrinsics"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc9ee7ef66569dd7516454fe26de4e401c0c62073929803486b96744594b9632"
|
||||
dependencies = [
|
||||
"core-models",
|
||||
"hax-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-ml-kem"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6a88086bf11bd2ec90926c749c4a427f2e59841437dbdede8cde8a96334ab"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"libcrux-intrinsics",
|
||||
"libcrux-platform",
|
||||
"libcrux-secrets",
|
||||
"libcrux-sha3",
|
||||
"libcrux-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-platform"
|
||||
version = "0.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db82d058aa76ea315a3b2092f69dfbd67ddb0e462038a206e1dcd73f058c0778"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-secrets"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e4dbbf6bc9f2bc0f20dc3bea3e5c99adff3bdccf6d2a40488963da69e2ec307"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-sha3"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2400bec764d1c75b8a496d5747cffe32f1fb864a12577f0aca2f55a92021c962"
|
||||
dependencies = [
|
||||
"hax-lib",
|
||||
"libcrux-intrinsics",
|
||||
"libcrux-platform",
|
||||
"libcrux-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libcrux-traits"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9adfd58e79d860f6b9e40e35127bfae9e5bd3ade33201d1347459011a2add034"
|
||||
dependencies = [
|
||||
"libcrux-secrets",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.9"
|
||||
|
|
@ -823,14 +567,12 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"argp",
|
||||
"aws-lc-rs",
|
||||
"boring-rustls-provider",
|
||||
"env_logger",
|
||||
"fast-tlsh",
|
||||
"futures-util",
|
||||
"log",
|
||||
"rustls-graviola",
|
||||
"rustls-openssl",
|
||||
"memchr",
|
||||
"regex",
|
||||
"rustls-post-quantum",
|
||||
"rustls-symcrypt",
|
||||
"sslrelay",
|
||||
"static_cell",
|
||||
"tokio",
|
||||
|
|
@ -912,7 +654,7 @@ checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types 0.3.2",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
|
|
@ -943,10 +685,10 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "pastey"
|
||||
version = "0.1.1"
|
||||
name = "outref"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
|
||||
checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
|
|
@ -960,27 +702,6 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs1"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs8",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
|
|
@ -1008,15 +729,6 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
|
|
@ -1027,28 +739,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
|
|
@ -1085,35 +775,6 @@ version = "5.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
|
|
@ -1181,38 +842,12 @@ dependencies = [
|
|||
"aws-lc-rs",
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.103.7",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-graviola"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e81f0f2005bfba00e8088f9cb75f4b3ce3f2a31aebfaeed0b2cc05e13d01ce06"
|
||||
dependencies = [
|
||||
"graviola",
|
||||
"libcrux-ml-kem",
|
||||
"rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-openssl"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f161084062dd5a443adfb0de9bbfab62699333e2d8424ca91da5f22ec88d1"
|
||||
dependencies = [
|
||||
"foreign-types 0.3.2",
|
||||
"once_cell",
|
||||
"openssl",
|
||||
"openssl-sys",
|
||||
"rustls",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.12.0"
|
||||
|
|
@ -1230,33 +865,7 @@ checksum = "0da3cd9229bac4fae1f589c8f875b3c891a058ddaa26eb3bde16b5e43dc174ce"
|
|||
dependencies = [
|
||||
"aws-lc-rs",
|
||||
"rustls",
|
||||
"rustls-webpki 0.103.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-symcrypt"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1aa1fbe108897452967f5de83014edf2e42606f09283c4ba1f671904098fcbc"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rustls",
|
||||
"rustls-webpki 0.102.8",
|
||||
"sec1",
|
||||
"symcrypt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted 0.9.0",
|
||||
"rustls-webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1271,24 +880,6 @@ dependencies = [
|
|||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"generic-array",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
|
|
@ -1296,7 +887,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1319,19 +909,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
|
|
@ -1354,16 +931,6 @@ dependencies = [
|
|||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sslrelay"
|
||||
version = "0.6.2"
|
||||
|
|
@ -1371,6 +938,12 @@ dependencies = [
|
|||
"openssl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "static_cell"
|
||||
version = "2.1.1"
|
||||
|
|
@ -1386,26 +959,6 @@ version = "2.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "symcrypt"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45f6e93e13900bae533f1f347c82d2fcf9d7ad7fffd7e58bc9fbd1b68575ca1c"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"symcrypt-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symcrypt-sys"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0878df9feb068709a68ccdb5cc3201649201e79e823566863f6ca4db4b9201aa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.107"
|
||||
|
|
@ -1528,12 +1081,6 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.1"
|
||||
|
|
@ -1570,17 +1117,6 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
|
@ -1593,6 +1129,12 @@ version = "0.9.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "vsimd"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
|
|
@ -1608,73 +1150,6 @@ dependencies = [
|
|||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
|
|
@ -1860,34 +1335,8 @@ dependencies = [
|
|||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"
|
||||
|
|
|
|||
34
Cargo.toml
34
Cargo.toml
|
|
@ -5,39 +5,17 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
argp = "0.4.0"
|
||||
aws-lc-rs = "1.14.1"
|
||||
#console-subscriber = "0.5.0"
|
||||
env_logger = "0.11.8"
|
||||
futures-util = "0.3.31"
|
||||
log = "0.4.28"
|
||||
sslrelay = { path = "../sslrelay", optional = true }
|
||||
memchr = "2.7.6"
|
||||
regex = "1.12.2"
|
||||
rustls-post-quantum = { version = "0.2.4", features = ["aws-lc-rs-unstable"] }
|
||||
sslrelay = { path = "../sslrelay" }
|
||||
static_cell = "2.1.1"
|
||||
tlsh = { package = "fast-tlsh", version = "0.1.10", features = ["easy-functions"] }
|
||||
tokio = { version = "1.48.0", features = ["io-util", "macros", "net", "rt", "rt-multi-thread", "sync", "time"]}
|
||||
tokio-rustls = "0.26.4"
|
||||
tokio-util = { version = "0.7.6", features = ["codec"] }
|
||||
x509-parser = "0.18.0"
|
||||
|
||||
# TLS impls
|
||||
aws-lc-rs = { version = "1.14.1", optional = true }
|
||||
boring-rustls-provider = { git = "https://github.com/janrueth/boring-rustls-provider.git", rev = "490340afa77e2c08fc45853124f99d49f4f9f8a0", optional = true }
|
||||
rustls-graviola = { version = "0.3.2", optional = true }
|
||||
rustls-openssl = { version = "0.3.0", default-features = false, features = ["tls12"], optional = true }
|
||||
rustls-post-quantum = { version = "0.2.4", optional = true }
|
||||
rustls-symcrypt = { version = "0.2.1", optional = true, features = ["chacha", "x25519"] }
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"aws-lc",
|
||||
"record",
|
||||
]
|
||||
|
||||
record = ["sslrelay"]
|
||||
|
||||
aws-lc = ["tokio-rustls/aws-lc-rs", "rustls-post-quantum", "rustls-post-quantum/aws-lc-rs-unstable", "aws-lc-rs"]
|
||||
boring = ["boring-rustls-provider"]
|
||||
graviola = ["rustls-graviola"]
|
||||
openssl = ["rustls-openssl"]
|
||||
ring = ["tokio-rustls/ring"]
|
||||
symcrypt = ["rustls-symcrypt"]
|
||||
|
||||
[profile.release]
|
||||
#lto = "fat"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
hard_tabs = true
|
||||
newline_style = "unix"
|
||||
|
||||
unstable_features = true
|
||||
format_code_in_doc_comments = true
|
||||
|
|
|
|||
291
src/client.rs
291
src/client.rs
|
|
@ -1,8 +1,10 @@
|
|||
use crate::{
|
||||
TlsMode,
|
||||
record::{Direction, Records},
|
||||
util::ResponseStreamer,
|
||||
util::{ResponseStreamer, print_bin},
|
||||
};
|
||||
|
||||
use futures_util::{StreamExt, TryStreamExt};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
net::ToSocketAddrs,
|
||||
|
|
@ -12,7 +14,7 @@ use std::{
|
|||
use tokio::{
|
||||
io::AsyncWriteExt,
|
||||
net::TcpStream,
|
||||
sync::{Mutex, Semaphore},
|
||||
sync::{Mutex, Semaphore, oneshot},
|
||||
};
|
||||
use tokio_rustls::{
|
||||
TlsConnector,
|
||||
|
|
@ -25,8 +27,9 @@ use tokio_rustls::{
|
|||
pki_types::ServerName,
|
||||
},
|
||||
};
|
||||
use tokio_util::codec::Framed;
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const TIMEOUT: Duration = Duration::from_secs(60);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DummyCertVerifier;
|
||||
|
|
@ -86,43 +89,24 @@ impl ServerCertVerifier for DummyCertVerifier {
|
|||
|
||||
pub async fn play(
|
||||
records: &'static Records,
|
||||
use_tls: bool,
|
||||
tls_mode: TlsMode,
|
||||
connect_to: (String, u16),
|
||||
sync_receiver: oneshot::Receiver<()>,
|
||||
repeat: u32,
|
||||
debug: bool,
|
||||
) {
|
||||
sync_receiver.await.unwrap();
|
||||
// Semaphore used to limit the number of concurrent clients.
|
||||
// Its handle is released when the task panics.
|
||||
let limiter = Arc::new(Semaphore::new(16));
|
||||
let limiter = Arc::new(Semaphore::new(32));
|
||||
let counter = Arc::new(AtomicU32::new(0));
|
||||
let running = Arc::new(Mutex::new(HashSet::new()));
|
||||
let total = records.len() * repeat as usize;
|
||||
let mut handles = Vec::new();
|
||||
let connect_to = connect_to.to_socket_addrs().unwrap().next().unwrap();
|
||||
let debug_mutex = Arc::new(Mutex::new(()));
|
||||
|
||||
let dummy_bytes = Arc::new(vec![0x42u8; 16 * 1024 * 1024]);
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||
|
||||
tokio::spawn({
|
||||
let running = running.clone();
|
||||
let counter = counter.clone();
|
||||
async move {
|
||||
let mut last_count = 0;
|
||||
loop {
|
||||
tokio::time::sleep(TIMEOUT).await;
|
||||
println!("Running: {:?}", running.lock().await);
|
||||
let new_count = counter.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if new_count == last_count {
|
||||
println!("Stalled at {} / {}, stopping", new_count, total);
|
||||
std::process::exit(0);
|
||||
}
|
||||
last_count = new_count;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if use_tls {
|
||||
match tls_mode {
|
||||
TlsMode::Both | TlsMode::Client => {
|
||||
let mut config = tokio_rustls::rustls::ClientConfig::builder()
|
||||
.dangerous()
|
||||
.with_custom_certificate_verifier(Arc::new(DummyCertVerifier))
|
||||
|
|
@ -141,70 +125,78 @@ pub async fn play(
|
|||
}
|
||||
config.key_log = Arc::new(tokio_rustls::rustls::KeyLogFile::new());
|
||||
let config = Arc::new(config);
|
||||
for _i in 0..repeat {
|
||||
let mut handles = Vec::new();
|
||||
for (conn_id, (server_name, records)) in records.iter() {
|
||||
for (id, (server_name, records)) in records.iter() {
|
||||
let connector = TlsConnector::from(config.clone());
|
||||
let counter = counter.clone();
|
||||
let limiter = limiter.clone();
|
||||
let running = running.clone();
|
||||
let dummy_bytes = dummy_bytes.clone();
|
||||
handles.push(tokio::spawn(async move {
|
||||
let mut running_guard = running.lock().await;
|
||||
running_guard.insert(*conn_id);
|
||||
running_guard.insert(*id);
|
||||
drop(running_guard);
|
||||
let limiter = limiter.acquire().await.unwrap();
|
||||
let server_name =
|
||||
ServerName::try_from(String::from_utf8(server_name.clone()).unwrap())
|
||||
.unwrap();
|
||||
'repeat: for _i in 0..repeat {
|
||||
let stream = TcpStream::connect(connect_to).await.unwrap();
|
||||
let stream = connector
|
||||
.connect(server_name.clone(), stream)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut stream = crate::codec::StreamCodec::new(stream);
|
||||
for (direction, reqs) in ResponseStreamer::new(records.iter()) {
|
||||
let mut stream = Framed::new(stream, crate::http::HttpClientCodec::new());
|
||||
for (direction, data_list) in ResponseStreamer::new(records.iter()) {
|
||||
match direction {
|
||||
Direction::ClientToServer => {
|
||||
for (req_id, len) in reqs {
|
||||
//println!("[CLT] ({conn_id}) >> {}", len);
|
||||
let mut data = dummy_bytes[0..len as usize].to_vec();
|
||||
data[0..4].copy_from_slice(&(len as u32).to_be_bytes());
|
||||
data[4..6].copy_from_slice(&(*conn_id as u16).to_be_bytes());
|
||||
data[6..8].copy_from_slice(&(req_id as u16).to_be_bytes());
|
||||
match tokio::time::timeout(TIMEOUT, async {
|
||||
stream.get_mut().write_all(&data).await.unwrap();
|
||||
})
|
||||
for data in data_list {
|
||||
//println!("[CLT] ({id}) >> {}", data.len());
|
||||
//stream.get_mut().write_all(data).await.unwrap();
|
||||
match tokio::time::timeout(
|
||||
TIMEOUT,
|
||||
stream.get_mut().write_all(data),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_v) => {}
|
||||
Ok(v) => v.unwrap(),
|
||||
Err(_e) => {
|
||||
println!("client timeout {conn_id} (sending)");
|
||||
continue;
|
||||
println!("client timeout {id} (sending)");
|
||||
continue 'repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Direction::ServerToClient => {
|
||||
let expected_total_len =
|
||||
reqs.iter().map(|(_req_id, len)| *len).sum::<u64>();
|
||||
let total_len: usize =
|
||||
data_list.iter().map(|data| data.len()).sum::<usize>();
|
||||
let reduced_len =
|
||||
total_len.saturating_sub(160 * data_list.len()).max(1);
|
||||
let mut total_recv = 0;
|
||||
//println!("[CLT] ({conn_id}) << {}", expected_total_len);
|
||||
while total_recv < expected_total_len {
|
||||
let resp =
|
||||
match tokio::time::timeout(TIMEOUT, stream.next()).await {
|
||||
Ok(v) => v.unwrap(),
|
||||
//println!("[CLT] ({id}) << {}", data.len());
|
||||
// let mut buf = Vec::new();
|
||||
// stream.read_buf(&mut buf).await.ok();
|
||||
//let mut buf = vec![0; data.len().saturating_sub(50).max(1)];
|
||||
//let resp = stream.next().await.unwrap().unwrap();
|
||||
while total_recv < reduced_len {
|
||||
let resp = match tokio::time::timeout(
|
||||
TIMEOUT,
|
||||
stream.next(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(v) => v.unwrap().unwrap(),
|
||||
Err(_e) => {
|
||||
// TODO fix
|
||||
println!(
|
||||
"client timeout {}: {} / {}",
|
||||
conn_id, total_recv, expected_total_len
|
||||
id, total_recv, total_len
|
||||
);
|
||||
//print_bin(data);
|
||||
break;
|
||||
break 'repeat;
|
||||
}
|
||||
};
|
||||
total_recv += resp.len() as u64;
|
||||
total_recv += resp.len();
|
||||
//dbg!(resp.len());
|
||||
//crate::http::decode_http(&mut buf, &mut stream).await;
|
||||
}
|
||||
/*if total_recv > total_len {
|
||||
println!("received too much {}: {} / {}", id, total_recv, total_len);
|
||||
|
|
@ -213,109 +205,206 @@ pub async fn play(
|
|||
}
|
||||
}
|
||||
//stream.get_mut().shutdown().await.unwrap();
|
||||
//println!("Client shutdown");
|
||||
tokio::time::timeout(TIMEOUT, stream.get_mut().shutdown())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let cnt = counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
println!("Client: {} / {}", cnt + 1, total);
|
||||
}
|
||||
drop(limiter);
|
||||
let mut running_guard = running.lock().await;
|
||||
running_guard.remove(conn_id);
|
||||
running_guard.remove(id);
|
||||
drop(running_guard);
|
||||
}));
|
||||
//tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _i in 0..repeat {
|
||||
let mut handles = Vec::new();
|
||||
for (conn_id, (_server_name, records)) in records.iter() {
|
||||
TlsMode::None | TlsMode::Server => {
|
||||
for (id, (_server_name, records)) in records.iter() {
|
||||
/*if *id != 33 {
|
||||
continue
|
||||
}*/
|
||||
let counter = counter.clone();
|
||||
let limiter = limiter.clone();
|
||||
let running = running.clone();
|
||||
let dummy_bytes = dummy_bytes.clone();
|
||||
let debug_mutex = debug_mutex.clone();
|
||||
handles.push(tokio::spawn(async move {
|
||||
let mut running_guard = running.lock().await;
|
||||
running_guard.insert(*conn_id);
|
||||
running_guard.insert(*id);
|
||||
drop(running_guard);
|
||||
let limiter = limiter.acquire().await.unwrap();
|
||||
//let mut buf = Vec::new();
|
||||
'repeat: for _i in 0..repeat {
|
||||
let stream = TcpStream::connect(connect_to).await.unwrap();
|
||||
let mut stream = crate::codec::StreamCodec::new(stream);
|
||||
for (direction, reqs) in ResponseStreamer::new(records.iter()) {
|
||||
let mut stream = Framed::new(stream, crate::http::HttpClientCodec::new());
|
||||
/*let mut skip_recv = false;
|
||||
for (direction, data) in records {
|
||||
match direction {
|
||||
Direction::ClientToServer => {
|
||||
for (req_id, len) in reqs {
|
||||
let mut data = dummy_bytes[0..len as usize].to_vec();
|
||||
data[0..4].copy_from_slice(&(len as u32).to_be_bytes());
|
||||
data[4..6].copy_from_slice(&(*conn_id as u16).to_be_bytes());
|
||||
data[6..8].copy_from_slice(&(req_id as u16).to_be_bytes());
|
||||
//println!("[CLT] ({conn_id}) >> {}", len);
|
||||
match tokio::time::timeout(TIMEOUT, async {
|
||||
stream.get_mut().write_all(&data).await.unwrap();
|
||||
})
|
||||
skip_recv = false;
|
||||
println!("[CLT] ({id}) >> {}", data.len());
|
||||
stream.write_all(data).await.unwrap();
|
||||
}
|
||||
Direction::ServerToClient => {
|
||||
if skip_recv {
|
||||
continue;
|
||||
}
|
||||
println!("[CLT] ({id}) << {}", data.len());
|
||||
//let mut buf = Vec::new();
|
||||
//stream.read_buf(&mut buf).await.ok();
|
||||
//let mut buf = vec![0; data.len().saturating_sub(50).max(1)];
|
||||
let mut buf = vec![0; data.len()];
|
||||
match tokio::time::timeout(
|
||||
std::time::Duration::from_millis(500),
|
||||
stream.readable(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(_v) => {}
|
||||
Ok(r) => {
|
||||
r.unwrap();
|
||||
}
|
||||
Err(_) => {
|
||||
println!("[CLT] timeout recv ({id})");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO utiliser crate::http ici
|
||||
match tokio::time::timeout(
|
||||
std::time::Duration::from_millis(500),
|
||||
stream.read_exact(&mut buf),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(r) => {
|
||||
r.unwrap();
|
||||
}
|
||||
Err(_) => {
|
||||
println!("[CLT] skip recv ({id})");
|
||||
skip_recv = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
for (direction, data_list) in ResponseStreamer::new(records.iter()) {
|
||||
match direction {
|
||||
Direction::ClientToServer => {
|
||||
for data in data_list.into_iter() {
|
||||
if debug {
|
||||
//println!("[CLT] ({id}) >> {}", str::from_utf8(&data[..data.len().min(255)]).unwrap());
|
||||
println!("[CLT] ({id}) >> {}", data.len());
|
||||
}
|
||||
//stream.get_mut().write_all(data).await.unwrap();
|
||||
match tokio::time::timeout(
|
||||
TIMEOUT,
|
||||
stream.get_mut().write_all(data),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(v) => v.unwrap(),
|
||||
Err(_e) => {
|
||||
println!("client timeout {conn_id} (sending)");
|
||||
continue;
|
||||
println!("client timeout {id} (sending)");
|
||||
continue 'repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Direction::ServerToClient => {
|
||||
let expected_total_len =
|
||||
reqs.iter().map(|(_req_id, len)| *len).sum::<u64>();
|
||||
let total_len: usize =
|
||||
data_list.iter().map(|data| data.len()).sum::<usize>();
|
||||
let reduced_len =
|
||||
total_len.saturating_sub(160 * data_list.len()).max(1);
|
||||
let mut total_recv = 0;
|
||||
//println!("[CLT] ({conn_id}) << {}", expected_total_len);
|
||||
while total_recv < expected_total_len {
|
||||
let resp =
|
||||
match tokio::time::timeout(TIMEOUT, stream.next()).await {
|
||||
Ok(v) => v.unwrap(),
|
||||
if debug {
|
||||
println!("[CLT] ({id}) << {total_len}");
|
||||
}
|
||||
//let mut buf = Vec::new();
|
||||
//stream.read_buf(&mut buf).await.ok();
|
||||
//let mut buf = vec![0; data.len().saturating_sub(50).max(1)];
|
||||
while total_recv < reduced_len {
|
||||
let resp = match tokio::time::timeout(
|
||||
TIMEOUT,
|
||||
stream.next(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(None) => break,
|
||||
Ok(Some(v)) => v.unwrap(),
|
||||
Err(_e) => {
|
||||
// TODO fix
|
||||
println!(
|
||||
"client timeout {}: {} / {}",
|
||||
conn_id, total_recv, expected_total_len
|
||||
id, total_recv, total_len
|
||||
);
|
||||
//print_bin(data);
|
||||
break;
|
||||
break 'repeat;
|
||||
}
|
||||
};
|
||||
total_recv += resp.len() as u64;
|
||||
total_recv += resp.len();
|
||||
/*if resp.len() != data.len() {
|
||||
let guard = debug_mutex.lock().await;
|
||||
println!("RECV NOT ENOUGH {} / {}", resp.len(), data.len());
|
||||
if resp.len() < 1000 && data.len() < 1000 {
|
||||
//print_bin(&resp);
|
||||
//println!("WANTED");
|
||||
//print_bin(data);
|
||||
}
|
||||
/*if total_recv > total_len {
|
||||
println!("received too much {}: {} / {}", id, total_recv, total_len);
|
||||
std::mem::drop(guard);
|
||||
}*/
|
||||
//print_bin(&resp);
|
||||
//let resp = stream.next().await.unwrap().unwrap();
|
||||
//dbg!(resp.len());
|
||||
//crate::http::decode_http(&mut buf, &mut stream).await;
|
||||
//buf.clear();
|
||||
}
|
||||
if total_recv < reduced_len {
|
||||
println!(
|
||||
"({}) RECV NOT ENOUGH {} / {}",
|
||||
id, total_recv, total_len
|
||||
);
|
||||
} else if debug {
|
||||
println!("[CLT] ({id}) << {total_len} OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//stream.get_mut().shutdown().await.unwrap();
|
||||
//println!("Client shutdown");
|
||||
tokio::time::timeout(TIMEOUT, stream.get_mut().shutdown())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let cnt = counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
println!("Client: {} / {}", cnt + 1, total);
|
||||
}
|
||||
drop(limiter);
|
||||
let mut running_guard = running.lock().await;
|
||||
running_guard.remove(conn_id);
|
||||
running_guard.remove(id);
|
||||
drop(running_guard);
|
||||
}));
|
||||
//tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
tokio::spawn({
|
||||
let running = running.clone();
|
||||
async move {
|
||||
let mut last_count = 0;
|
||||
loop {
|
||||
tokio::time::sleep(TIMEOUT).await;
|
||||
println!("Running: {:?}", running.lock().await);
|
||||
let new_count = counter.load(std::sync::atomic::Ordering::Relaxed);
|
||||
if new_count == last_count {
|
||||
println!("Stalled at {} / {}, stopping", new_count, total);
|
||||
std::process::exit(0);
|
||||
}
|
||||
last_count = new_count;
|
||||
}
|
||||
}
|
||||
});
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("Unfinished: {:?}", running.lock().await);
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
|
|
|||
40
src/codec.rs
40
src/codec.rs
|
|
@ -1,40 +0,0 @@
|
|||
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||
|
||||
pub struct StreamCodec<S> {
|
||||
stream: S,
|
||||
}
|
||||
|
||||
impl<S: AsyncRead + Unpin> StreamCodec<S> {
|
||||
pub fn new(stream: S) -> Self {
|
||||
Self { stream }
|
||||
}
|
||||
|
||||
pub async fn next(&mut self) -> Result<Vec<u8>, std::io::Error> {
|
||||
let mut buf = vec![0; 8];
|
||||
self.stream.read_exact(&mut buf).await?;
|
||||
let expected_len = u32::from_be_bytes(buf[0..4].try_into().unwrap()) as usize;
|
||||
if expected_len < 8 || expected_len > 8 * 1024 * 1024 {
|
||||
return Err(std::io::ErrorKind::InvalidData.into());
|
||||
}
|
||||
buf.resize(expected_len, 0);
|
||||
self.stream.read_exact(&mut buf[8..expected_len]).await?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut S {
|
||||
&mut self.stream
|
||||
}
|
||||
}
|
||||
|
||||
/*#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use tokio_util::{bytes::BytesMut, codec::Framed};
|
||||
|
||||
#[test]
|
||||
fn test_decode() {
|
||||
let stream = futures_util::stream::iter([BytesMut::fr&[0u8]]);
|
||||
let stream = Framed::new(stream, CustomCodec::new());
|
||||
}
|
||||
}*/
|
||||
397
src/http.rs
Normal file
397
src/http.rs
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
use regex::bytes::Regex;
|
||||
use std::{pin::Pin, sync::LazyLock};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||
use tokio_util::{
|
||||
bytes::BytesMut,
|
||||
codec::{Decoder, Encoder},
|
||||
};
|
||||
|
||||
use crate::util::{is_hex, parse_hex};
|
||||
|
||||
static REGEX_CONTENT_LENGTH: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r#"[cC]ontent-[lL]ength: *(\d+)\r\n"#).unwrap());
|
||||
static REGEX_CHUNKED: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r#"[tT]ransfer-[eE]ncoding: *[cC]hunked\r\n"#).unwrap());
|
||||
|
||||
/*pin_project! {
|
||||
pub struct Framer<S, C> {
|
||||
#[pin]
|
||||
pub stream: S,
|
||||
codec: C,
|
||||
buf: BytesMut,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsyncRead + Unpin, C: Decoder> Framer<S, C> {
|
||||
pub fn new(stream: S, codec: C) -> Self {
|
||||
Self {
|
||||
stream,
|
||||
codec,
|
||||
buf: BytesMut::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn next(&mut self) -> Option<Vec<u8>> {
|
||||
self.stream.read_buf(&mut self.buf).await.unwrap();
|
||||
None
|
||||
}
|
||||
}*/
|
||||
|
||||
pub struct HttpClientCodec {
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl HttpClientCodec {
|
||||
pub fn new() -> Self {
|
||||
Self { buf: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for HttpClientCodec {
|
||||
type Item = Vec<u8>;
|
||||
type Error = std::io::Error;
|
||||
fn decode(
|
||||
&mut self,
|
||||
src: &mut tokio_util::bytes::BytesMut,
|
||||
) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.buf.extend_from_slice(&src);
|
||||
src.clear();
|
||||
let src = &mut self.buf;
|
||||
if let Some(mut end_index) = memchr::memmem::find(src, b"\r\n\r\n") {
|
||||
end_index += 4;
|
||||
if let Some(captures) = REGEX_CONTENT_LENGTH.captures(src) {
|
||||
// Content-Length: simple body
|
||||
if let Some(content_length) = captures.get(1) {
|
||||
// Read body
|
||||
let content_length: usize = str::from_utf8(content_length.as_bytes())
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap();
|
||||
if src.len() >= end_index + content_length {
|
||||
let remaining = src.split_off(end_index + content_length);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
} else {
|
||||
//dbg!("Not enough data");
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
// Invalid Content-Length
|
||||
Err(std::io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
} else if REGEX_CHUNKED.is_match(&src[0..end_index]) {
|
||||
// Chunked body
|
||||
let mut content = &src[end_index..];
|
||||
let mut total_len = end_index;
|
||||
loop {
|
||||
if let Some(len_end_index) = memchr::memmem::find(content, b"\r\n") {
|
||||
let len_slice = &content[0..len_end_index];
|
||||
if len_end_index < 8 && is_hex(len_slice) {
|
||||
let chunk_len = parse_hex(len_slice) as usize;
|
||||
if content.len() >= len_end_index + chunk_len + 4 {
|
||||
total_len += len_end_index + chunk_len + 4;
|
||||
// Should we check the ending CRLF?
|
||||
if chunk_len == 0 {
|
||||
let remaining = src.split_off(total_len);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
return Ok(Some(out));
|
||||
}
|
||||
// else, wait for the next chunk
|
||||
content = &content[len_end_index + chunk_len + 4..];
|
||||
} else {
|
||||
// Not enough data
|
||||
return Ok(None);
|
||||
}
|
||||
} else {
|
||||
// Invalid chunk length
|
||||
return Err(std::io::ErrorKind::InvalidData.into());
|
||||
}
|
||||
} else {
|
||||
// Not enough data
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Header ended without Content-Type nor chunks => no body
|
||||
let remaining = src.split_off(end_index);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
}
|
||||
} else {
|
||||
//dbg!("Unfinished header");
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/*self.buf.extend_from_slice(&src);
|
||||
src.clear();
|
||||
let src = &mut self.buf;
|
||||
if self.chunked {
|
||||
if let Some(len_end_index) = memchr::memmem::find(src, b"\r\n") {
|
||||
let len_slice = &src[0..len_end_index];
|
||||
if len_end_index < 8 && is_hex(len_slice) {
|
||||
let chunk_len = parse_hex(len_slice) as usize;
|
||||
if src.len() >= len_end_index + chunk_len + 4 {
|
||||
// Should we check the ending CRLF?
|
||||
if chunk_len == 0 {
|
||||
self.chunked = false;
|
||||
}
|
||||
let remaining = src.split_off(len_end_index+chunk_len+4);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
} else {
|
||||
// Not enough data
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
// Invalid chunk length
|
||||
Err(std::io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
} else {
|
||||
// Not enough data
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
if let Some(mut end_index) = memchr::memmem::find(src, b"\r\n\r\n") {
|
||||
end_index += 4;
|
||||
if let Some(captures) = REGEX_CONTENT_LENGTH.captures(src) {
|
||||
if let Some(content_length) = captures.get(1) {
|
||||
// Read body
|
||||
let content_length: usize = str::from_utf8(content_length.as_bytes())
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap();
|
||||
if src.len() >= end_index + content_length {
|
||||
if REGEX_CHUNKED.is_match(&src[0..end_index]) {
|
||||
self.chunked = true;
|
||||
}
|
||||
//dbg!(content_length);
|
||||
let remaining = src.split_off(end_index + content_length);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
} else {
|
||||
//dbg!("Not enough data");
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
// Invalid Content-Length
|
||||
Err(std::io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
} else {
|
||||
// Header ended without Content-Type => no body
|
||||
let remaining = src.split_off(end_index);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
}
|
||||
} else {
|
||||
//dbg!("Unfinished header");
|
||||
Ok(None)
|
||||
}
|
||||
}*/
|
||||
|
||||
/*if let Some(start_index) = memchr::memmem::find(src, b"HTTP") {
|
||||
if start_index != 0 {
|
||||
dbg!(start_index);
|
||||
if start_index == 529 {
|
||||
println!("{src:?}");
|
||||
}
|
||||
}
|
||||
let src2 = &src[start_index..];
|
||||
if let Some(mut end_index) = memchr::memmem::find(src2, b"\r\n\r\n") {
|
||||
end_index += 4;
|
||||
if let Some(captures) = REGEX_CONTENT_LENGTH.captures(src2) {
|
||||
if let Some(content_length) = captures.get(1) {
|
||||
// Read body
|
||||
let content_length: usize = str::from_utf8(content_length.as_bytes())
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap();
|
||||
if src2.len() >= end_index + content_length {
|
||||
if src2.len() > end_index + content_length {
|
||||
dbg!(src2.len(), end_index + content_length);
|
||||
println!("{src2:?}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
//dbg!(content_length);
|
||||
let out = src2.to_vec();
|
||||
src.clear();
|
||||
Ok(Some(out))
|
||||
} else {
|
||||
//dbg!("Not enough data");
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
// Invalid Content-Length
|
||||
Err(std::io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
} else {
|
||||
// Header ended without Content-Type => no body
|
||||
let out = src2.to_vec();
|
||||
src.clear();
|
||||
Ok(Some(out))
|
||||
}
|
||||
} else {
|
||||
//dbg!("Unfinished header");
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
//dbg!("Unstarted header");
|
||||
Ok(None)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder<Vec<u8>> for HttpClientCodec {
|
||||
type Error = std::io::Error;
|
||||
fn encode(
|
||||
&mut self,
|
||||
_item: Vec<u8>,
|
||||
_dst: &mut tokio_util::bytes::BytesMut,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HttpServerCodec {
|
||||
buf: Vec<u8>,
|
||||
}
|
||||
|
||||
impl HttpServerCodec {
|
||||
pub fn new() -> Self {
|
||||
Self { buf: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for HttpServerCodec {
|
||||
type Item = Vec<u8>;
|
||||
type Error = std::io::Error;
|
||||
fn decode(
|
||||
&mut self,
|
||||
src: &mut tokio_util::bytes::BytesMut,
|
||||
) -> Result<Option<Self::Item>, Self::Error> {
|
||||
self.buf.extend_from_slice(&src);
|
||||
src.clear();
|
||||
let src = &mut self.buf;
|
||||
if let Some(mut end_index) = memchr::memmem::find(src, b"\r\n\r\n") {
|
||||
end_index += 4;
|
||||
if let Some(captures) = REGEX_CONTENT_LENGTH.captures(src) {
|
||||
// Content-Length: simple body
|
||||
if let Some(content_length) = captures.get(1) {
|
||||
// Read body
|
||||
let content_length: usize = str::from_utf8(content_length.as_bytes())
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap();
|
||||
if src.len() >= end_index + content_length {
|
||||
let remaining = src.split_off(end_index + content_length);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
} else {
|
||||
//dbg!("Not enough data");
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
// Invalid Content-Length
|
||||
Err(std::io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
} else if REGEX_CHUNKED.is_match(&src[0..end_index]) {
|
||||
// Chunked body
|
||||
let mut content = &src[end_index..];
|
||||
let mut total_len = end_index;
|
||||
loop {
|
||||
if let Some(len_end_index) = memchr::memmem::find(content, b"\r\n") {
|
||||
let len_slice = &content[0..len_end_index];
|
||||
if len_end_index < 8 && is_hex(len_slice) {
|
||||
let chunk_len = parse_hex(len_slice) as usize;
|
||||
if content.len() >= len_end_index + chunk_len + 4 {
|
||||
total_len += len_end_index + chunk_len + 4;
|
||||
// Should we check the ending CRLF?
|
||||
if chunk_len == 0 {
|
||||
let remaining = src.split_off(total_len);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
return Ok(Some(out));
|
||||
}
|
||||
// else, wait for the next chunk
|
||||
content = &content[len_end_index + chunk_len + 4..];
|
||||
} else {
|
||||
// Not enough data
|
||||
return Ok(None);
|
||||
}
|
||||
} else {
|
||||
// Invalid chunk length
|
||||
return Err(std::io::ErrorKind::InvalidData.into());
|
||||
}
|
||||
} else {
|
||||
// Not enough data
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Header ended without Content-Type nor chunks => no body
|
||||
let remaining = src.split_off(end_index);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
}
|
||||
} else {
|
||||
//dbg!("Unfinished header");
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/*self.buf.extend_from_slice(&src);
|
||||
src.clear();
|
||||
let src = &mut self.buf;
|
||||
if let Some(mut end_index) = memchr::memmem::find(src, b"\r\n\r\n") {
|
||||
end_index += 4;
|
||||
if let Some(captures) = REGEX_CONTENT_LENGTH.captures(&src[0..end_index]) {
|
||||
if let Some(content_length) = captures.get(1) {
|
||||
// Read body
|
||||
let content_length: usize = str::from_utf8(content_length.as_bytes())
|
||||
.unwrap()
|
||||
.parse()
|
||||
.unwrap();
|
||||
if src.len() >= end_index + content_length {
|
||||
//dbg!(content_length);
|
||||
let remaining = src.split_off(end_index + content_length);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
} else {
|
||||
//dbg!("Not enough data");
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
// Invalid Content-Length
|
||||
Err(std::io::ErrorKind::InvalidData.into())
|
||||
}
|
||||
} else {
|
||||
// Header ended without Content-Type => no body
|
||||
let remaining = src.split_off(end_index);
|
||||
let out = src.to_vec();
|
||||
*src = remaining;
|
||||
Ok(Some(out))
|
||||
}
|
||||
} else {
|
||||
//dbg!("Unfinished header");
|
||||
Ok(None)
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder<Vec<u8>> for HttpServerCodec {
|
||||
type Error = std::io::Error;
|
||||
fn encode(
|
||||
&mut self,
|
||||
_item: Vec<u8>,
|
||||
_dst: &mut tokio_util::bytes::BytesMut,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
197
src/main.rs
197
src/main.rs
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(ascii_char)]
|
||||
|
||||
mod client;
|
||||
mod codec;
|
||||
mod http;
|
||||
mod record;
|
||||
mod server;
|
||||
mod util;
|
||||
|
|
@ -10,6 +10,8 @@ use record::Records;
|
|||
|
||||
use argp::FromArgs;
|
||||
use static_cell::StaticCell;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio_rustls::rustls::crypto::CryptoProvider;
|
||||
|
||||
/// Play recorded requests and responses
|
||||
#[derive(FromArgs)]
|
||||
|
|
@ -24,14 +26,11 @@ struct Opt {
|
|||
#[derive(FromArgs)]
|
||||
#[argp(subcommand)]
|
||||
enum Subcommand {
|
||||
/// Replay from records (client)
|
||||
Client(OptClient),
|
||||
/// Replay from records (server)
|
||||
Server(OptServer),
|
||||
/// Replay from records
|
||||
Play(OptPlay),
|
||||
/// Print records
|
||||
Print(OptPrint),
|
||||
/// Record traffic
|
||||
#[cfg(feature = "record")]
|
||||
Record(OptRecord),
|
||||
/// Remove record
|
||||
Remove(OptRemove),
|
||||
|
|
@ -41,44 +40,32 @@ enum Subcommand {
|
|||
|
||||
/// Replay from records
|
||||
#[derive(FromArgs)]
|
||||
#[argp(subcommand, name = "client")]
|
||||
struct OptClient {
|
||||
#[argp(subcommand, name = "play")]
|
||||
struct OptPlay {
|
||||
/// Connect to address
|
||||
#[argp(positional)]
|
||||
connect_addr: String,
|
||||
forward_addr: String,
|
||||
/// Connect to port
|
||||
#[argp(positional)]
|
||||
connect_port: u16,
|
||||
/// Whether to use TLS
|
||||
#[argp(switch, long = "tls")]
|
||||
tls: bool,
|
||||
/// Repeat N times
|
||||
#[argp(option, short = 'r', default = "1")]
|
||||
repeat: u32,
|
||||
/// UDP end notification will be sent to this address:port
|
||||
#[argp(option, short = 'n')]
|
||||
notify_addr: Option<String>,
|
||||
/// Only play this record
|
||||
#[argp(option)]
|
||||
record: Option<u64>,
|
||||
/// Print debug info
|
||||
#[argp(switch, short = 'd')]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
/// Replay from records
|
||||
#[derive(FromArgs)]
|
||||
#[argp(subcommand, name = "server")]
|
||||
struct OptServer {
|
||||
forward_port: u16,
|
||||
/// Listen to port
|
||||
#[argp(positional)]
|
||||
listen_port: u16,
|
||||
/// Path to PEM certificates and keys
|
||||
#[argp(positional)]
|
||||
certs: String,
|
||||
/// Whether to use TLS
|
||||
#[argp(switch, long = "tls")]
|
||||
tls: bool,
|
||||
/// Where to use TLS
|
||||
#[argp(positional)]
|
||||
tls: String,
|
||||
/// Repeat N times
|
||||
#[argp(option, short = 'r', default = "1")]
|
||||
repeat: u32,
|
||||
/// Only play this record
|
||||
#[argp(option)]
|
||||
record: Option<u64>,
|
||||
/// Only run these parts
|
||||
#[argp(option, default = "String::from(\"both\")")]
|
||||
run: String,
|
||||
/// Print debug info
|
||||
#[argp(switch, short = 'd')]
|
||||
debug: bool,
|
||||
|
|
@ -88,13 +75,15 @@ struct OptServer {
|
|||
#[derive(FromArgs)]
|
||||
#[argp(subcommand, name = "print")]
|
||||
struct OptPrint {
|
||||
/// Print packets
|
||||
#[argp(switch, short = 'p')]
|
||||
packets: bool,
|
||||
/// Record number
|
||||
#[argp(option, short = 'n')]
|
||||
number: Option<u64>,
|
||||
}
|
||||
|
||||
/// Record traffic
|
||||
#[cfg(feature = "record")]
|
||||
#[derive(FromArgs)]
|
||||
#[argp(subcommand, name = "record")]
|
||||
struct OptRecord {}
|
||||
|
|
@ -119,6 +108,21 @@ struct OptRemove {
|
|||
packet_number: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum RunMode {
|
||||
Client,
|
||||
Server,
|
||||
Both,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
enum TlsMode {
|
||||
None,
|
||||
Client,
|
||||
Server,
|
||||
Both,
|
||||
}
|
||||
|
||||
static RECORDS: StaticCell<Records> = StaticCell::new();
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -127,49 +131,140 @@ async fn main() {
|
|||
let opt: Opt = argp::parse_args_or_exit(argp::DEFAULT);
|
||||
|
||||
match opt.subcommand {
|
||||
Subcommand::Client(subopt) => {
|
||||
Subcommand::Play(subopt) => {
|
||||
let tls_mode = match subopt.tls.as_str() {
|
||||
"none" => TlsMode::None,
|
||||
"client" => TlsMode::Client,
|
||||
"server" => TlsMode::Server,
|
||||
"both" => TlsMode::Both,
|
||||
_ => panic!("TLS mode must be one of none,client,server,both."),
|
||||
};
|
||||
let run_mode = match subopt.run.as_str() {
|
||||
"client" => RunMode::Client,
|
||||
"server" => RunMode::Server,
|
||||
"both" => RunMode::Both,
|
||||
_ => panic!("run mode must be one of client,server,both."),
|
||||
};
|
||||
let records = RECORDS.init(record::read_record_file(&opt.record_file));
|
||||
|
||||
if let Some(only_record) = subopt.record {
|
||||
records.retain(|id, _| *id == only_record);
|
||||
}
|
||||
|
||||
util::init_provider();
|
||||
let mut ciphers: Option<Vec<String>> = None;
|
||||
let mut kexes: Option<Vec<String>> = None;
|
||||
for (var, val) in std::env::vars() {
|
||||
match var.as_str() {
|
||||
"CIPHERS" => ciphers = Some(val.split(',').map(str::to_string).collect()),
|
||||
"KEXES" => kexes = Some(val.split(',').map(str::to_string).collect()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let mut prov = tokio_rustls::rustls::crypto::aws_lc_rs::default_provider();
|
||||
if let Some(ciphers) = ciphers {
|
||||
prov.cipher_suites.clear();
|
||||
for cipher in ciphers {
|
||||
match cipher.as_str() {
|
||||
"AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384),
|
||||
"AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256),
|
||||
"CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_RSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_RSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
other => {
|
||||
println!("Unknown cipher `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(kexes) = kexes {
|
||||
prov.kx_groups.clear();
|
||||
for kex in kexes {
|
||||
match kex.as_str() {
|
||||
"X25519" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::X25519),
|
||||
"SECP256R1" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::SECP256R1),
|
||||
"SECP384R1" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::SECP384R1),
|
||||
"X25519MLKEM768" => prov.kx_groups.push(
|
||||
tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::X25519MLKEM768,
|
||||
),
|
||||
"SECP256R1MLKEM768" => prov.kx_groups.push(
|
||||
tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::SECP256R1MLKEM768,
|
||||
),
|
||||
"MLKEM768" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::MLKEM768),
|
||||
other => {
|
||||
println!("Unknown kex `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CryptoProvider::install_default(prov).unwrap();
|
||||
|
||||
let (sync_sender, sync_receiver) = oneshot::channel();
|
||||
//console_subscriber::init();
|
||||
let client = tokio::spawn({
|
||||
let records = &*records;
|
||||
async move {
|
||||
if run_mode == RunMode::Both || run_mode == RunMode::Client {
|
||||
client::play(
|
||||
records,
|
||||
subopt.tls,
|
||||
(subopt.connect_addr, subopt.connect_port),
|
||||
tls_mode,
|
||||
(subopt.forward_addr, subopt.forward_port),
|
||||
sync_receiver,
|
||||
subopt.repeat,
|
||||
subopt.debug,
|
||||
)
|
||||
.await;
|
||||
if let Some(notify_addr) = subopt.notify_addr {
|
||||
let socket = std::net::UdpSocket::bind("0.0.0.0:48567").unwrap();
|
||||
socket.send_to(b"done", ¬ify_addr).unwrap();
|
||||
} else {
|
||||
std::future::pending().await
|
||||
}
|
||||
}
|
||||
Subcommand::Server(subopt) => {
|
||||
let records = RECORDS.init(record::read_record_file(&opt.record_file));
|
||||
|
||||
util::init_provider();
|
||||
|
||||
//console_subscriber::init();
|
||||
});
|
||||
if run_mode == RunMode::Both || run_mode == RunMode::Server {
|
||||
server::play(
|
||||
records,
|
||||
subopt.tls,
|
||||
tls_mode,
|
||||
&subopt.certs,
|
||||
("0.0.0.0", subopt.listen_port),
|
||||
sync_sender,
|
||||
subopt.debug,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
client.await.unwrap();
|
||||
}
|
||||
Subcommand::Print(subopt) => {
|
||||
let records = record::read_record_file(&opt.record_file);
|
||||
record::print_records(&records, subopt.number);
|
||||
record::print_records(&records, subopt.packets, subopt.number);
|
||||
}
|
||||
#[cfg(feature = "record")]
|
||||
Subcommand::Record(_subopt) => {
|
||||
record::make_record(&opt.record_file);
|
||||
}
|
||||
|
|
|
|||
101
src/record.rs
101
src/record.rs
|
|
@ -4,10 +4,12 @@ use std::{
|
|||
sync::mpsc::{Receiver, Sender, channel},
|
||||
};
|
||||
|
||||
use crate::util::{ResponseStreamer, print_bin};
|
||||
|
||||
const CLIENT_TO_SERVER: u8 = b'C';
|
||||
const SERVER_TO_CLIENT: u8 = b'S';
|
||||
|
||||
pub type Records = BTreeMap<u64, (Vec<u8>, Vec<(u64, Direction, u64)>)>;
|
||||
pub type Records = BTreeMap<u64, (Vec<u8>, Vec<(Direction, Vec<u8>)>)>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Direction {
|
||||
|
|
@ -32,7 +34,7 @@ fn write_record(
|
|||
direction: Direction,
|
||||
conn_id: u64,
|
||||
server_name: &str,
|
||||
len: u64,
|
||||
data: &[u8],
|
||||
) {
|
||||
let server_name = server_name.as_bytes();
|
||||
file.write_all(&[match direction {
|
||||
|
|
@ -43,7 +45,8 @@ fn write_record(
|
|||
file.write_all(&conn_id.to_be_bytes()).unwrap();
|
||||
file.write_all(&[server_name.len() as u8]).unwrap();
|
||||
file.write_all(server_name).unwrap();
|
||||
file.write_all(&len.to_be_bytes()).unwrap();
|
||||
file.write_all(&(data.len() as u64).to_be_bytes()).unwrap();
|
||||
file.write_all(&data).unwrap();
|
||||
file.flush().unwrap();
|
||||
}
|
||||
|
||||
|
|
@ -75,25 +78,17 @@ impl Recorder {
|
|||
let Some(server_name) = server_name else {
|
||||
continue;
|
||||
};
|
||||
write_record(
|
||||
&mut self.file,
|
||||
direction,
|
||||
conn_id,
|
||||
&server_name,
|
||||
data.len() as u64,
|
||||
);
|
||||
write_record(&mut self.file, direction, conn_id, &server_name, &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "record")]
|
||||
#[derive(Clone)]
|
||||
struct Handler {
|
||||
sender: Sender<(u64, Option<String>, Direction, Vec<u8>)>,
|
||||
server_name: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "record")]
|
||||
impl sslrelay::HandlerCallbacks for Handler {
|
||||
// DownStream non blocking callback
|
||||
fn ds_nb_callback(&self, in_data: Vec<u8>, conn_id: u64) {
|
||||
|
|
@ -134,7 +129,6 @@ impl sslrelay::HandlerCallbacks for Handler {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "record")]
|
||||
pub fn make_record(path: &str) {
|
||||
let (mut recorder, sender) = Recorder::new(path);
|
||||
let mut relay = sslrelay::SSLRelay::new(
|
||||
|
|
@ -167,7 +161,7 @@ pub fn make_record(path: &str) {
|
|||
|
||||
pub fn read_record_file(path: &str) -> Records {
|
||||
let mut file = std::fs::OpenOptions::new().read(true).open(path).unwrap();
|
||||
let mut records = BTreeMap::<u64, (Vec<u8>, Vec<(u64, Direction, u64)>)>::new();
|
||||
let mut records = BTreeMap::<u64, (Vec<u8>, Vec<(Direction, Vec<u8>)>)>::new();
|
||||
loop {
|
||||
let mut direction = [0; 1];
|
||||
if file.read(&mut direction).unwrap() != 1 {
|
||||
|
|
@ -208,39 +202,84 @@ pub fn read_record_file(path: &str) -> Records {
|
|||
println!("Error: len too large {len}. stop.");
|
||||
break;
|
||||
}
|
||||
let mut buf = vec![0; len as usize];
|
||||
if file.read(&mut buf).unwrap() != len as usize {
|
||||
println!("Error: incomplete data. stop.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Replace URL with unique id, to allow for better tracking by making each request unique.
|
||||
// (proxy may modify some headers, but not the URL)
|
||||
let mut insert_id = |req_id| {
|
||||
if direction == Direction::ClientToServer {
|
||||
let mut spaces = buf
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, c)| if *c == b' ' { Some(i) } else { None });
|
||||
let s1 = spaces.next().unwrap();
|
||||
let s2 = spaces.next().unwrap();
|
||||
let new_url = format!("/{conn_id}-{req_id}/");
|
||||
if s2 - s1 - 1 < new_url.len() {
|
||||
// Not optimal but good enough
|
||||
let mut new_buf = Vec::new();
|
||||
new_buf.extend_from_slice(&buf[0..s1 + 1]);
|
||||
new_buf.extend_from_slice(new_url.as_bytes());
|
||||
new_buf.extend_from_slice(&buf[s2..]);
|
||||
buf = new_buf;
|
||||
} else {
|
||||
buf[s1 + 1..s2][0..new_url.len()].copy_from_slice(new_url.as_bytes());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match records.entry(conn_id) {
|
||||
btree_map::Entry::Occupied(mut entry) => {
|
||||
let req_id = entry.get().1.len() as u64;
|
||||
entry.get_mut().1.push((req_id, direction, len));
|
||||
(insert_id)(entry.get().1.len());
|
||||
entry.get_mut().1.push((direction, buf));
|
||||
}
|
||||
btree_map::Entry::Vacant(entry) => {
|
||||
let req_id = 0;
|
||||
entry.insert((server_name, vec![(req_id, direction, len)]));
|
||||
(insert_id)(0);
|
||||
entry.insert((server_name, vec![(direction, buf)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
records
|
||||
}
|
||||
|
||||
pub fn print_records(records: &Records, number: Option<u64>) {
|
||||
for (conn_id, (server_name, records)) in records {
|
||||
pub fn print_records(records: &Records, print_packets: bool, number: Option<u64>) {
|
||||
for (id, (server_name, records)) in records {
|
||||
if let Some(number) = number
|
||||
&& number != *conn_id
|
||||
&& number != *id
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let server_name = str::from_utf8(server_name.as_slice()).unwrap();
|
||||
println!("{conn_id} {server_name}");
|
||||
for (req_id, direction, len) in records {
|
||||
println!("{id} {server_name}");
|
||||
for (direction, data) in records {
|
||||
match direction {
|
||||
Direction::ClientToServer => {
|
||||
println!(" ({req_id}) >> {len}");
|
||||
println!(" >> {}", data.len());
|
||||
}
|
||||
Direction::ServerToClient => {
|
||||
println!(" ({req_id}) << {len}");
|
||||
println!(" << {}", data.len());
|
||||
}
|
||||
}
|
||||
if print_packets {
|
||||
/*let data_tr = if data.len() >= 256 && *direction == Direction::ServerToClient {
|
||||
&data[0..256]
|
||||
} else {
|
||||
data.as_slice()
|
||||
};
|
||||
if let Ok(data_tr) = str::from_utf8(data_tr) {
|
||||
println!(" {data_tr:?}")
|
||||
} else {
|
||||
println!(" {data_tr:?}")
|
||||
}
|
||||
if let Some(header_end) = memchr::memmem::find(data, b"\r\n\r\n") {
|
||||
println!(" --> body len: {}", data.len() - header_end - 4);
|
||||
}*/
|
||||
print_bin(&data[0..data.len().min(8192)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -253,13 +292,7 @@ pub fn make_test_record(path: &str) {
|
|||
.open(path)
|
||||
.unwrap();
|
||||
for (conn_id, server_name, direction, data) in TEST_RECORD {
|
||||
write_record(
|
||||
&mut file,
|
||||
*direction,
|
||||
*conn_id,
|
||||
server_name,
|
||||
data.len() as u64,
|
||||
);
|
||||
write_record(&mut file, *direction, *conn_id, *server_name, *data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -278,9 +311,9 @@ pub fn remove_record(
|
|||
.unwrap();
|
||||
for (conn_id, (server_name, packets)) in records.into_iter() {
|
||||
let server_name = String::from_utf8(server_name).unwrap();
|
||||
for (packet_id, (_req_id, direction, len)) in packets.into_iter().enumerate() {
|
||||
for (packet_id, (direction, data)) in packets.into_iter().enumerate() {
|
||||
if conn_id != record_to_remove || packet_id != packet_to_remove {
|
||||
write_record(&mut output_file, direction, conn_id, &server_name, len);
|
||||
write_record(&mut output_file, direction, conn_id, &server_name, &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
338
src/server.rs
338
src/server.rs
|
|
@ -1,7 +1,12 @@
|
|||
use crate::record::{Direction, Records};
|
||||
use crate::{
|
||||
TlsMode,
|
||||
record::{Direction, Records},
|
||||
util::print_bin,
|
||||
};
|
||||
|
||||
use futures_util::stream::StreamExt;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::{io::AsyncWriteExt, net::TcpListener};
|
||||
use tokio::{io::AsyncWriteExt, net::TcpListener, sync::oneshot};
|
||||
use tokio_rustls::rustls::{
|
||||
pki_types::{
|
||||
CertificateDer, PrivateKeyDer,
|
||||
|
|
@ -10,61 +15,80 @@ use tokio_rustls::rustls::{
|
|||
server::ResolvesServerCertUsingSni,
|
||||
sign::CertifiedKey,
|
||||
};
|
||||
use tokio_util::codec::Framed;
|
||||
use x509_parser::prelude::GeneralName;
|
||||
|
||||
pub async fn play(
|
||||
records: &'static Records,
|
||||
use_tls: bool,
|
||||
tls_mode: TlsMode,
|
||||
cert_path: &str,
|
||||
listen_addr: (&str, u16),
|
||||
_debug: bool,
|
||||
sync_sender: oneshot::Sender<()>,
|
||||
debug: bool,
|
||||
) {
|
||||
let mut response_map = HashMap::new();
|
||||
for (conn_id, (_server_name, records)) in records.iter() {
|
||||
let mut last_client_req_id = None;
|
||||
for (id, (server_name, records)) in records.iter() {
|
||||
let mut hash = None;
|
||||
let mut responses = Vec::new();
|
||||
for (req_id, direction, len) in records {
|
||||
for (direction, data) in records {
|
||||
match direction {
|
||||
Direction::ClientToServer => {
|
||||
if let Some(last_client_req_id) = last_client_req_id
|
||||
if let Some(hash) = hash
|
||||
&& !responses.is_empty()
|
||||
{
|
||||
response_map.insert((*conn_id, last_client_req_id), (responses, false));
|
||||
response_map.insert((server_name.to_vec(), hash), (id, responses, false));
|
||||
responses = Vec::new();
|
||||
}
|
||||
last_client_req_id = Some(*req_id);
|
||||
let mut slashes = data
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, c)| if *c == b'/' { Some(i) } else { None });
|
||||
let s1 = slashes.next();
|
||||
let s2 = slashes.next();
|
||||
hash = Some(if let (Some(s1), Some(s2)) = (s1, s2) {
|
||||
data[s1 + 1..s2].to_vec()
|
||||
} else {
|
||||
panic!("Did not find URL: {:?}", &data[0..256]);
|
||||
tlsh::hash_buf(data)
|
||||
.map_or_else(|_| data.clone(), |h| h.to_string().into_bytes())
|
||||
});
|
||||
}
|
||||
Direction::ServerToClient => {
|
||||
responses.push((*req_id, *len));
|
||||
responses.push(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(last_client_req_id) = last_client_req_id {
|
||||
if let Some(hash) = hash {
|
||||
if !responses.is_empty() {
|
||||
response_map.insert((*conn_id, last_client_req_id), (responses, true));
|
||||
} else if let Some(entry) = response_map.get_mut(&(*conn_id, last_client_req_id)) {
|
||||
entry.1 = true;
|
||||
response_map.insert((server_name.to_vec(), hash), (id, responses, true));
|
||||
} else {
|
||||
response_map
|
||||
.get_mut(&(server_name.to_vec(), hash))
|
||||
.unwrap()
|
||||
.2 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let response_map = Arc::new(response_map);
|
||||
let dummy_bytes = Arc::new(vec![0x42u8; 16 * 1024 * 1024]);
|
||||
|
||||
if use_tls {
|
||||
match tls_mode {
|
||||
TlsMode::Both | TlsMode::Server => {
|
||||
let mut resolver = ResolvesServerCertUsingSni::new();
|
||||
let mut config = tokio_rustls::rustls::ServerConfig::builder()
|
||||
.with_no_client_auth()
|
||||
.with_cert_resolver(Arc::new(ResolvesServerCertUsingSni::new()));
|
||||
config.max_early_data_size = 8192;
|
||||
for file in std::fs::read_dir(cert_path)
|
||||
.unwrap_or_else(|e| panic!("Cannot read certificate directory `{cert_path}`: {e:?}"))
|
||||
{
|
||||
for file in std::fs::read_dir(cert_path).unwrap_or_else(|e| {
|
||||
panic!("Cannot read certificate directory `{cert_path}`: {e:?}")
|
||||
}) {
|
||||
match file {
|
||||
Ok(file) => {
|
||||
if file.file_name().as_encoded_bytes().ends_with(b".crt") {
|
||||
for section in
|
||||
<(pem::SectionKind, Vec<u8>) as PemObject>::pem_file_iter(file.path())
|
||||
<(pem::SectionKind, Vec<u8>) as PemObject>::pem_file_iter(
|
||||
file.path(),
|
||||
)
|
||||
.unwrap()
|
||||
{
|
||||
let (kind, data) = section.unwrap();
|
||||
|
|
@ -75,12 +99,14 @@ pub async fn play(
|
|||
//println!("File: {:?}", file.file_name());
|
||||
let mut key_path = file.path().to_path_buf();
|
||||
key_path.pop();
|
||||
let file_name = file.file_name().to_str().unwrap().to_string();
|
||||
let file_name =
|
||||
file.file_name().to_str().unwrap().to_string();
|
||||
let mut key_file_name =
|
||||
file_name[0..file_name.len() - 4].to_string();
|
||||
key_file_name.push_str(".key");
|
||||
let key =
|
||||
PrivateKeyDer::from_pem_file(key_path.join(key_file_name))
|
||||
let key = PrivateKeyDer::from_pem_file(
|
||||
key_path.join(key_file_name),
|
||||
)
|
||||
.unwrap();
|
||||
let key = config
|
||||
.crypto_provider()
|
||||
|
|
@ -125,6 +151,7 @@ pub async fn play(
|
|||
let config = Arc::new(config);
|
||||
|
||||
let listener = TcpListener::bind(listen_addr).await.unwrap();
|
||||
sync_sender.send(()).unwrap();
|
||||
loop {
|
||||
let config = config.clone();
|
||||
let (stream, _peer_addr) = listener.accept().await.unwrap();
|
||||
|
|
@ -134,7 +161,43 @@ pub async fn play(
|
|||
);
|
||||
//let acceptor = acceptor.clone();
|
||||
let response_map = response_map.clone();
|
||||
let dummy_bytes = dummy_bytes.clone();
|
||||
/*let fut = async move {
|
||||
let accepted = acceptor.await.unwrap();
|
||||
let server_name = accepted.client_hello().server_name().unwrap().to_string();
|
||||
let mut stream = accepted.into_stream(config).await.unwrap();
|
||||
let mut req = Vec::new();
|
||||
http::decode_http(&mut req, &mut stream).await;
|
||||
let req_hash = tlsh::hash_buf(&req)
|
||||
.map_or_else(|_| req.clone(), |h| h.to_string().into_bytes());
|
||||
let mut best = None;
|
||||
for (i_server_name, hash) in response_map.keys() {
|
||||
if i_server_name != server_name.as_bytes() {
|
||||
continue;
|
||||
}
|
||||
let diff = compare(&req_hash, hash);
|
||||
if let Some((best_hash, best_diff)) = &mut best {
|
||||
if diff < *best_diff {
|
||||
*best_hash = hash;
|
||||
*best_diff = diff;
|
||||
}
|
||||
} else {
|
||||
best = Some((hash, diff));
|
||||
}
|
||||
}
|
||||
if let Some((hash, _diff)) = best {
|
||||
let (id, responses) = response_map
|
||||
.get(&(server_name.as_bytes().to_vec(), hash.clone()))
|
||||
.unwrap();
|
||||
for &res in responses {
|
||||
println!("[SRV] response for ({}): {} bytes", id, res.len());
|
||||
stream.write_all(res).await.unwrap();
|
||||
stream.flush().await.unwrap();
|
||||
}
|
||||
} else {
|
||||
println!("No response found for SNI=`{server_name}`");
|
||||
}
|
||||
stream.shutdown().await.unwrap();
|
||||
};*/
|
||||
let fut = async move {
|
||||
let accepted = acceptor.await.unwrap();
|
||||
let server_name = accepted
|
||||
|
|
@ -148,49 +211,64 @@ pub async fn play(
|
|||
.await
|
||||
.map_err(|e| panic!("{e:?} with name `{server_name}`"))
|
||||
.unwrap();
|
||||
let mut stream = crate::codec::StreamCodec::new(stream);
|
||||
let mut break_next = false;
|
||||
let mut stream = Framed::new(stream, crate::http::HttpServerCodec::new());
|
||||
//let mut previous = Vec::new();
|
||||
loop {
|
||||
let Ok(req) =
|
||||
tokio::time::timeout(tokio::time::Duration::from_secs(5), stream.next())
|
||||
.await
|
||||
else {
|
||||
if break_next {
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
while let Some(req) = stream.next().await {
|
||||
let req = req.unwrap();
|
||||
if req.len() < 8 {
|
||||
println!("Invalid request");
|
||||
break;
|
||||
}
|
||||
let _expected_len = u32::from_be_bytes(req[0..4].try_into().unwrap()) as u64;
|
||||
let conn_id = u16::from_be_bytes(req[4..6].try_into().unwrap()) as u64;
|
||||
let req_id = u16::from_be_bytes(req[6..8].try_into().unwrap()) as u64;
|
||||
//println!("REQUEST");
|
||||
//print_bin(&req);
|
||||
let req_hash = {
|
||||
let mut slashes = req
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, c)| if *c == b'/' { Some(i) } else { None });
|
||||
let s1 = slashes.next();
|
||||
let s2 = slashes.next();
|
||||
if let (Some(s1), Some(s2)) = (s1, s2) {
|
||||
req[s1 + 1..s2].to_vec()
|
||||
} else {
|
||||
//println!("Previous: {:?}", &previous);
|
||||
println!("Did not find URL: {:?}", &req[0..req.len().min(255)]);
|
||||
tlsh::hash_buf(&req)
|
||||
.map_or_else(|_| req.clone(), |h| h.to_string().into_bytes())
|
||||
}
|
||||
};
|
||||
//previous = req.clone();
|
||||
let mut best = None;
|
||||
for (i_server_name, hash) in response_map.keys() {
|
||||
if i_server_name != server_name.as_bytes() {
|
||||
continue;
|
||||
}
|
||||
let diff = if &req_hash == hash {
|
||||
0
|
||||
} else {
|
||||
compare(&req_hash, hash)
|
||||
};
|
||||
if let Some((best_hash, best_diff)) = &mut best {
|
||||
if diff < *best_diff {
|
||||
*best_hash = hash;
|
||||
*best_diff = diff;
|
||||
}
|
||||
} else {
|
||||
best = Some((hash, diff));
|
||||
}
|
||||
}
|
||||
let stream = stream.get_mut();
|
||||
if let Some((responses, last)) = response_map.get(&(conn_id, req_id)) {
|
||||
if let Some((hash, _diff)) = best {
|
||||
let (id, responses, last) = response_map
|
||||
.get(&(server_name.as_bytes().to_vec(), hash.clone()))
|
||||
.unwrap();
|
||||
//dbg!(id);
|
||||
for (req_id, len) in responses {
|
||||
for &res in responses {
|
||||
//println!("[SRV] response for ({}): {} bytes", id, res.len());
|
||||
let mut data = dummy_bytes[0..*len as usize].to_vec();
|
||||
data[0..4].copy_from_slice(&(*len as u32).to_be_bytes());
|
||||
data[4..6].copy_from_slice(&(conn_id as u16).to_be_bytes());
|
||||
data[6..8].copy_from_slice(&(*req_id as u16).to_be_bytes());
|
||||
stream.write_all(&data).await.unwrap();
|
||||
stream.write_all(res).await.unwrap();
|
||||
stream.flush().await.unwrap();
|
||||
}
|
||||
if *last {
|
||||
break_next = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
println!("No response found for {conn_id}-{req_id} SNI=`{server_name}`");
|
||||
println!("No response found for SNI=`{server_name}`");
|
||||
}
|
||||
}
|
||||
stream.get_mut().shutdown().await.unwrap();
|
||||
|
|
@ -199,64 +277,118 @@ pub async fn play(
|
|||
fut.await;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
}
|
||||
TlsMode::None | TlsMode::Client => {
|
||||
let listener = TcpListener::bind(listen_addr).await.unwrap_or_else(|e| {
|
||||
println!("Server: Cannot listen: {e:?}");
|
||||
std::process::exit(1)
|
||||
});
|
||||
sync_sender.send(()).unwrap();
|
||||
loop {
|
||||
let (stream, _peer_addr) = listener.accept().await.unwrap();
|
||||
let response_map = response_map.clone();
|
||||
let dummy_bytes = dummy_bytes.clone();
|
||||
let fut = async move {
|
||||
//println!("[SRV] New task");
|
||||
//let mut stream = Framed::new(stream, crate::codec::CustomCodec::new());
|
||||
let mut stream = crate::codec::StreamCodec::new(stream);
|
||||
let mut break_next = false;
|
||||
//let mut previous = Vec::new();
|
||||
loop {
|
||||
let Ok(req) =
|
||||
tokio::time::timeout(tokio::time::Duration::from_secs(5), stream.next())
|
||||
.await
|
||||
else {
|
||||
if break_next {
|
||||
println!("break timeout");
|
||||
break;
|
||||
/*let fut = async move {
|
||||
println!("[SRV] New task");
|
||||
let mut req = Vec::new();
|
||||
http::decode_http(&mut req, &mut stream).await;
|
||||
let req_hash = tlsh::hash_buf(&req)
|
||||
.map_or_else(|_| req.clone(), |h| h.to_string().into_bytes());
|
||||
let mut best = None;
|
||||
for (i_server_name, hash) in response_map.keys() {
|
||||
let diff = compare(&req_hash, hash);
|
||||
if let Some((best_server_name, best_hash, best_diff)) = &mut best {
|
||||
if diff < *best_diff {
|
||||
*best_server_name = i_server_name;
|
||||
*best_hash = hash;
|
||||
*best_diff = diff;
|
||||
}
|
||||
} else {
|
||||
println!("continue");
|
||||
continue;
|
||||
best = Some((i_server_name, hash, diff));
|
||||
}
|
||||
};
|
||||
let req = req.unwrap();
|
||||
if req.len() < 8 {
|
||||
println!("Invalid request");
|
||||
break;
|
||||
}
|
||||
let expected_len = u32::from_be_bytes(req[0..4].try_into().unwrap()) as u64;
|
||||
let conn_id = u16::from_be_bytes(req[4..6].try_into().unwrap()) as u64;
|
||||
let req_id = u16::from_be_bytes(req[6..8].try_into().unwrap()) as u64;
|
||||
//println!("[SRV] ({conn_id}) << {expected_len}");
|
||||
//println!("REQUEST");
|
||||
//print_bin(&req);
|
||||
//previous = req.clone();
|
||||
let stream = stream.get_mut();
|
||||
if let Some((responses, last)) = response_map.get(&(conn_id, req_id)) {
|
||||
//dbg!(id);
|
||||
for (req_id, len) in responses {
|
||||
//println!("[SRV] ({conn_id}) >> {len}");
|
||||
let mut data = dummy_bytes[0..*len as usize].to_vec();
|
||||
data[0..4].copy_from_slice(&(*len as u32).to_be_bytes());
|
||||
data[4..6].copy_from_slice(&(conn_id as u16).to_be_bytes());
|
||||
data[6..8].copy_from_slice(&(*req_id as u16).to_be_bytes());
|
||||
stream.write_all(&data).await.unwrap();
|
||||
if let Some((server_name, hash, _diff)) = best {
|
||||
let (id, responses) = response_map
|
||||
.get(&(server_name.clone(), hash.clone()))
|
||||
.unwrap();
|
||||
for &res in responses {
|
||||
println!("[SRV] response for ({}): {} bytes", id, res.len());
|
||||
stream.write_all(res).await.unwrap();
|
||||
stream.flush().await.unwrap();
|
||||
}
|
||||
} else {
|
||||
println!("[SRV] No response found");
|
||||
}
|
||||
//println!("Server shutdown");
|
||||
stream.shutdown().await.unwrap();
|
||||
};*/
|
||||
let fut = async move {
|
||||
//println!("[SRV] New task");
|
||||
//let mut stream = crate::http::Framer::new(stream, crate::http::HttpServerCodec::new());
|
||||
let mut stream = Framed::new(stream, crate::http::HttpServerCodec::new());
|
||||
//let mut previous = Vec::new();
|
||||
while let Some(req) = stream.next().await {
|
||||
let req = req.unwrap();
|
||||
//println!("REQUEST");
|
||||
//print_bin(&req);
|
||||
//println!("[SRV] << {}", str::from_utf8(&req[..req.len().min(255)]).unwrap());
|
||||
let req_hash = {
|
||||
let mut slashes = req
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, c)| if *c == b'/' { Some(i) } else { None });
|
||||
let s1 = slashes.next();
|
||||
let s2 = slashes.next();
|
||||
if let (Some(s1), Some(s2)) = (s1, s2) {
|
||||
let uniq_id = req[s1 + 1..s2].to_vec();
|
||||
if debug {
|
||||
if let Ok(uniq_id) = str::from_utf8(&uniq_id) {
|
||||
println!("[SRV] ({uniq_id}) << {}", req.len());
|
||||
}
|
||||
}
|
||||
uniq_id
|
||||
} else {
|
||||
//println!("Previous: {:?}", &previous);
|
||||
println!("Did not find URL: {:?}", &req[0..req.len().min(255)]);
|
||||
tlsh::hash_buf(&req)
|
||||
.map_or_else(|_| req.clone(), |h| h.to_string().into_bytes())
|
||||
}
|
||||
};
|
||||
//previous = req.clone();
|
||||
let mut best = None;
|
||||
for (i_server_name, hash) in response_map.keys() {
|
||||
let diff = compare(&req_hash, hash);
|
||||
if let Some((best_server_name, best_hash, best_diff)) = &mut best {
|
||||
if diff < *best_diff {
|
||||
*best_server_name = i_server_name;
|
||||
*best_hash = hash;
|
||||
*best_diff = diff;
|
||||
}
|
||||
} else {
|
||||
best = Some((i_server_name, hash, diff));
|
||||
}
|
||||
}
|
||||
let stream = stream.get_mut();
|
||||
if let Some((server_name, hash, _diff)) = best {
|
||||
let (id, responses, last) = response_map
|
||||
.get(&(server_name.clone(), hash.clone()))
|
||||
.unwrap();
|
||||
//dbg!(id);
|
||||
for &res in responses {
|
||||
if debug {
|
||||
println!("[SRV] ({id}) >> {}", res.len());
|
||||
//println!("[SRV] response for ({}): {} bytes", id, res.len());
|
||||
}
|
||||
stream.write_all(res).await.unwrap();
|
||||
stream.flush().await.unwrap();
|
||||
if debug {
|
||||
println!("[SRV] ({id}) >> {} OK", res.len());
|
||||
}
|
||||
}
|
||||
if *last {
|
||||
break_next = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
println!("No response found for {conn_id}-{req_id}");
|
||||
println!("[SRV] No response found");
|
||||
}
|
||||
}
|
||||
//println!("Server shutdown");
|
||||
|
|
@ -268,4 +400,18 @@ pub async fn play(
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compare(a: &[u8], b: &[u8]) -> u32 {
|
||||
if let (Ok(a), Ok(b)) = (str::from_utf8(a), str::from_utf8(b)) {
|
||||
if let Ok(diff) = tlsh::compare(a, b) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
if a == b {
|
||||
0
|
||||
} else {
|
||||
a.len().max(b.len()) as u32
|
||||
}
|
||||
}
|
||||
|
|
|
|||
476
src/util.rs
476
src/util.rs
|
|
@ -1,8 +1,26 @@
|
|||
use std::iter::Peekable;
|
||||
|
||||
use crate::record::Direction;
|
||||
|
||||
use log::info;
|
||||
use std::iter::Peekable;
|
||||
use tokio_rustls::rustls::crypto::CryptoProvider;
|
||||
fn hex_digit(c: u8) -> u32 {
|
||||
((c & !(16 | 32 | 64)) + ((c & 64) >> 6) * 9) as _
|
||||
}
|
||||
|
||||
pub fn parse_hex(s: &[u8]) -> u32 {
|
||||
let mut r = 0;
|
||||
for i in s.iter() {
|
||||
r <<= 4;
|
||||
r |= hex_digit(*i);
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
pub fn is_hex(s: &[u8]) -> bool {
|
||||
s.iter().all(|c| {
|
||||
let c = *c | 32;
|
||||
(c >= b'a' && c <= b'f') || (c >= b'0' && c <= b'9')
|
||||
})
|
||||
}
|
||||
|
||||
/// Print ASCII if possible
|
||||
pub fn print_bin(s: &[u8]) {
|
||||
|
|
@ -28,407 +46,85 @@ pub fn print_bin(s: &[u8]) {
|
|||
|
||||
pub struct ResponseStreamer<I: Iterator>(Peekable<I>);
|
||||
|
||||
impl<I: Iterator> ResponseStreamer<I> {
|
||||
impl<'a, I: Iterator> ResponseStreamer<I> {
|
||||
pub fn new(inner: I) -> Self {
|
||||
Self(inner.peekable())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, I: Iterator<Item = &'a (u64, Direction, u64)>> Iterator for ResponseStreamer<I> {
|
||||
type Item = (Direction, Vec<(u64, u64)>);
|
||||
impl<'a, I: Iterator<Item = &'a (Direction, Vec<u8>)>> Iterator for ResponseStreamer<I> {
|
||||
type Item = (&'a Direction, Vec<&'a Vec<u8>>);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (first_req_id, first_direction, first_len) = self.0.next()?;
|
||||
let mut items = vec![(*first_req_id, *first_len)];
|
||||
while let Some((_req_id, direction, _len)) = self.0.peek()
|
||||
&& direction == first_direction
|
||||
let (direction, first_item) = self.0.next()?;
|
||||
let mut items = vec![first_item];
|
||||
while let Some((item_direction, _item)) = self.0.peek()
|
||||
&& item_direction == direction
|
||||
{
|
||||
let (req_id, _direction, len) = self.0.next().unwrap();
|
||||
items.push((*req_id, *len));
|
||||
items.push(&self.0.next().unwrap().1);
|
||||
}
|
||||
Some((*first_direction, items))
|
||||
Some((direction, items))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_provider() {
|
||||
let mut ciphers: Option<Vec<String>> = None;
|
||||
let mut kexes: Option<Vec<String>> = None;
|
||||
for (var, val) in std::env::vars() {
|
||||
match var.as_str() {
|
||||
"CIPHERS" => ciphers = Some(val.split(',').map(str::to_string).collect()),
|
||||
"KEXES" => kexes = Some(val.split(',').map(str::to_string).collect()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Ensure multiple provider cannot be enabled without compile error.
|
||||
let _provider;
|
||||
#[cfg(feature = "aws-lc")]
|
||||
{
|
||||
info!("Using RusTLS provider aws-lc");
|
||||
let mut prov = rustls_post_quantum::provider();
|
||||
if let Some(ciphers) = ciphers {
|
||||
prov.cipher_suites.clear();
|
||||
for cipher in ciphers {
|
||||
match cipher.as_str() {
|
||||
"AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384),
|
||||
"AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_128_GCM_SHA256),
|
||||
"CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_RSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_RSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
other => {
|
||||
log::error!("Unknown cipher `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(kexes) = kexes {
|
||||
prov.kx_groups.clear();
|
||||
for kex in kexes {
|
||||
match kex.as_str() {
|
||||
"X25519" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::X25519),
|
||||
"SECP256R1" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::SECP256R1),
|
||||
"SECP384R1" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::SECP384R1),
|
||||
"X25519MLKEM768" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::X25519MLKEM768),
|
||||
"SECP256R1MLKEM768" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::SECP256R1MLKEM768),
|
||||
"MLKEM768" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::aws_lc_rs::kx_group::MLKEM768),
|
||||
other => {
|
||||
log::error!("Unknown kex `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_provider = CryptoProvider::install_default(prov);
|
||||
}
|
||||
#[cfg(feature = "boring")]
|
||||
{
|
||||
info!("Using RusTLS provider boring");
|
||||
let mut prov = boring_rustls_provider::provider();
|
||||
if let Some(ciphers) = ciphers {
|
||||
prov.cipher_suites.clear();
|
||||
for cipher in ciphers {
|
||||
match cipher.as_str() {
|
||||
"AES_256_GCM_SHA384" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls13(
|
||||
&boring_rustls_provider::tls13::AES_256_GCM_SHA384,
|
||||
)),
|
||||
"AES_128_GCM_SHA256" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls13(
|
||||
&boring_rustls_provider::tls13::AES_128_GCM_SHA256,
|
||||
)),
|
||||
"CHACHA20_POLY1305_SHA256" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls13(
|
||||
&boring_rustls_provider::tls13::CHACHA20_POLY1305_SHA256,
|
||||
)),
|
||||
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls12(
|
||||
&boring_rustls_provider::tls12::ECDHE_ECDSA_AES256_GCM_SHA384,
|
||||
)),
|
||||
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls12(
|
||||
&boring_rustls_provider::tls12::ECDHE_ECDSA_AES128_GCM_SHA256,
|
||||
)),
|
||||
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls12(
|
||||
&boring_rustls_provider::tls12::ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
)),
|
||||
"ECDHE_RSA_WITH_AES_256_GCM_SHA384" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls12(
|
||||
&boring_rustls_provider::tls12::ECDHE_RSA_AES256_GCM_SHA384,
|
||||
)),
|
||||
"ECDHE_RSA_WITH_AES_128_GCM_SHA256" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls12(
|
||||
&boring_rustls_provider::tls12::ECDHE_RSA_AES128_GCM_SHA256,
|
||||
)),
|
||||
"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => prov.cipher_suites.push(tokio_rustls::rustls::SupportedCipherSuite::Tls12(
|
||||
&boring_rustls_provider::tls12::ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
)),
|
||||
other => {
|
||||
log::error!("Unknown cipher `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(kexes) = kexes {
|
||||
prov.kx_groups.clear();
|
||||
for kex in kexes {
|
||||
match kex.as_str() {
|
||||
"X25519" => prov
|
||||
.kx_groups
|
||||
.push(boring_rustls_provider::ALL_KX_GROUPS[0]),
|
||||
"SECP256R1" => prov
|
||||
.kx_groups
|
||||
.push(boring_rustls_provider::ALL_KX_GROUPS[2]),
|
||||
"SECP384R1" => prov
|
||||
.kx_groups
|
||||
.push(boring_rustls_provider::ALL_KX_GROUPS[3]),
|
||||
other => {
|
||||
log::error!("Unknown kex `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_provider = CryptoProvider::install_default(prov);
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_hex_digit() {
|
||||
assert_eq!(hex_digit(b'0'), 0);
|
||||
assert_eq!(hex_digit(b'1'), 1);
|
||||
assert_eq!(hex_digit(b'2'), 2);
|
||||
assert_eq!(hex_digit(b'3'), 3);
|
||||
assert_eq!(hex_digit(b'4'), 4);
|
||||
assert_eq!(hex_digit(b'5'), 5);
|
||||
assert_eq!(hex_digit(b'6'), 6);
|
||||
assert_eq!(hex_digit(b'7'), 7);
|
||||
assert_eq!(hex_digit(b'8'), 8);
|
||||
assert_eq!(hex_digit(b'9'), 9);
|
||||
assert_eq!(hex_digit(b'a'), 10);
|
||||
assert_eq!(hex_digit(b'b'), 11);
|
||||
assert_eq!(hex_digit(b'c'), 12);
|
||||
assert_eq!(hex_digit(b'd'), 13);
|
||||
assert_eq!(hex_digit(b'e'), 14);
|
||||
assert_eq!(hex_digit(b'f'), 15);
|
||||
assert_eq!(hex_digit(b'A'), 10);
|
||||
assert_eq!(hex_digit(b'B'), 11);
|
||||
assert_eq!(hex_digit(b'C'), 12);
|
||||
assert_eq!(hex_digit(b'D'), 13);
|
||||
assert_eq!(hex_digit(b'E'), 14);
|
||||
assert_eq!(hex_digit(b'F'), 15);
|
||||
}
|
||||
|
||||
#[cfg(feature = "graviola")]
|
||||
{
|
||||
info!("Using RusTLS provider graviola");
|
||||
let mut prov = rustls_graviola::default_provider();
|
||||
if let Some(ciphers) = ciphers {
|
||||
prov.cipher_suites.clear();
|
||||
for cipher in ciphers {
|
||||
match cipher.as_str() {
|
||||
"AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_graviola::suites::TLS13_AES_256_GCM_SHA384),
|
||||
"AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_graviola::suites::TLS13_AES_128_GCM_SHA256),
|
||||
"CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_graviola::suites::TLS13_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_graviola::suites::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_graviola::suites::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => prov.cipher_suites.push(
|
||||
rustls_graviola::suites::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
),
|
||||
"ECDHE_RSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_graviola::suites::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_RSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_graviola::suites::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_graviola::suites::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
other => {
|
||||
log::error!("Unknown cipher `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(kexes) = kexes {
|
||||
prov.kx_groups.clear();
|
||||
for kex in kexes {
|
||||
match kex.as_str() {
|
||||
"X25519" => prov.kx_groups.push(&rustls_graviola::kx::X25519),
|
||||
"SECP256R1" => prov.kx_groups.push(&rustls_graviola::kx::P256),
|
||||
"SECP384R1" => prov.kx_groups.push(&rustls_graviola::kx::P384),
|
||||
"X25519MLKEM768" => prov.kx_groups.push(rustls_graviola::kx::X25519MLKEM768),
|
||||
other => {
|
||||
log::error!("Unknown kex `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_provider = CryptoProvider::install_default(prov);
|
||||
#[test]
|
||||
fn test_parse_hex() {
|
||||
assert_eq!(parse_hex(b"abc123"), 0xabc123);
|
||||
assert_eq!(parse_hex(b"1"), 1);
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
{
|
||||
info!("Using RusTLS provider openssl");
|
||||
let mut prov = rustls_openssl::default_provider();
|
||||
if let Some(ciphers) = ciphers {
|
||||
prov.cipher_suites.clear();
|
||||
for cipher in ciphers {
|
||||
match cipher.as_str() {
|
||||
"AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_openssl::cipher_suite::TLS13_AES_256_GCM_SHA384),
|
||||
"AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_openssl::cipher_suite::TLS13_AES_128_GCM_SHA256),
|
||||
"CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_openssl::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => prov.cipher_suites.push(
|
||||
rustls_openssl::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
),
|
||||
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => prov.cipher_suites.push(
|
||||
rustls_openssl::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
),
|
||||
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => prov.cipher_suites.push(
|
||||
rustls_openssl::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
),
|
||||
"ECDHE_RSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_RSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => prov.cipher_suites.push(
|
||||
rustls_openssl::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
||||
),
|
||||
other => {
|
||||
log::error!("Unknown cipher `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(kexes) = kexes {
|
||||
prov.kx_groups.clear();
|
||||
for kex in kexes {
|
||||
match kex.as_str() {
|
||||
"X25519" => prov.kx_groups.push(rustls_openssl::kx_group::X25519),
|
||||
"SECP256R1" => prov.kx_groups.push(rustls_openssl::kx_group::SECP256R1),
|
||||
"SECP384R1" => prov.kx_groups.push(rustls_openssl::kx_group::SECP384R1),
|
||||
"X25519MLKEM768" => prov
|
||||
.kx_groups
|
||||
.push(rustls_openssl::kx_group::X25519MLKEM768),
|
||||
"MLKEM768" => prov.kx_groups.push(rustls_openssl::kx_group::MLKEM768),
|
||||
other => {
|
||||
log::error!("Unknown kex `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_provider = CryptoProvider::install_default(prov);
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
{
|
||||
info!("Using RusTLS provider ring");
|
||||
let mut prov = tokio_rustls::rustls::crypto::ring::default_provider();
|
||||
if let Some(ciphers) = ciphers {
|
||||
prov.cipher_suites.clear();
|
||||
for cipher in ciphers {
|
||||
match cipher.as_str() {
|
||||
"AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS13_AES_256_GCM_SHA384),
|
||||
"AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS13_AES_128_GCM_SHA256),
|
||||
"CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_RSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_RSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(tokio_rustls::rustls::crypto::ring::cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
other => {
|
||||
log::error!("Unknown cipher `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(kexes) = kexes {
|
||||
prov.kx_groups.clear();
|
||||
for kex in kexes {
|
||||
match kex.as_str() {
|
||||
"X25519" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::ring::kx_group::X25519),
|
||||
"SECP256R1" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::ring::kx_group::SECP256R1),
|
||||
"SECP384R1" => prov
|
||||
.kx_groups
|
||||
.push(tokio_rustls::rustls::crypto::ring::kx_group::SECP384R1),
|
||||
other => {
|
||||
log::error!("Unknown kex `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_provider = CryptoProvider::install_default(prov);
|
||||
}
|
||||
#[cfg(feature = "symcrypt")]
|
||||
{
|
||||
info!("Using RusTLS provider symcrypt");
|
||||
let mut prov = rustls_symcrypt::default_symcrypt_provider();
|
||||
if let Some(ciphers) = ciphers {
|
||||
prov.cipher_suites.clear();
|
||||
for cipher in ciphers {
|
||||
match cipher.as_str() {
|
||||
"AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS13_AES_256_GCM_SHA384),
|
||||
"AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS13_AES_128_GCM_SHA256),
|
||||
"CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS13_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
"ECDHE_RSA_WITH_AES_256_GCM_SHA384" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384),
|
||||
"ECDHE_RSA_WITH_AES_128_GCM_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256),
|
||||
"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" => prov
|
||||
.cipher_suites
|
||||
.push(rustls_symcrypt::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256),
|
||||
other => {
|
||||
log::error!("Unknown cipher `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(kexes) = kexes {
|
||||
prov.kx_groups.clear();
|
||||
for kex in kexes {
|
||||
match kex.as_str() {
|
||||
"X25519" => prov.kx_groups.push(rustls_symcrypt::X25519),
|
||||
"SECP256R1" => prov.kx_groups.push(rustls_symcrypt::SECP256R1),
|
||||
"SECP384R1" => prov.kx_groups.push(rustls_symcrypt::SECP384R1),
|
||||
other => {
|
||||
log::error!("Unknown kex `{other}`")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_provider = CryptoProvider::install_default(prov);
|
||||
#[test]
|
||||
fn test_is_hex() {
|
||||
assert!(is_hex(b"0"));
|
||||
assert!(is_hex(b"1"));
|
||||
assert!(is_hex(b"2"));
|
||||
assert!(is_hex(b"3"));
|
||||
assert!(is_hex(b"4"));
|
||||
assert!(is_hex(b"5"));
|
||||
assert!(is_hex(b"6"));
|
||||
assert!(is_hex(b"7"));
|
||||
assert!(is_hex(b"8"));
|
||||
assert!(is_hex(b"9"));
|
||||
assert!(is_hex(b"a"));
|
||||
assert!(is_hex(b"b"));
|
||||
assert!(is_hex(b"c"));
|
||||
assert!(is_hex(b"d"));
|
||||
assert!(is_hex(b"e"));
|
||||
assert!(is_hex(b"f"));
|
||||
assert!(is_hex(b"A"));
|
||||
assert!(is_hex(b"B"));
|
||||
assert!(is_hex(b"C"));
|
||||
assert!(is_hex(b"D"));
|
||||
assert!(is_hex(b"E"));
|
||||
assert!(is_hex(b"F"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue