From 00ab7511cfdc287e638edb810a378511a0974178 Mon Sep 17 00:00:00 2001 From: yinbing Date: Tue, 16 Sep 2025 15:04:36 -0700 Subject: [PATCH 01/35] compile succeed on windows, need to check linux build --- Cargo.lock | 1206 ++++++++++++++++- proxy_agent_shared/Cargo.toml | 15 +- proxy_agent_shared/src/certificates_helper.rs | 535 ++++++++ .../src/client/data_model/error.rs | 63 + .../client/data_model/hostga_plugin_model.rs | 119 ++ .../src/client/data_model/mod.rs | 3 + .../client/data_model/wire_server_model.rs | 363 +++++ .../src/client/hostga_plugin_client.rs | 178 +++ proxy_agent_shared/src/client/mod.rs | 3 + .../src/client/wire_server_client.rs | 86 ++ proxy_agent_shared/src/lib.rs | 2 + 11 files changed, 2526 insertions(+), 47 deletions(-) create mode 100644 proxy_agent_shared/src/certificates_helper.rs create mode 100644 proxy_agent_shared/src/client/data_model/error.rs create mode 100644 proxy_agent_shared/src/client/data_model/hostga_plugin_model.rs create mode 100644 proxy_agent_shared/src/client/data_model/mod.rs create mode 100644 proxy_agent_shared/src/client/data_model/wire_server_model.rs create mode 100644 proxy_agent_shared/src/client/hostga_plugin_client.rs create mode 100644 proxy_agent_shared/src/client/mod.rs create mode 100644 proxy_agent_shared/src/client/wire_server_client.rs diff --git a/Cargo.lock b/Cargo.lock index f07e20dc..9ff4e728 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,7 @@ dependencies = [ "serde_json", "static_vcruntime", "sysinfo", - "thiserror", + "thiserror 1.0.64", "tokio", "windows-service", "winres", @@ -114,7 +114,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -124,7 +124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -133,6 +133,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.3.0" @@ -153,7 +159,7 @@ dependencies = [ "log", "object", "once_cell", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -167,7 +173,7 @@ dependencies = [ "hashbrown 0.15.2", "log", "object", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -197,17 +203,17 @@ dependencies = [ "serde_json", "static_vcruntime", "sysinfo", - "thiserror", + "thiserror 1.0.64", "tokio", "tokio-util", - "tower", + "tower 0.5.2", "tower-http", "uuid", "uzers", "winapi", "windows-acl", "windows-service", - "windows-sys", + "windows-sys 0.52.0", "winres", ] @@ -226,6 +232,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.6.0" @@ -282,7 +294,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -349,6 +361,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -413,18 +435,54 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "field-offset" version = "0.3.6" @@ -447,6 +505,30 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -454,6 +536,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -462,11 +545,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" @@ -481,9 +570,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-io", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -493,8 +586,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", "libc", - "wasi", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -503,6 +612,25 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -612,6 +740,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", @@ -623,6 +752,39 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots 1.0.2", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.8" @@ -630,12 +792,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", + "futures-channel", "futures-util", "http", "http-body", "hyper", "pin-project-lite", + "socket2", "tokio", + "tower 0.4.13", + "tower-service", + "tracing", ] [[package]] @@ -662,6 +829,113 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.7.1" @@ -672,6 +946,12 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -719,12 +999,30 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "log" version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.7.4" @@ -740,6 +1038,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -757,8 +1061,25 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", - "wasi", - "windows-sys", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] @@ -816,6 +1137,50 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_info" version = "3.8.2" @@ -824,7 +1189,33 @@ checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", "serde", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -839,6 +1230,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -870,7 +1276,7 @@ dependencies = [ "clap", "proxy_agent_shared", "static_vcruntime", - "thiserror", + "thiserror 1.0.64", "tokio", "winres", ] @@ -879,54 +1285,150 @@ dependencies = [ name = "proxy_agent_shared" version = "9.9.9" dependencies = [ + "base64", "chrono", "concurrent-queue", "ctor", "log", "once_cell", "os_info", + "quick-xml", "regex", + "reqwest", "serde", "serde-xml-rs", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.64", "thread-id", "time", "tokio", + "uuid", + "windows 0.61.3", "windows-service", - "windows-sys", + "windows-sys 0.52.0", "winreg", ] [[package]] -name = "quote" -version = "1.0.37" +name = "quick-xml" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ - "proc-macro2", + "memchr", + "serde", ] [[package]] -name = "rand" -version = "0.8.5" +name = "quinn" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.16", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.16", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + +[[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[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 0.9.3", ] [[package]] @@ -935,7 +1437,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", ] [[package]] @@ -987,12 +1498,81 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.26.11", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1002,6 +1582,63 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.21" @@ -1014,6 +1651,38 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.23" @@ -1037,7 +1706,7 @@ checksum = "53630160a98edebde0123eb4dfd0fce6adff091b2305db3154a9e920206eb510" dependencies = [ "log", "serde", - "thiserror", + "thiserror 1.0.64", "xml-rs", ] @@ -1064,6 +1733,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1092,9 +1773,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_vcruntime" version = "2.0.0" @@ -1107,6 +1794,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.96" @@ -1123,6 +1816,20 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "sysinfo" @@ -1136,7 +1843,41 @@ dependencies = [ "ntapi", "once_cell", "rayon", - "windows", + "windows 0.52.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.52.0", ] [[package]] @@ -1145,7 +1886,16 @@ version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", ] [[package]] @@ -1159,6 +1909,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread-id" version = "4.2.2" @@ -1200,6 +1961,31 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.43.1" @@ -1207,12 +1993,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "492a604e2fd7f814268a378409e6c92b5525d747d10db9a229723f55a417958c" dependencies = [ "backtrace", + "bytes", "libc", "mio", "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1226,6 +2013,26 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -1248,6 +2055,21 @@ dependencies = [ "serde", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", +] + [[package]] name = "tower" version = "0.5.2" @@ -1327,6 +2149,30 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1339,8 +2185,8 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ - "getrandom", - "rand", + "getrandom 0.2.15", + "rand 0.8.5", "uuid-macro-internal", ] @@ -1365,6 +2211,12 @@ dependencies = [ "log", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -1386,6 +2238,24 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -1412,6 +2282,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -1444,6 +2327,44 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "widestring" version = "0.4.3" @@ -1488,6 +2409,19 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + [[package]] name = "windows-acl" version = "0.3.0" @@ -1500,6 +2434,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -1517,9 +2460,20 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", ] [[package]] @@ -1550,13 +2504,49 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-result" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -1567,7 +2557,17 @@ checksum = "d24d6bcc7f734a4091ecf8d7a64c5f7d7066f45585c1861eba06449909609c8a" dependencies = [ "bitflags", "widestring 1.1.0", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets", ] [[package]] @@ -1576,7 +2576,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -1588,6 +2588,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -1604,6 +2613,15 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -1671,12 +2689,48 @@ dependencies = [ "toml", ] +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "xml-rs" version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -1697,3 +2751,63 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/proxy_agent_shared/Cargo.toml b/proxy_agent_shared/Cargo.toml index f3f4e353..6763e134 100644 --- a/proxy_agent_shared/Cargo.toml +++ b/proxy_agent_shared/Cargo.toml @@ -18,6 +18,10 @@ thiserror = "1.0.64" tokio = { version = "1", features = ["rt", "macros", "sync", "time"] } log = { version = "0.4.26", features = ["std"] } ctor = "0.3.6" # used for test setup and clean up +base64 = "0.22.1" +reqwest = { version = "0.12", features = ["json", "blocking", "rustls-tls"] } +quick-xml = { version = "0.38.0", features = ["serialize", "serde-types"]} +uuid = { version = "1.8", features = ["v4"] } [target.'cfg(windows)'.dependencies] windows-service = "0.7.0" # windows NT service @@ -41,4 +45,13 @@ features = [ ] [target.'cfg(not(windows))'.dependencies] -os_info = "3.7.0" # read Linux OS version and arch \ No newline at end of file +os_info = "3.7.0" # read Linux OS version and arch + +[target.'cfg(windows)'.dependencies.windows] +version = "0.61.3" +features = [ + "Win32_Foundation", + "Win32_Security_Cryptography", + "Win32_System_SystemInformation", + "Win32_System_Memory", +] \ No newline at end of file diff --git a/proxy_agent_shared/src/certificates_helper.rs b/proxy_agent_shared/src/certificates_helper.rs new file mode 100644 index 00000000..bce6e82f --- /dev/null +++ b/proxy_agent_shared/src/certificates_helper.rs @@ -0,0 +1,535 @@ +use base64::Engine; +use uuid::Uuid; +use windows::{ + core::{BOOL, PCWSTR, PSTR}, + Win32::{ + Security::Cryptography::{ + szOID_KEY_USAGE, szOID_SUBJECT_KEY_IDENTIFIER, CertCloseStore, + CertCreateSelfSignCertificate, CertFindCertificateInStore, CertFreeCertificateContext, + CertOpenStore, CertStrToNameW, CryptAcquireCertificatePrivateKey, CryptEncodeObjectEx, + CryptExportPublicKeyInfo, CryptHashPublicKeyInfo, CryptMsgClose, CryptMsgControl, + CryptMsgGetParam, CryptMsgOpenToDecode, CryptMsgUpdate, NCryptCreatePersistedKey, + NCryptFinalizeKey, NCryptFreeObject, NCryptOpenStorageProvider, NCryptSetProperty, + CALG_SHA1, CERT_CONTEXT, CERT_DATA_ENCIPHERMENT_KEY_USAGE, + CERT_DIGITAL_SIGNATURE_KEY_USAGE, CERT_EXTENSION, CERT_EXTENSIONS, CERT_FIND_HASH, + CERT_KEY_CERT_SIGN_KEY_USAGE, CERT_KEY_ENCIPHERMENT_KEY_USAGE, CERT_KEY_SPEC, + CERT_OFFLINE_CRL_SIGN_KEY_USAGE, CERT_PUBLIC_KEY_INFO, CERT_STORE_MAXIMUM_ALLOWED_FLAG, + CERT_STORE_PROV_SYSTEM_W, CERT_SYSTEM_STORE_LOCAL_MACHINE, CERT_X500_NAME_STR, + CMSG_CTRL_DECRYPT, CMSG_CTRL_DECRYPT_PARA, CMSG_CTRL_DECRYPT_PARA_0, + CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, + CRYPT_ACQUIRE_FLAGS, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, CRYPT_BIT_BLOB, + CRYPT_INTEGER_BLOB, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, + MS_KEY_STORAGE_PROVIDER, NCRYPT_ALLOW_EXPORT_FLAG, NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG, + NCRYPT_EXPORT_POLICY_PROPERTY, NCRYPT_HANDLE, NCRYPT_KEY_HANDLE, + NCRYPT_LENGTH_PROPERTY, NCRYPT_PROV_HANDLE, NCRYPT_RSA_ALGORITHM, PKCS_7_ASN_ENCODING, + X509_ASN_ENCODING, + }, + System::SystemInformation::GetSystemTime, + }, +}; + +use crate::client::data_model::error::ErrorDetails; + +pub struct CertificateDetails { + pub public_key_der: Vec, + pub p_cert_ctx: *mut CERT_CONTEXT, +} + +impl Drop for CertificateDetails { + fn drop(&mut self) { + if !self.p_cert_ctx.is_null() { + if !unsafe { CertFreeCertificateContext(Some(self.p_cert_ctx)) }.as_bool() { + eprintln!("Failed to free certificate context.") + } + } + } +} + +pub fn generate_self_signed_certificate( + subject_name: &str, +) -> Result { + unsafe { + // Open KSP + let mut h_prov = NCRYPT_PROV_HANDLE(0); + NCryptOpenStorageProvider(&mut h_prov, MS_KEY_STORAGE_PROVIDER, 0)?; + + // Create an RSA key + let key_name: Vec = Uuid::new_v4().to_string().encode_utf16().collect(); + let mut h_key = NCRYPT_KEY_HANDLE(0); + NCryptCreatePersistedKey( + h_prov, + &mut h_key, + NCRYPT_RSA_ALGORITHM, + PCWSTR(key_name.as_ptr()), // not NULL + windows::Win32::Security::Cryptography::CERT_KEY_SPEC(0), + windows::Win32::Security::Cryptography::NCRYPT_FLAGS(0), + )?; + + // Set key length property to 2048 bits + let key_length: u32 = 2048; + NCryptSetProperty( + h_key.into(), + NCRYPT_LENGTH_PROPERTY, + &key_length.to_ne_bytes(), + windows::Win32::Security::Cryptography::NCRYPT_FLAGS(0), + )?; + + // Set key export policy + NCryptSetProperty( + h_key.into(), + NCRYPT_EXPORT_POLICY_PROPERTY, + &(NCRYPT_ALLOW_EXPORT_FLAG | NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG).to_ne_bytes(), + windows::Win32::Security::Cryptography::NCRYPT_FLAGS(0), + )?; + + // Finalize the key + NCryptFinalizeKey( + h_key, + windows::Win32::Security::Cryptography::NCRYPT_FLAGS(0), + )?; + + // Set up subject name for cert + let subject = format!("CN={}", subject_name); + let subject_w: Vec = subject.encode_utf16().chain(Some(0)).collect(); + let mut size = 0u32; + + CertStrToNameW( + X509_ASN_ENCODING, + PCWSTR(subject_w.as_ptr()), + CERT_X500_NAME_STR, + Some(std::ptr::null_mut()), + Some(std::ptr::null_mut()), + &mut size, + Some(std::ptr::null_mut()), + )?; + let mut name_buf = vec![0u8; size as usize]; + CertStrToNameW( + X509_ASN_ENCODING, + PCWSTR(subject_w.as_ptr()), + CERT_X500_NAME_STR, + None, + Some(name_buf.as_mut_ptr()), + &mut size, + Some(std::ptr::null_mut()), + )?; + + let subject_blob = CRYPT_INTEGER_BLOB { + cbData: size, + pbData: name_buf.as_mut_ptr(), + }; + + // Validity period + let mut start = GetSystemTime(); + start.wYear -= 1; + let mut end = GetSystemTime(); + end.wYear += 3; + + let mut exts = build_cert_extensions(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(h_key.0))?; + + let cert_exts = CERT_EXTENSIONS { + cExtension: exts.len() as u32, + rgExtension: exts.as_mut_ptr(), + }; + + // Create cert + let cert_ctx = CertCreateSelfSignCertificate( + Some(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(h_key.0)), + &subject_blob, + windows::Win32::Security::Cryptography::CERT_CREATE_SELFSIGN_FLAGS(0), + None, + None, + Some(&start), + Some(&end), + Some(&cert_exts), + ); + + // Get cert data + let cert_der = std::slice::from_raw_parts( + (*cert_ctx).pbCertEncoded, + (*cert_ctx).cbCertEncoded as usize, + ); + + let res = CertificateDetails { + public_key_der: cert_der.to_vec(), + p_cert_ctx: cert_ctx, + }; + + // Cleanup + NCryptFreeObject(h_prov.into())?; + NCryptFreeObject(h_key.into())?; + Ok(res) + } +} + +fn build_cert_extensions( + h_key: HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, +) -> Result, ErrorDetails> { + let mut extensions: Vec = Vec::new(); + + // Key Usage + let key_usage: u8 = CERT_DIGITAL_SIGNATURE_KEY_USAGE as u8 + | CERT_KEY_CERT_SIGN_KEY_USAGE as u8 + | CERT_OFFLINE_CRL_SIGN_KEY_USAGE as u8 + | CERT_KEY_ENCIPHERMENT_KEY_USAGE as u8 + | CERT_DATA_ENCIPHERMENT_KEY_USAGE as u8; + + let key_usage_blob = CRYPT_BIT_BLOB { + cbData: 1, + pbData: &key_usage as *const u8 as *mut u8, + cUnusedBits: 0, + }; + + let mut encoded_key_usage_len: u32 = 0; + unsafe { + CryptEncodeObjectEx( + X509_ASN_ENCODING, + szOID_KEY_USAGE, + &key_usage_blob as *const _ as *const _, + windows::Win32::Security::Cryptography::CRYPT_ENCODE_OBJECT_FLAGS(0), + None, + None, + &mut encoded_key_usage_len, + )?; + } + + let mut encoded_key_usage = vec![0u8; encoded_key_usage_len as usize]; + + unsafe { + CryptEncodeObjectEx( + X509_ASN_ENCODING, + szOID_KEY_USAGE, + &key_usage_blob as *const _ as *const _, + windows::Win32::Security::Cryptography::CRYPT_ENCODE_OBJECT_FLAGS(0), + None, + Some(encoded_key_usage.as_mut_ptr() as *mut _), + &mut encoded_key_usage_len, + )?; + } + + extensions.push(CERT_EXTENSION { + pszObjId: PSTR(szOID_KEY_USAGE.as_ptr() as *mut _), + fCritical: BOOL(0), // FALSE + Value: CRYPT_INTEGER_BLOB { + cbData: encoded_key_usage_len, + pbData: encoded_key_usage.as_mut_ptr(), + }, + }); + + let mut size = 0; + unsafe { + CryptExportPublicKeyInfo(h_key, Some(0), X509_ASN_ENCODING, None, &mut size)?; + } + let mut buffer = vec![0u8; size as usize]; + + let p_info = buffer.as_mut_ptr() as *mut CERT_PUBLIC_KEY_INFO; + + // Subject Key Identifier (let Windows generate it) + unsafe { + CryptExportPublicKeyInfo(h_key, Some(0), X509_ASN_ENCODING, Some(p_info), &mut size) + }?; + + let mut ski_hash = [0u8; 20]; + let mut ski_size = ski_hash.len() as u32; + unsafe { + CryptHashPublicKeyInfo( + None, + CALG_SHA1, + 0, + X509_ASN_ENCODING, + p_info, + Some(ski_hash.as_mut_ptr()), + &mut ski_size, + ) + }?; + + let ski_blob = CRYPT_INTEGER_BLOB { + cbData: ski_size, + pbData: ski_hash.as_mut_ptr(), + }; + + let mut encoded_ski_size = 0; + unsafe { + CryptEncodeObjectEx( + X509_ASN_ENCODING, + szOID_SUBJECT_KEY_IDENTIFIER, + &ski_blob as *const _ as *const _, + windows::Win32::Security::Cryptography::CRYPT_ENCODE_OBJECT_FLAGS(0), + None, + None, + &mut encoded_ski_size, + ) + }?; + + let mut encoded_ski = vec![0u8; encoded_ski_size as usize]; + + unsafe { + CryptEncodeObjectEx( + X509_ASN_ENCODING, + szOID_SUBJECT_KEY_IDENTIFIER, + &ski_blob as *const _ as *const _, + windows::Win32::Security::Cryptography::CRYPT_ENCODE_OBJECT_FLAGS(0), + None, + Some(encoded_ski.as_mut_ptr() as *mut _), + &mut encoded_ski_size, + ) + }?; + + extensions.push(CERT_EXTENSION { + pszObjId: PSTR(szOID_SUBJECT_KEY_IDENTIFIER.as_ptr() as *mut _), + fCritical: BOOL(0), // FALSE + Value: CRYPT_INTEGER_BLOB { + cbData: encoded_ski_size, + pbData: encoded_ski.as_mut_ptr(), + }, + }); + Ok(extensions) +} + +pub fn decrypt_from_base64( + base64_input: &str, + cert_details: &CertificateDetails, +) -> std::result::Result { + let encrypted = base64_input.replace("\r", "").replace("\n", ""); + let cms_blob = base64::engine::general_purpose::STANDARD.decode(encrypted)?; + let res = decrypt_with_cng(cert_details.p_cert_ctx, &cms_blob)?; + let res = String::from_utf8(res)?; + Ok(res) +} + +fn decrypt_with_cng( + p_cert_ctx: *const CERT_CONTEXT, + encrypted_payload: &[u8], +) -> Result, ErrorDetails> { + unsafe { + // Acquire the private key handle using the CNG-compatible function. + let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); + let mut key_spec = CERT_KEY_SPEC(0u32); + let mut must_free = BOOL(0); + CryptAcquireCertificatePrivateKey( + p_cert_ctx, + CRYPT_ACQUIRE_FLAGS( + CRYPT_ACQUIRE_COMPARE_KEY_FLAG.0 + | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG.0 + | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG.0, + ), + None, + &mut h_key, + Some(&mut key_spec), + Some(&mut must_free), + )?; + + // Decode the encrypted message. + let msg_handle = CryptMsgOpenToDecode( + X509_ASN_ENCODING.0 | PKCS_7_ASN_ENCODING.0, + 0, + 0, + None, + None, + None, + ); + + if msg_handle.is_null() { + return Err(ErrorDetails { + code: -1, + message: "Failed to open message handle to decrypt.".to_string(), + }); + } + + CryptMsgUpdate(msg_handle, Some(encrypted_payload), true)?; + + // Create an instance of the nested struct (the union) + let anonymous_union = CMSG_CTRL_DECRYPT_PARA_0 { + hCryptProv: h_key.0, + }; + + // Create the main struct instance + let mut decrypt_para = CMSG_CTRL_DECRYPT_PARA { + cbSize: std::mem::size_of::() as u32, + Anonymous: anonymous_union, + dwKeySpec: key_spec.0, + dwRecipientIndex: 0, + }; + + CryptMsgControl( + msg_handle, + 0, + CMSG_CTRL_DECRYPT, + Some(&mut decrypt_para as *mut _ as *mut _), + )?; + + // Get the decrypted message size. + let mut content_size = 0; + CryptMsgGetParam(msg_handle, 2, 0, None, &mut content_size)?; + + // Get the decrypted message content. + let mut decrypted_data_buffer = vec![0u8; content_size as usize]; + CryptMsgGetParam( + msg_handle, + 2, + 0, + Some(decrypted_data_buffer.as_mut_ptr() as *mut _), + &mut content_size, + )?; + + CryptMsgClose(Some(msg_handle))?; + if must_free.as_bool() { + NCryptFreeObject(NCRYPT_HANDLE(h_key.0))?; + } + + Ok(decrypted_data_buffer) + } +} + +fn has_private_key(cert_ctx: *const CERT_CONTEXT) -> bool { + let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); + let mut key_spec = CERT_KEY_SPEC(0u32); + let mut must_free = BOOL(0); + + match unsafe { + CryptAcquireCertificatePrivateKey( + cert_ctx, + CRYPT_ACQUIRE_FLAGS( + CRYPT_ACQUIRE_COMPARE_KEY_FLAG.0 + | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG.0 + | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG.0, + ), + None, + &mut h_key, + Some(&mut key_spec), + Some(&mut must_free), + ) + } { + Ok(_) => {} + Err(e) => { + eprintln!("Acquire key error: {}", e); + return false; + } + } + + if h_key.is_invalid() { + return false; + } + + if must_free.as_bool() { + unsafe { NCryptFreeObject(NCRYPT_HANDLE(h_key.0)).unwrap_or(()) }; + } + return true; +} + +/// Convert thumbprint hex string like "AB CD EF 12 ..." into Vec +fn parse_thumbprint(thumbprint: &str) -> Vec { + thumbprint + .as_bytes() + .chunks(2) + .map(|pair| { + let s = std::str::from_utf8(pair).unwrap(); + u8::from_str_radix(s, 16).unwrap() + }) + .collect() +} + +pub fn get_cert_by_thumbprint( + thumbprint_str: &str, + store_name: &str, +) -> windows::core::Result { + let thumbprint = parse_thumbprint(thumbprint_str); + let mut store_name: Vec = store_name.encode_utf16().chain(Some(0)).collect(); + unsafe { + let h_store = CertOpenStore( + CERT_STORE_PROV_SYSTEM_W, + windows::Win32::Security::Cryptography::CERT_QUERY_ENCODING_TYPE(0), + Some(windows::Win32::Security::Cryptography::HCRYPTPROV_LEGACY(0)), + windows::Win32::Security::Cryptography::CERT_OPEN_STORE_FLAGS( + CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_MAXIMUM_ALLOWED_FLAG.0, + ), + Some(store_name.as_mut_ptr() as *mut _), + )?; + + if h_store.is_invalid() { + return Err(windows::core::Error::from_win32()); + } + + let mut hash_blob = CRYPT_INTEGER_BLOB { + cbData: thumbprint.len() as u32, + pbData: thumbprint.as_ptr() as *mut u8, + }; + + // Find cert by thumbprint + let p_cert_context = CertFindCertificateInStore( + h_store, + windows::Win32::Security::Cryptography::CERT_QUERY_ENCODING_TYPE(0), + 0, + CERT_FIND_HASH, + Some(&mut hash_blob as *mut _ as *const _), + None, + ); + + CertCloseStore(Some(h_store), 0)?; + + // Get cert data + let cert_der = std::slice::from_raw_parts( + (*p_cert_context).pbCertEncoded, + (*p_cert_context).cbCertEncoded as usize, + ); + + Ok(CertificateDetails { + public_key_der: cert_der.to_vec(), + p_cert_ctx: p_cert_context, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use windows::{ + core::PSTR, + Win32::Security::Cryptography::{ + szOID_NIST_AES256_CBC, CryptEncryptMessage, CRYPT_ENCRYPT_MESSAGE_PARA, + }, + }; + + #[test] + fn test_certificate_decryption() { + let cert = generate_self_signed_certificate(&Uuid::new_v4().to_string()).unwrap(); + + let org_str = "Hello, World!"; + let encrypted = encrypt(&cert, org_str); + + let decrypted = decrypt_from_base64(&encrypted, &cert).unwrap(); + + assert!(decrypted.eq(org_str)) + } + + pub fn encrypt(cert: &CertificateDetails, org_str: &str) -> String { + let mut info = CRYPT_ENCRYPT_MESSAGE_PARA::default(); + info.cbSize = std::mem::size_of::() as u32; + info.dwMsgEncodingType = X509_ASN_ENCODING.0 | PKCS_7_ASN_ENCODING.0; + info.ContentEncryptionAlgorithm.pszObjId = PSTR(szOID_NIST_AES256_CBC.as_ptr() as *mut _); + info.dwFlags = 0; + let cert_ctx_ptrs = [cert.p_cert_ctx as *const _]; + let mut encrypted_size: u32 = 0; + unsafe { + CryptEncryptMessage( + &info, + &cert_ctx_ptrs, + Some(org_str.as_bytes()), + None, + &mut encrypted_size as *mut u32, + ) + .unwrap(); + } + let mut encrypted_data = vec![0u8; encrypted_size as usize]; + unsafe { + CryptEncryptMessage( + &info, + &cert_ctx_ptrs, + Some(org_str.as_bytes()), + Some(encrypted_data.as_mut_ptr()), + &mut encrypted_size as *mut u32, + ) + .unwrap(); + } + return base64::engine::general_purpose::STANDARD.encode(&encrypted_data); + } +} diff --git a/proxy_agent_shared/src/client/data_model/error.rs b/proxy_agent_shared/src/client/data_model/error.rs new file mode 100644 index 00000000..7270fbe4 --- /dev/null +++ b/proxy_agent_shared/src/client/data_model/error.rs @@ -0,0 +1,63 @@ +use std::{fmt, string::FromUtf8Error}; + +use base64::DecodeError; +use reqwest::header::InvalidHeaderValue; + +#[derive(Debug, Clone)] +pub struct ErrorDetails { + pub message: String, + pub code: i32, +} + +impl fmt::Display for ErrorDetails { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl std::error::Error for ErrorDetails {} + +impl From for ErrorDetails { + fn from(value: DecodeError) -> Self { + ErrorDetails { + message: format!("Decode Error: {:?}", value), + code: -1, + } + } +} + +impl From for ErrorDetails { + fn from(value: FromUtf8Error) -> Self { + ErrorDetails { + message: format!("Uft8 Convert Error: {:?}", value), + code: -1, + } + } +} + +impl From for ErrorDetails { + fn from(value: InvalidHeaderValue) -> Self { + ErrorDetails { + message: format!("Invalid Http Header Value: {:?}", value), + code: -1, + } + } +} + +impl From for ErrorDetails { + fn from(value: serde_json::Error) -> Self { + ErrorDetails { + message: format!("Json Error: {:?}", value), + code: -1, + } + } +} + +impl From for ErrorDetails { + fn from(value: windows::core::Error) -> Self { + ErrorDetails { + message: format!("Windows API Error: {:?}", value), + code: -1, + } + } +} diff --git a/proxy_agent_shared/src/client/data_model/hostga_plugin_model.rs b/proxy_agent_shared/src/client/data_model/hostga_plugin_model.rs new file mode 100644 index 00000000..81c680ef --- /dev/null +++ b/proxy_agent_shared/src/client/data_model/hostga_plugin_model.rs @@ -0,0 +1,119 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct VMSettings { + #[serde(rename = "hostGAPluginVersion")] + pub host_ga_plugin_version: Option, + #[serde(rename = "activityId")] + pub activity_id: Option, + #[serde(rename = "correlationId")] + pub correlation_id: Option, + #[serde(rename = "inSvdSeqNo")] + pub in_svd_seq_no: Option, + #[serde(rename = "certificatesRevision")] + pub certificates_revision: Option, + #[serde(rename = "extensionsLastModifiedTickCount")] + pub extensions_last_modified_tick_count: Option, + #[serde(rename = "extensionGoalStatesSource")] + pub extension_goal_states_source: Option, + #[serde(rename = "statusUploadBlob")] + pub status_upload_blob: Option, + #[serde(rename = "gaFamilies")] + pub ga_families: Option>, + #[serde(rename = "extensionGoalStates")] + pub extension_goal_states: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct StatusUploadBlob { + #[serde(rename = "statusBlobType")] + pub status_blob_type: Option, + pub value: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct GaFamily { + pub name: Option, + pub version: Option, + #[serde(rename = "isVersionFromRSM")] + pub is_version_from_rsm: Option, + #[serde(rename = "isVMEnabledForRSMUpgrades")] + pub is_vm_enabled_for_rsm_upgrades: Option, + pub uris: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ExtensionGoalState { + pub name: Option, + pub version: Option, + pub location: Option, + #[serde(rename = "failoverLocation")] + pub failover_location: Option, + #[serde(rename = "additionalLocations")] + pub additional_locations: Option>, + pub state: Option, + #[serde(rename = "autoUpgrade")] + pub auto_upgrade: Option, + #[serde(rename = "runAsStartupTask")] + pub run_as_startup_task: Option, + #[serde(rename = "isJson")] + pub is_json: Option, + #[serde(rename = "useExactVersion")] + pub use_exact_version: Option, + #[serde(rename = "settingsSeqNo")] + pub settings_seq_no: Option, + #[serde(rename = "isMultiConfig")] + pub is_multi_config: Option, + pub settings: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Settings { + #[serde(rename = "protectedSettingsCertThumbprint")] + pub protected_settings_cert_thumbprint: Option, + #[serde(rename = "protectedSettings")] + pub protected_settings: Option, + #[serde(rename = "publicSettings")] + pub public_settings: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RawCertificatesPayload { + #[serde(rename = "Pkcs7BlobWithPfxContents")] + pub pkcs7_blob_with_pfx_contents: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Certificates { + #[serde(rename = "activityId")] + pub activity_id: Option, + + #[serde(rename = "correlationId")] + pub correlation_id: Option, + #[serde(rename = "certificates")] + pub certificates: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Certificate { + #[serde(rename = "name")] + pub name: Option, + + #[serde(rename = "storeName")] + pub store_name: Option, + + #[serde(rename = "configurationLevel")] + pub configuration_level: Option, + + #[serde(rename = "certificateInBase64")] + pub certificate_in_base64: Option, + + #[serde(rename = "includePrivateKey")] + pub include_private_key: Option, + + #[serde(rename = "thumbprint")] + pub thumbprint: Option, + + #[serde(rename = "certificateBlobFormatType")] + pub certificate_blob_format_type: Option, +} diff --git a/proxy_agent_shared/src/client/data_model/mod.rs b/proxy_agent_shared/src/client/data_model/mod.rs new file mode 100644 index 00000000..66b7668e --- /dev/null +++ b/proxy_agent_shared/src/client/data_model/mod.rs @@ -0,0 +1,3 @@ +pub mod wire_server_model; +pub mod hostga_plugin_model; +pub mod error; \ No newline at end of file diff --git a/proxy_agent_shared/src/client/data_model/wire_server_model.rs b/proxy_agent_shared/src/client/data_model/wire_server_model.rs new file mode 100644 index 00000000..04d546ce --- /dev/null +++ b/proxy_agent_shared/src/client/data_model/wire_server_model.rs @@ -0,0 +1,363 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename = "Versions")] +pub struct Versions { + #[serde(rename = "Preferred")] + pub preferred: Preferred, + + #[serde(rename = "Supported")] + pub supported: Supported, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Preferred { + #[serde(rename = "Version")] + pub version: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Supported { + #[serde(rename = "Version")] + pub versions: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename = "GoalState")] +pub struct GoalState { + #[serde(rename = "Version")] + pub version: Option, + + #[serde(rename = "Incarnation")] + pub incarnation: Option, + + #[serde(rename = "Machine")] + pub machine: Option, + + #[serde(rename = "Container")] + pub container: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Machine { + #[serde(rename = "ExpectedState")] + pub expected_state: Option, + + #[serde(rename = "StopRolesDeadlineHint")] + pub stop_roles_deadline_hint: Option, + + #[serde(rename = "LBProbePorts")] + pub lb_probe_ports: Option, + + #[serde(rename = "ExpectHealthReport")] + pub expect_health_report: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct LBProbePorts { + #[serde(rename = "Port")] + pub port: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Container { + #[serde(rename = "ContainerId")] + pub container_id: Option, + + #[serde(rename = "RoleInstanceList")] + pub role_instance_list: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RoleInstanceList { + #[serde(rename = "RoleInstance")] + pub role_instance: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RoleInstance { + #[serde(rename = "InstanceId")] + pub instance_id: Option, + + #[serde(rename = "State")] + pub state: Option, + + #[serde(rename = "Configuration")] + pub configuration: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Configuration { + #[serde(rename = "HostingEnvironmentConfig")] + pub hosting_environment_config: Option, + + #[serde(rename = "SharedConfig")] + pub shared_config: Option, + + #[serde(rename = "ExtensionsConfig")] + pub extensions_config: Option, + + #[serde(rename = "FullConfig")] + pub full_config: Option, + + #[serde(rename = "Certificates")] + pub certificates: Option, + + #[serde(rename = "ConfigName")] + pub config_name: Option, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +#[serde(rename = "RDConfig")] +pub struct RDConfig { + #[serde(rename = "@version")] + pub version: Option, + + #[serde(rename = "StoredCertificates")] + pub stored_certificates: Option, + + #[serde(rename = "Deployment")] + pub deployment: Option, + + #[serde(rename = "Incarnation")] + pub incarnation: Option, + + #[serde(rename = "Role")] + pub role: Option, + + #[serde(rename = "HostingEnvironmentSettings")] + pub hosting_environment_settings: Option, + + #[serde(rename = "ApplicationSettings")] + pub application_settings: Option, + + #[serde(rename = "OutputEndpoints")] + pub output_endpoints: Option, + + #[serde(rename = "Instances")] + pub instances: Option, + + #[serde(rename = "Neighborhoods")] + pub neighborhoods: Option, +} + +// ----- StoredCertificates ----- +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct StoredCertificates { + #[serde(rename = "StoredCertificate", default)] + pub stored_certificate: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct StoredCertificate { + #[serde(rename = "@name")] + pub name: Option, + #[serde(rename = "@certificateId")] + pub certificate_id: Option, + #[serde(rename = "@storeName")] + pub store_name: Option, + #[serde(rename = "@configurationLevel")] + pub configuration_level: Option, +} + +// ----- Deployment ----- +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Deployment { + #[serde(rename = "@name")] + pub name: Option, + #[serde(rename = "@incarnation")] + pub incarnation: Option, + #[serde(rename = "@guid")] + pub guid: Option, + + #[serde(rename = "Service", default)] + pub services: Vec, + #[serde(rename = "ServiceInstance", default)] + pub service_instances: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Service { + #[serde(rename = "@name")] + pub name: Option, + #[serde(rename = "@guid")] + pub guid: Option, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct ServiceInstance { + #[serde(rename = "@name")] + pub name: Option, + #[serde(rename = "@guid")] + pub guid: Option, +} + +// ----- Incarnation ----- +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Incarnation { + #[serde(rename = "@number")] + pub number: Option, + #[serde(rename = "@instance")] + pub instance: Option, + #[serde(rename = "@guid")] + pub guid: Option, +} + +// ----- Role ----- +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Role { + #[serde(rename = "@guid")] + pub guid: Option, + #[serde(rename = "@name")] + pub name: Option, + #[serde(rename = "@hostingEnvironment")] + pub hosting_environment: Option, + #[serde(rename = "@hostingEnvironmentVersion")] + pub hosting_environment_version: Option, + #[serde(rename = "@software")] + pub software: Option, + #[serde(rename = "@softwareType")] + pub software_type: Option, + #[serde(rename = "@entryPoint")] + pub entry_point: Option, + #[serde(rename = "@parameters")] + pub parameters: Option, + #[serde(rename = "@cpu")] + pub cpu: Option, + #[serde(rename = "@memory")] + pub memory: Option, + #[serde(rename = "@bandwidth")] + pub bandwidth: Option, + #[serde(rename = "@isManagementRole")] + pub is_management_role: Option, +} + +// ----- HostingEnvironmentSettings ----- +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct HostingEnvironmentSettings { + #[serde(rename = "@name")] + pub name: Option, + #[serde(rename = "@Runtime")] + pub runtime: Option, + #[serde(rename = "CAS")] + pub cas: Option, + #[serde(rename = "PrivilegeLevel")] + pub privilege_level: Option, + #[serde(rename = "AdditionalProperties")] + pub additional_properties: Option, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct CAS { + #[serde(rename = "@mode")] + pub mode: Option, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct PrivilegeLevel { + #[serde(rename = "@mode")] + pub mode: Option, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct AdditionalProperties { + #[serde(rename = "Extensions")] + pub extensions: Option, // CDATA content +} + +// ----- ApplicationSettings ----- +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct ApplicationSettings { + #[serde(rename = "Setting", default)] + pub settings: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Setting { + #[serde(rename = "@name")] + pub name: Option, + #[serde(rename = "@value")] + pub value: Option, +} + +// ----- Instances ----- +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Instances { + #[serde(rename = "Instance", default)] + pub instances: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Instance { + #[serde(rename = "@id")] + pub id: Option, + #[serde(rename = "@neighborhoodID")] + pub neighborhood_id: Option, + #[serde(rename = "@address")] + pub address: Option, + #[serde(rename = "FaultDomains")] + pub fault_domains: Option, + #[serde(rename = "InputEndpoints")] + pub input_endpoints: Option, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct FaultDomains { + #[serde(rename = "@randomID")] + pub random_id: Option, + #[serde(rename = "@updateID")] + pub update_id: Option, + #[serde(rename = "@updateCount")] + pub update_count: Option, +} + +// ----- Neighborhoods ----- +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Neighborhoods { + #[serde(rename = "Neighborhood", default)] + pub neighborhoods: Vec, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct Neighborhood { + #[serde(rename = "@id")] + pub id: Option, + #[serde(rename = "@innerbandwidth")] + pub innerbandwidth: Option, + #[serde(rename = "@innerlatency")] + pub innerlatency: Option, + #[serde(rename = "@outwardbandwidth")] + pub outwardbandwidth: Option, + #[serde(rename = "@outwardlatency")] + pub outwardlatency: Option, + #[serde(rename = "@parentNeighborhoodID")] + pub parent_neighborhood_id: Option, +} + +#[derive(Debug, Serialize, Deserialize, Default)] +#[serde(rename = "HostingEnvironmentConfig")] +pub struct HostingEnvironmentConfig { + #[serde(rename = "@version")] + pub version: Option, + + #[serde(rename = "@goalStateIncarnation")] + pub goal_state_incarnation: Option, + + #[serde(rename = "StoredCertificates")] + pub stored_certificates: Option, + + #[serde(rename = "Deployment")] + pub deployment: Option, + + #[serde(rename = "Incarnation")] + pub incarnation: Option, + + #[serde(rename = "Role")] + pub role: Option, + + #[serde(rename = "HostingEnvironmentSettings")] + pub hosting_environment_settings: Option, + + #[serde(rename = "ApplicationSettings")] + pub application_settings: Option, +} diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs new file mode 100644 index 00000000..74e7f7fd --- /dev/null +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -0,0 +1,178 @@ +use crate::certificates_helper::{decrypt_from_base64, generate_self_signed_certificate}; +use crate::client::data_model::error::ErrorDetails; +use crate::client::data_model::hostga_plugin_model::{Certificates, RawCertificatesPayload, VMSettings}; +use base64::Engine; +use reqwest::header::HeaderMap; +use reqwest::header::HeaderValue; +use reqwest::{Client, StatusCode}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +pub struct HostGAPluginClient { + base_url: String, + client: Client, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct HostGAPluginResponse { + pub body: Option, + pub etag: Option, + pub certificates_revision: Option, + pub version: Option, +} + +impl HostGAPluginClient { + const CERTIFICATES_URL: &'static str = "certificates"; + const VMSETTINGS_URL: &'static str = "vmSettings"; + + const ETAG_HEADER: &'static str = "etag"; + const X_MS_SERVER_VERSION_HEADER: &'static str = "x-ms-server-version"; + const X_MS_CERTIFICATES_REVISION_HEADER: &'static str = "x-ms-certificates-revision"; + const TRANSPORT_CERTIFICATE_HEADER: &'static str = "x-ms-guest-agent-public-x509-cert"; + const TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER: &'static str = "x-ms-cipher-name"; + + pub fn new(base_url: &str) -> HostGAPluginClient { + HostGAPluginClient { + base_url: base_url.to_string(), + client: Client::new(), + } + } + + pub async fn get_vmsettings(&self) -> Result, ErrorDetails> { + self.get::( + &format!("{}/{}", self.base_url, Self::VMSETTINGS_URL), + Option::None, + ) + .await + } + + pub async fn get_certificates( + &self, + cert_revision: u32, + ) -> Result, ErrorDetails> { + let cert = generate_self_signed_certificate(&Uuid::new_v4().to_string())?; + //let cert = get_cert_by_thumbprint("9bf93bb248b4504626c5d1247da2e7c5f8e0a03a")?; + let cert_der = cert.public_key_der.clone(); + let cert_base64 = base64::engine::general_purpose::STANDARD.encode(cert_der.clone()); + + let mut headers = HeaderMap::new(); + headers.insert( + Self::TRANSPORT_CERTIFICATE_HEADER, + HeaderValue::from_str(&cert_base64)?, + ); + headers.insert( + Self::TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER, + HeaderValue::from_str("AES256_CBC")?, + ); + println!("Requesting certificates with headers: {:?}", headers); + let raw_certs_resp = self + .get::( + &format!( + "{}/{}/{}", + self.base_url, + Self::CERTIFICATES_URL, + cert_revision + ), + Some(headers), + ) + .await?; + + if let Some(cert_base64) = raw_certs_resp + .body + .as_ref() + .and_then(|body| body.pkcs7_blob_with_pfx_contents.as_ref()) + { + let certs = decrypt_from_base64(cert_base64, &cert)?; + + return Ok(HostGAPluginResponse { + body: Some(serde_json::from_str::(&certs)?), + etag: raw_certs_resp.etag.clone(), + certificates_revision: raw_certs_resp.certificates_revision.clone(), + version: raw_certs_resp.version.clone(), + }); + } + + Err(ErrorDetails { + message: format!("certificate payload is empty."), + code: -1, + }) + } + + pub async fn get( + &self, + url: &str, + headers_map: Option, + ) -> Result, ErrorDetails> + where + for<'a> T: Deserialize<'a>, + { + let mut request = self.client.get(url); + println!("Requesting URL: {}", url); + if let Some(headers) = headers_map { + request = request.headers(headers); + } + + let resp = request.send().await.map_err(|e| { + let mut error_code = -1; + if let Some(status) = e.status() { + error_code = status.as_u16() as i32; + } + ErrorDetails { + code: error_code, + message: format!("HostGAPlugin Request Error: {}, url: {}", e, url), + } + })?; + + let headers = resp.headers(); + + let etag = headers + .get(Self::ETAG_HEADER) + .and_then(|v| v.to_str().ok()) + .map(|v| v.to_string()); + + let certificates_revision = headers + .get(Self::X_MS_CERTIFICATES_REVISION_HEADER) + .and_then(|v| v.to_str().ok()) + .and_then(|s| s.parse::().ok()); + + let version = headers + .get(Self::X_MS_SERVER_VERSION_HEADER) + .and_then(|v| v.to_str().ok()) + .map(|v| v.to_string()); + + if resp.status() == StatusCode::NOT_MODIFIED { + let _hostgap_response: HostGAPluginResponse = HostGAPluginResponse { + body: None, + etag: etag, + version: version, + certificates_revision: certificates_revision, + }; + return Ok(_hostgap_response); + } else if resp.status().is_success() { + let body = resp.text().await.map_err(|e| ErrorDetails { + code: -1, + message: format!("Failed to get response body: {}", e), + })?; + let body_json = serde_json::from_str::(&body).map_err(|e| ErrorDetails { + code: -1, + message: format!("Failed to deserialized json payload, error: {}", e), + })?; + return Ok(HostGAPluginResponse { + body: Option::Some(body_json), + etag: etag, + version: version, + certificates_revision: certificates_revision, + }); + } + + let status = resp.status(); + return Err(ErrorDetails { + code: status.as_u16() as i32, + message: format!( + "Http Error Status: {}, Body: {}", + status, + resp.text().await.unwrap_or_default() + ), + }); + } +} diff --git a/proxy_agent_shared/src/client/mod.rs b/proxy_agent_shared/src/client/mod.rs new file mode 100644 index 00000000..b5ace2a7 --- /dev/null +++ b/proxy_agent_shared/src/client/mod.rs @@ -0,0 +1,3 @@ +pub mod data_model; +pub mod wire_server_client; +pub mod hostga_plugin_client; \ No newline at end of file diff --git a/proxy_agent_shared/src/client/wire_server_client.rs b/proxy_agent_shared/src/client/wire_server_client.rs new file mode 100644 index 00000000..a8073922 --- /dev/null +++ b/proxy_agent_shared/src/client/wire_server_client.rs @@ -0,0 +1,86 @@ +use quick_xml::de::from_str; +use reqwest::Client; +use serde::Deserialize; + +use crate::client::data_model::{error::ErrorDetails, wire_server_model::{GoalState, Versions}}; + +pub struct WireServerClient { + base_url: String, + version: String, + client: Client, +} + +impl WireServerClient { + const X_MS_VERSION_HEADER: &'static str = "x-ms-version"; + const VERSIONS_URL: &'static str = "?comp=Versions"; + const GOAL_STATE_URL: &'static str = "machine?comp=goalstate"; + + pub fn new(base_url: &str) -> WireServerClient { + WireServerClient { + base_url: base_url.to_string(), + version: "2015-04-05".to_string(), + client: Client::new(), + } + } + + // http://168.63.129.16?comp=Versions + pub async fn get_versions(&self) -> Result { + self.get::(Self::VERSIONS_URL).await + } + + // http://168.63.129.16/machine?comp=goalstate + pub async fn get_goal_state(&self) -> Result { + self.get::(Self::GOAL_STATE_URL).await + } + + pub async fn get(&self, sub_url: &str) -> Result + where + T: for<'a> Deserialize<'a>, + { + self.get_url(&format!("{}/{}", &self.base_url, sub_url)) + .await + } + + pub async fn get_url(&self, url: &str) -> Result + where + T: for<'a> Deserialize<'a>, + { + match self + .client + .get(url) + .header(Self::X_MS_VERSION_HEADER, &self.version) + .send() + .await + { + Ok(resp) => { + if resp.status().is_success() { + let body = resp.text().await.map_err(|e| ErrorDetails { + code: -1, + message: format!("{}", e), + })?; + let result = from_str::(&body).map_err(|e| ErrorDetails { + code: -2, + message: format!("XML Deserialization Failed: {}", e), + })?; + return Ok(result); + } else { + let status = resp.status(); + return Err(ErrorDetails { + code: status.as_u16() as i32, + message: format!( + "Http Error Status: {}, Body: {}", + status, + resp.text().await.unwrap_or_default() + ), + }); + } + } + Err(e) => { + return Err(ErrorDetails { + code: -3, + message: format!("Request Error: {}", e), + }); + } + } + } +} diff --git a/proxy_agent_shared/src/lib.rs b/proxy_agent_shared/src/lib.rs index 27a01e6a..c3075b5e 100644 --- a/proxy_agent_shared/src/lib.rs +++ b/proxy_agent_shared/src/lib.rs @@ -13,6 +13,8 @@ pub mod telemetry; pub mod version; #[cfg(windows)] pub mod windows; +pub mod client; +pub mod certificates_helper; #[cfg(not(windows))] pub mod linux; From 8bf8ddee7f46a95b60f120bd5b16f6896d0255ed Mon Sep 17 00:00:00 2001 From: yinbing Date: Tue, 16 Sep 2025 15:41:06 -0700 Subject: [PATCH 02/35] small optimize, to-do: unit test --- proxy_agent_shared/src/certificates_helper.rs | 235 +++++++++++------- .../src/client/data_model/mod.rs | 4 +- .../src/client/hostga_plugin_client.rs | 4 +- proxy_agent_shared/src/client/mod.rs | 2 +- .../src/client/wire_server_client.rs | 5 +- proxy_agent_shared/src/lib.rs | 4 +- 6 files changed, 152 insertions(+), 102 deletions(-) diff --git a/proxy_agent_shared/src/certificates_helper.rs b/proxy_agent_shared/src/certificates_helper.rs index bce6e82f..3cd2b8b7 100644 --- a/proxy_agent_shared/src/certificates_helper.rs +++ b/proxy_agent_shared/src/certificates_helper.rs @@ -18,8 +18,8 @@ use windows::{ CMSG_CTRL_DECRYPT, CMSG_CTRL_DECRYPT_PARA, CMSG_CTRL_DECRYPT_PARA_0, CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, CRYPT_ACQUIRE_FLAGS, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, CRYPT_BIT_BLOB, - CRYPT_INTEGER_BLOB, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, - MS_KEY_STORAGE_PROVIDER, NCRYPT_ALLOW_EXPORT_FLAG, NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG, + CRYPT_INTEGER_BLOB, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, MS_KEY_STORAGE_PROVIDER, + NCRYPT_ALLOW_EXPORT_FLAG, NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG, NCRYPT_EXPORT_POLICY_PROPERTY, NCRYPT_HANDLE, NCRYPT_KEY_HANDLE, NCRYPT_LENGTH_PROPERTY, NCRYPT_PROV_HANDLE, NCRYPT_RSA_ALGORITHM, PKCS_7_ASN_ENCODING, X509_ASN_ENCODING, @@ -32,7 +32,10 @@ use crate::client::data_model::error::ErrorDetails; pub struct CertificateDetails { pub public_key_der: Vec, + #[cfg(windows)] pub p_cert_ctx: *mut CERT_CONTEXT, + #[cfg(not(windows))] + pub private_key_der: Vec, } impl Drop for CertificateDetails { @@ -48,14 +51,30 @@ impl Drop for CertificateDetails { pub fn generate_self_signed_certificate( subject_name: &str, ) -> Result { + #[cfg(windows)] + { + return generate_self_signed_certificate_windows(subject_name); + } + #[cfg(not(windows))] + { + todo!() + } +} + +#[cfg(windows)] +fn generate_self_signed_certificate_windows( + subject_name: &str, +) -> Result { + // Open KSP + let mut h_prov = NCRYPT_PROV_HANDLE(0); unsafe { - // Open KSP - let mut h_prov = NCRYPT_PROV_HANDLE(0); NCryptOpenStorageProvider(&mut h_prov, MS_KEY_STORAGE_PROVIDER, 0)?; + } - // Create an RSA key - let key_name: Vec = Uuid::new_v4().to_string().encode_utf16().collect(); - let mut h_key = NCRYPT_KEY_HANDLE(0); + // Create an RSA key + let key_name: Vec = Uuid::new_v4().to_string().encode_utf16().collect(); + let mut h_key = NCRYPT_KEY_HANDLE(0); + unsafe { NCryptCreatePersistedKey( h_prov, &mut h_key, @@ -64,9 +83,11 @@ pub fn generate_self_signed_certificate( windows::Win32::Security::Cryptography::CERT_KEY_SPEC(0), windows::Win32::Security::Cryptography::NCRYPT_FLAGS(0), )?; + } + let key_length: u32 = 2048; + unsafe { // Set key length property to 2048 bits - let key_length: u32 = 2048; NCryptSetProperty( h_key.into(), NCRYPT_LENGTH_PROPERTY, @@ -87,12 +108,13 @@ pub fn generate_self_signed_certificate( h_key, windows::Win32::Security::Cryptography::NCRYPT_FLAGS(0), )?; + } - // Set up subject name for cert - let subject = format!("CN={}", subject_name); - let subject_w: Vec = subject.encode_utf16().chain(Some(0)).collect(); - let mut size = 0u32; - + // Set up subject name for cert + let subject = format!("CN={}", subject_name); + let subject_w: Vec = subject.encode_utf16().chain(Some(0)).collect(); + let mut size = 0u32; + unsafe { CertStrToNameW( X509_ASN_ENCODING, PCWSTR(subject_w.as_ptr()), @@ -102,7 +124,9 @@ pub fn generate_self_signed_certificate( &mut size, Some(std::ptr::null_mut()), )?; - let mut name_buf = vec![0u8; size as usize]; + } + let mut name_buf = vec![0u8; size as usize]; + unsafe { CertStrToNameW( X509_ASN_ENCODING, PCWSTR(subject_w.as_ptr()), @@ -112,27 +136,29 @@ pub fn generate_self_signed_certificate( &mut size, Some(std::ptr::null_mut()), )?; + } - let subject_blob = CRYPT_INTEGER_BLOB { - cbData: size, - pbData: name_buf.as_mut_ptr(), - }; + let subject_blob = CRYPT_INTEGER_BLOB { + cbData: size, + pbData: name_buf.as_mut_ptr(), + }; - // Validity period - let mut start = GetSystemTime(); - start.wYear -= 1; - let mut end = GetSystemTime(); - end.wYear += 3; + // Validity period + let mut start = unsafe { GetSystemTime() }; + start.wYear -= 1; + let mut end = unsafe { GetSystemTime() }; + end.wYear += 3; - let mut exts = build_cert_extensions(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(h_key.0))?; + let mut exts = build_cert_extensions(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(h_key.0))?; - let cert_exts = CERT_EXTENSIONS { - cExtension: exts.len() as u32, - rgExtension: exts.as_mut_ptr(), - }; + let cert_exts = CERT_EXTENSIONS { + cExtension: exts.len() as u32, + rgExtension: exts.as_mut_ptr(), + }; - // Create cert - let cert_ctx = CertCreateSelfSignCertificate( + // Create cert + let cert_ctx = unsafe { + CertCreateSelfSignCertificate( Some(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(h_key.0)), &subject_blob, windows::Win32::Security::Cryptography::CERT_CREATE_SELFSIGN_FLAGS(0), @@ -141,26 +167,31 @@ pub fn generate_self_signed_certificate( Some(&start), Some(&end), Some(&cert_exts), - ); + ) + }; - // Get cert data - let cert_der = std::slice::from_raw_parts( + // Get cert data + let cert_der = unsafe { + std::slice::from_raw_parts( (*cert_ctx).pbCertEncoded, (*cert_ctx).cbCertEncoded as usize, - ); + ) + }; - let res = CertificateDetails { - public_key_der: cert_der.to_vec(), - p_cert_ctx: cert_ctx, - }; + let res = CertificateDetails { + public_key_der: cert_der.to_vec(), + p_cert_ctx: cert_ctx, + }; - // Cleanup + // Cleanup + unsafe { NCryptFreeObject(h_prov.into())?; NCryptFreeObject(h_key.into())?; - Ok(res) - } + }; + Ok(res) } +#[cfg(windows)] fn build_cert_extensions( h_key: HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, ) -> Result, ErrorDetails> { @@ -288,23 +319,31 @@ fn build_cert_extensions( pub fn decrypt_from_base64( base64_input: &str, cert_details: &CertificateDetails, -) -> std::result::Result { - let encrypted = base64_input.replace("\r", "").replace("\n", ""); - let cms_blob = base64::engine::general_purpose::STANDARD.decode(encrypted)?; - let res = decrypt_with_cng(cert_details.p_cert_ctx, &cms_blob)?; - let res = String::from_utf8(res)?; - Ok(res) +) -> Result { + #[cfg(windows)] + { + return decrypt_from_base64_windows(base64_input, cert_details); + } + #[cfg(not(windows))] + { + todo!() + } } -fn decrypt_with_cng( - p_cert_ctx: *const CERT_CONTEXT, - encrypted_payload: &[u8], -) -> Result, ErrorDetails> { +#[cfg(windows)] +fn decrypt_from_base64_windows( + base64_input: &str, + cert_details: &CertificateDetails, +) -> Result { + let encrypted = base64_input.replace("\r", "").replace("\n", ""); + let encrypted_payload = &base64::engine::general_purpose::STANDARD.decode(encrypted)?; + let p_cert_ctx = cert_details.p_cert_ctx; + + // Acquire the private key handle using the CNG-compatible function. + let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); + let mut key_spec = CERT_KEY_SPEC(0u32); + let mut must_free = BOOL(0); unsafe { - // Acquire the private key handle using the CNG-compatible function. - let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); - let mut key_spec = CERT_KEY_SPEC(0u32); - let mut must_free = BOOL(0); CryptAcquireCertificatePrivateKey( p_cert_ctx, CRYPT_ACQUIRE_FLAGS( @@ -316,71 +355,77 @@ fn decrypt_with_cng( &mut h_key, Some(&mut key_spec), Some(&mut must_free), - )?; + ) + }?; - // Decode the encrypted message. - let msg_handle = CryptMsgOpenToDecode( + // Decode the encrypted message. + let msg_handle = unsafe { + CryptMsgOpenToDecode( X509_ASN_ENCODING.0 | PKCS_7_ASN_ENCODING.0, 0, 0, None, None, None, - ); - - if msg_handle.is_null() { - return Err(ErrorDetails { - code: -1, - message: "Failed to open message handle to decrypt.".to_string(), - }); - } - - CryptMsgUpdate(msg_handle, Some(encrypted_payload), true)?; + ) + }; - // Create an instance of the nested struct (the union) - let anonymous_union = CMSG_CTRL_DECRYPT_PARA_0 { - hCryptProv: h_key.0, - }; + if msg_handle.is_null() { + return Err(ErrorDetails { + code: -1, + message: "Failed to open message handle to decrypt.".to_string(), + }); + } + unsafe { CryptMsgUpdate(msg_handle, Some(encrypted_payload), true) }?; + // Create an instance of the nested struct (the union) + let anonymous_union = CMSG_CTRL_DECRYPT_PARA_0 { + hCryptProv: h_key.0, + }; - // Create the main struct instance - let mut decrypt_para = CMSG_CTRL_DECRYPT_PARA { - cbSize: std::mem::size_of::() as u32, - Anonymous: anonymous_union, - dwKeySpec: key_spec.0, - dwRecipientIndex: 0, - }; + // Create the main struct instance + let mut decrypt_para = CMSG_CTRL_DECRYPT_PARA { + cbSize: std::mem::size_of::() as u32, + Anonymous: anonymous_union, + dwKeySpec: key_spec.0, + dwRecipientIndex: 0, + }; + unsafe { CryptMsgControl( msg_handle, 0, CMSG_CTRL_DECRYPT, Some(&mut decrypt_para as *mut _ as *mut _), - )?; + ) + }?; - // Get the decrypted message size. - let mut content_size = 0; - CryptMsgGetParam(msg_handle, 2, 0, None, &mut content_size)?; + // Get the decrypted message size. + let mut content_size = 0; + unsafe { CryptMsgGetParam(msg_handle, 2, 0, None, &mut content_size) }?; - // Get the decrypted message content. - let mut decrypted_data_buffer = vec![0u8; content_size as usize]; + // Get the decrypted message content. + let mut decrypted_data_buffer = vec![0u8; content_size as usize]; + unsafe { CryptMsgGetParam( msg_handle, 2, 0, Some(decrypted_data_buffer.as_mut_ptr() as *mut _), &mut content_size, - )?; - - CryptMsgClose(Some(msg_handle))?; - if must_free.as_bool() { - NCryptFreeObject(NCRYPT_HANDLE(h_key.0))?; - } + ) + }?; - Ok(decrypted_data_buffer) + unsafe { CryptMsgClose(Some(msg_handle)) }?; + if must_free.as_bool() { + unsafe { NCryptFreeObject(NCRYPT_HANDLE(h_key.0)) }?; } + + let res = String::from_utf8(decrypted_data_buffer)?; + Ok(res) } -fn has_private_key(cert_ctx: *const CERT_CONTEXT) -> bool { +#[cfg(windows)] +pub fn has_private_key(cert_ctx: *const CERT_CONTEXT) -> bool { let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); let mut key_spec = CERT_KEY_SPEC(0u32); let mut must_free = BOOL(0); @@ -479,7 +524,7 @@ pub fn get_cert_by_thumbprint( } } -#[cfg(test)] +#[cfg(all(test, windows))] mod tests { use super::*; use windows::{ @@ -501,7 +546,7 @@ mod tests { assert!(decrypted.eq(org_str)) } - pub fn encrypt(cert: &CertificateDetails, org_str: &str) -> String { + fn encrypt(cert: &CertificateDetails, org_str: &str) -> String { let mut info = CRYPT_ENCRYPT_MESSAGE_PARA::default(); info.cbSize = std::mem::size_of::() as u32; info.dwMsgEncodingType = X509_ASN_ENCODING.0 | PKCS_7_ASN_ENCODING.0; diff --git a/proxy_agent_shared/src/client/data_model/mod.rs b/proxy_agent_shared/src/client/data_model/mod.rs index 66b7668e..9664ab58 100644 --- a/proxy_agent_shared/src/client/data_model/mod.rs +++ b/proxy_agent_shared/src/client/data_model/mod.rs @@ -1,3 +1,3 @@ -pub mod wire_server_model; +pub mod error; pub mod hostga_plugin_model; -pub mod error; \ No newline at end of file +pub mod wire_server_model; diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs index 74e7f7fd..42256fa9 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -1,6 +1,8 @@ use crate::certificates_helper::{decrypt_from_base64, generate_self_signed_certificate}; use crate::client::data_model::error::ErrorDetails; -use crate::client::data_model::hostga_plugin_model::{Certificates, RawCertificatesPayload, VMSettings}; +use crate::client::data_model::hostga_plugin_model::{ + Certificates, RawCertificatesPayload, VMSettings, +}; use base64::Engine; use reqwest::header::HeaderMap; use reqwest::header::HeaderValue; diff --git a/proxy_agent_shared/src/client/mod.rs b/proxy_agent_shared/src/client/mod.rs index b5ace2a7..25e8f2b5 100644 --- a/proxy_agent_shared/src/client/mod.rs +++ b/proxy_agent_shared/src/client/mod.rs @@ -1,3 +1,3 @@ pub mod data_model; +pub mod hostga_plugin_client; pub mod wire_server_client; -pub mod hostga_plugin_client; \ No newline at end of file diff --git a/proxy_agent_shared/src/client/wire_server_client.rs b/proxy_agent_shared/src/client/wire_server_client.rs index a8073922..b7691862 100644 --- a/proxy_agent_shared/src/client/wire_server_client.rs +++ b/proxy_agent_shared/src/client/wire_server_client.rs @@ -2,7 +2,10 @@ use quick_xml::de::from_str; use reqwest::Client; use serde::Deserialize; -use crate::client::data_model::{error::ErrorDetails, wire_server_model::{GoalState, Versions}}; +use crate::client::data_model::{ + error::ErrorDetails, + wire_server_model::{GoalState, Versions}, +}; pub struct WireServerClient { base_url: String, diff --git a/proxy_agent_shared/src/lib.rs b/proxy_agent_shared/src/lib.rs index c3075b5e..ccc15ac8 100644 --- a/proxy_agent_shared/src/lib.rs +++ b/proxy_agent_shared/src/lib.rs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT +pub mod certificates_helper; +pub mod client; pub mod error; #[cfg(windows)] pub mod etw; @@ -13,8 +15,6 @@ pub mod telemetry; pub mod version; #[cfg(windows)] pub mod windows; -pub mod client; -pub mod certificates_helper; #[cfg(not(windows))] pub mod linux; From 89a4ea43911eda1488f956c57cff95cf3bcde1b8 Mon Sep 17 00:00:00 2001 From: yinbing Date: Wed, 17 Sep 2025 15:00:19 -0700 Subject: [PATCH 03/35] fixes for build --- .../src/certificate/certificate_helper.rs | 48 ++ .../certificate_helper_windows.rs} | 425 +++++------------- proxy_agent_shared/src/certificate/mod.rs | 3 + .../src/client/hostga_plugin_client.rs | 6 +- proxy_agent_shared/src/lib.rs | 2 +- 5 files changed, 169 insertions(+), 315 deletions(-) create mode 100644 proxy_agent_shared/src/certificate/certificate_helper.rs rename proxy_agent_shared/src/{certificates_helper.rs => certificate/certificate_helper_windows.rs} (57%) create mode 100644 proxy_agent_shared/src/certificate/mod.rs diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs new file mode 100644 index 00000000..020d77cf --- /dev/null +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -0,0 +1,48 @@ + +use crate::{client::data_model::error::ErrorDetails}; + +pub trait CertificateDetails { + type CertificateContext; + fn get_certificate_context(&self) -> &Self::CertificateContext; + fn set_certificate_context(&mut self, cert_context: Self::CertificateContext); + fn get_public_cert_der(&self) -> &[u8]; +} + +#[cfg(windows)] +use windows::Win32::Security::Cryptography::CERT_CONTEXT; +#[cfg(windows)] +type CertCtxType = *mut CERT_CONTEXT; + +#[cfg(not(windows))] +type CertCtxType = (); + +pub fn generate_self_signed_certificate(subject_name: &str) -> Result, ErrorDetails> { + #[cfg(windows)] + { + use crate::certificate::certificate_helper_windows::generate_self_signed_certificate_windows; + + return generate_self_signed_certificate_windows(subject_name); + } + + #[cfg(not(windows))] + { + Err(ErrorDetails { message: "Not Implemented.".to_string(), code: -1 }) + } +} + +pub fn decrypt_from_base64( + base64_input: &str, + cert_details: &impl CertificateDetails +) -> Result { + #[cfg(windows)] + { + use crate::certificate::certificate_helper_windows::decrypt_from_base64_windows; + + return decrypt_from_base64_windows(base64_input, cert_details); + } + + #[cfg(not(windows))] + { + Err(ErrorDetails { message: "Not Implemented.".to_string(), code: -1 }) + } +} diff --git a/proxy_agent_shared/src/certificates_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs similarity index 57% rename from proxy_agent_shared/src/certificates_helper.rs rename to proxy_agent_shared/src/certificate/certificate_helper_windows.rs index 3cd2b8b7..25857553 100644 --- a/proxy_agent_shared/src/certificates_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs @@ -1,44 +1,16 @@ use base64::Engine; use uuid::Uuid; -use windows::{ - core::{BOOL, PCWSTR, PSTR}, - Win32::{ - Security::Cryptography::{ - szOID_KEY_USAGE, szOID_SUBJECT_KEY_IDENTIFIER, CertCloseStore, - CertCreateSelfSignCertificate, CertFindCertificateInStore, CertFreeCertificateContext, - CertOpenStore, CertStrToNameW, CryptAcquireCertificatePrivateKey, CryptEncodeObjectEx, - CryptExportPublicKeyInfo, CryptHashPublicKeyInfo, CryptMsgClose, CryptMsgControl, - CryptMsgGetParam, CryptMsgOpenToDecode, CryptMsgUpdate, NCryptCreatePersistedKey, - NCryptFinalizeKey, NCryptFreeObject, NCryptOpenStorageProvider, NCryptSetProperty, - CALG_SHA1, CERT_CONTEXT, CERT_DATA_ENCIPHERMENT_KEY_USAGE, - CERT_DIGITAL_SIGNATURE_KEY_USAGE, CERT_EXTENSION, CERT_EXTENSIONS, CERT_FIND_HASH, - CERT_KEY_CERT_SIGN_KEY_USAGE, CERT_KEY_ENCIPHERMENT_KEY_USAGE, CERT_KEY_SPEC, - CERT_OFFLINE_CRL_SIGN_KEY_USAGE, CERT_PUBLIC_KEY_INFO, CERT_STORE_MAXIMUM_ALLOWED_FLAG, - CERT_STORE_PROV_SYSTEM_W, CERT_SYSTEM_STORE_LOCAL_MACHINE, CERT_X500_NAME_STR, - CMSG_CTRL_DECRYPT, CMSG_CTRL_DECRYPT_PARA, CMSG_CTRL_DECRYPT_PARA_0, - CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, - CRYPT_ACQUIRE_FLAGS, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, CRYPT_BIT_BLOB, - CRYPT_INTEGER_BLOB, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, MS_KEY_STORAGE_PROVIDER, - NCRYPT_ALLOW_EXPORT_FLAG, NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG, - NCRYPT_EXPORT_POLICY_PROPERTY, NCRYPT_HANDLE, NCRYPT_KEY_HANDLE, - NCRYPT_LENGTH_PROPERTY, NCRYPT_PROV_HANDLE, NCRYPT_RSA_ALGORITHM, PKCS_7_ASN_ENCODING, - X509_ASN_ENCODING, - }, - System::SystemInformation::GetSystemTime, - }, -}; +use windows::{core::{BOOL, PCWSTR, PSTR}, Win32::{Security::Cryptography::{szOID_KEY_USAGE, szOID_SUBJECT_KEY_IDENTIFIER, CertCreateSelfSignCertificate, CertFreeCertificateContext, CertStrToNameW, CryptAcquireCertificatePrivateKey, CryptEncodeObjectEx, CryptExportPublicKeyInfo, CryptHashPublicKeyInfo, CryptMsgClose, CryptMsgControl, CryptMsgGetParam, CryptMsgOpenToDecode, CryptMsgUpdate, NCryptCreatePersistedKey, NCryptFinalizeKey, NCryptFreeObject, NCryptOpenStorageProvider, NCryptSetProperty, CALG_SHA1, CERT_CONTEXT, CERT_DATA_ENCIPHERMENT_KEY_USAGE, CERT_DIGITAL_SIGNATURE_KEY_USAGE, CERT_EXTENSION, CERT_EXTENSIONS, CERT_KEY_CERT_SIGN_KEY_USAGE, CERT_KEY_ENCIPHERMENT_KEY_USAGE, CERT_KEY_SPEC, CERT_OFFLINE_CRL_SIGN_KEY_USAGE, CERT_PUBLIC_KEY_INFO, CERT_X500_NAME_STR, CMSG_CTRL_DECRYPT, CMSG_CTRL_DECRYPT_PARA, CMSG_CTRL_DECRYPT_PARA_0, CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, CRYPT_ACQUIRE_FLAGS, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, CRYPT_BIT_BLOB, CRYPT_INTEGER_BLOB, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, MS_KEY_STORAGE_PROVIDER, NCRYPT_ALLOW_EXPORT_FLAG, NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG, NCRYPT_EXPORT_POLICY_PROPERTY, NCRYPT_HANDLE, NCRYPT_KEY_HANDLE, NCRYPT_LENGTH_PROPERTY, NCRYPT_PROV_HANDLE, NCRYPT_RSA_ALGORITHM, PKCS_7_ASN_ENCODING, X509_ASN_ENCODING}, System::SystemInformation::GetSystemTime}}; -use crate::client::data_model::error::ErrorDetails; +use crate::{certificate::certificate_helper::CertificateDetails, client::data_model::error::ErrorDetails}; -pub struct CertificateDetails { +pub struct CertificateDetailsWindows { pub public_key_der: Vec, - #[cfg(windows)] pub p_cert_ctx: *mut CERT_CONTEXT, - #[cfg(not(windows))] - pub private_key_der: Vec, } -impl Drop for CertificateDetails { + +impl Drop for CertificateDetailsWindows { fn drop(&mut self) { if !self.p_cert_ctx.is_null() { if !unsafe { CertFreeCertificateContext(Some(self.p_cert_ctx)) }.as_bool() { @@ -48,23 +20,25 @@ impl Drop for CertificateDetails { } } -pub fn generate_self_signed_certificate( - subject_name: &str, -) -> Result { - #[cfg(windows)] - { - return generate_self_signed_certificate_windows(subject_name); +impl CertificateDetails for CertificateDetailsWindows { + type CertificateContext = *mut CERT_CONTEXT; + + fn get_certificate_context(&self) -> &Self::CertificateContext { + return &self.p_cert_ctx; + } + + fn set_certificate_context(&mut self, cert_context: Self::CertificateContext) { + self.p_cert_ctx = cert_context; } - #[cfg(not(windows))] - { - todo!() + + fn get_public_cert_der(&self) -> &[u8] { + &self.public_key_der } } -#[cfg(windows)] -fn generate_self_signed_certificate_windows( +pub fn generate_self_signed_certificate_windows( subject_name: &str, -) -> Result { +) -> Result, ErrorDetails> { // Open KSP let mut h_prov = NCRYPT_PROV_HANDLE(0); unsafe { @@ -178,7 +152,7 @@ fn generate_self_signed_certificate_windows( ) }; - let res = CertificateDetails { + let res = CertificateDetailsWindows { public_key_der: cert_der.to_vec(), p_cert_ctx: cert_ctx, }; @@ -191,7 +165,99 @@ fn generate_self_signed_certificate_windows( Ok(res) } -#[cfg(windows)] +pub fn decrypt_from_base64_windows( + base64_input: &str, + cert_details: &impl CertificateDetails, +) -> Result { + let encrypted = base64_input.replace("\r", "").replace("\n", ""); + let encrypted_payload = &base64::engine::general_purpose::STANDARD.decode(encrypted)?; + let p_cert_ctx = *cert_details.get_certificate_context(); + + // Acquire the private key handle using the CNG-compatible function. + let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); + let mut key_spec = CERT_KEY_SPEC(0u32); + let mut must_free = BOOL(0); + unsafe { + CryptAcquireCertificatePrivateKey( + p_cert_ctx, + CRYPT_ACQUIRE_FLAGS( + CRYPT_ACQUIRE_COMPARE_KEY_FLAG.0 + | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG.0 + | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG.0, + ), + None, + &mut h_key, + Some(&mut key_spec), + Some(&mut must_free), + ) + }?; + + // Decode the encrypted message. + let msg_handle = unsafe { + CryptMsgOpenToDecode( + X509_ASN_ENCODING.0 | PKCS_7_ASN_ENCODING.0, + 0, + 0, + None, + None, + None, + ) + }; + + if msg_handle.is_null() { + return Err(ErrorDetails { + code: -1, + message: "Failed to open message handle to decrypt.".to_string(), + }); + } + unsafe { CryptMsgUpdate(msg_handle, Some(encrypted_payload), true) }?; + // Create an instance of the nested struct (the union) + let anonymous_union = CMSG_CTRL_DECRYPT_PARA_0 { + hCryptProv: h_key.0, + }; + + // Create the main struct instance + let mut decrypt_para = CMSG_CTRL_DECRYPT_PARA { + cbSize: std::mem::size_of::() as u32, + Anonymous: anonymous_union, + dwKeySpec: key_spec.0, + dwRecipientIndex: 0, + }; + + unsafe { + CryptMsgControl( + msg_handle, + 0, + CMSG_CTRL_DECRYPT, + Some(&mut decrypt_para as *mut _ as *mut _), + ) + }?; + + // Get the decrypted message size. + let mut content_size = 0; + unsafe { CryptMsgGetParam(msg_handle, 2, 0, None, &mut content_size) }?; + + // Get the decrypted message content. + let mut decrypted_data_buffer = vec![0u8; content_size as usize]; + unsafe { + CryptMsgGetParam( + msg_handle, + 2, + 0, + Some(decrypted_data_buffer.as_mut_ptr() as *mut _), + &mut content_size, + ) + }?; + + unsafe { CryptMsgClose(Some(msg_handle)) }?; + if must_free.as_bool() { + unsafe { NCryptFreeObject(NCRYPT_HANDLE(h_key.0)) }?; + } + + let res = String::from_utf8(decrypted_data_buffer)?; + Ok(res) +} + fn build_cert_extensions( h_key: HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, ) -> Result, ErrorDetails> { @@ -314,267 +380,4 @@ fn build_cert_extensions( }, }); Ok(extensions) -} - -pub fn decrypt_from_base64( - base64_input: &str, - cert_details: &CertificateDetails, -) -> Result { - #[cfg(windows)] - { - return decrypt_from_base64_windows(base64_input, cert_details); - } - #[cfg(not(windows))] - { - todo!() - } -} - -#[cfg(windows)] -fn decrypt_from_base64_windows( - base64_input: &str, - cert_details: &CertificateDetails, -) -> Result { - let encrypted = base64_input.replace("\r", "").replace("\n", ""); - let encrypted_payload = &base64::engine::general_purpose::STANDARD.decode(encrypted)?; - let p_cert_ctx = cert_details.p_cert_ctx; - - // Acquire the private key handle using the CNG-compatible function. - let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); - let mut key_spec = CERT_KEY_SPEC(0u32); - let mut must_free = BOOL(0); - unsafe { - CryptAcquireCertificatePrivateKey( - p_cert_ctx, - CRYPT_ACQUIRE_FLAGS( - CRYPT_ACQUIRE_COMPARE_KEY_FLAG.0 - | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG.0 - | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG.0, - ), - None, - &mut h_key, - Some(&mut key_spec), - Some(&mut must_free), - ) - }?; - - // Decode the encrypted message. - let msg_handle = unsafe { - CryptMsgOpenToDecode( - X509_ASN_ENCODING.0 | PKCS_7_ASN_ENCODING.0, - 0, - 0, - None, - None, - None, - ) - }; - - if msg_handle.is_null() { - return Err(ErrorDetails { - code: -1, - message: "Failed to open message handle to decrypt.".to_string(), - }); - } - unsafe { CryptMsgUpdate(msg_handle, Some(encrypted_payload), true) }?; - // Create an instance of the nested struct (the union) - let anonymous_union = CMSG_CTRL_DECRYPT_PARA_0 { - hCryptProv: h_key.0, - }; - - // Create the main struct instance - let mut decrypt_para = CMSG_CTRL_DECRYPT_PARA { - cbSize: std::mem::size_of::() as u32, - Anonymous: anonymous_union, - dwKeySpec: key_spec.0, - dwRecipientIndex: 0, - }; - - unsafe { - CryptMsgControl( - msg_handle, - 0, - CMSG_CTRL_DECRYPT, - Some(&mut decrypt_para as *mut _ as *mut _), - ) - }?; - - // Get the decrypted message size. - let mut content_size = 0; - unsafe { CryptMsgGetParam(msg_handle, 2, 0, None, &mut content_size) }?; - - // Get the decrypted message content. - let mut decrypted_data_buffer = vec![0u8; content_size as usize]; - unsafe { - CryptMsgGetParam( - msg_handle, - 2, - 0, - Some(decrypted_data_buffer.as_mut_ptr() as *mut _), - &mut content_size, - ) - }?; - - unsafe { CryptMsgClose(Some(msg_handle)) }?; - if must_free.as_bool() { - unsafe { NCryptFreeObject(NCRYPT_HANDLE(h_key.0)) }?; - } - - let res = String::from_utf8(decrypted_data_buffer)?; - Ok(res) -} - -#[cfg(windows)] -pub fn has_private_key(cert_ctx: *const CERT_CONTEXT) -> bool { - let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); - let mut key_spec = CERT_KEY_SPEC(0u32); - let mut must_free = BOOL(0); - - match unsafe { - CryptAcquireCertificatePrivateKey( - cert_ctx, - CRYPT_ACQUIRE_FLAGS( - CRYPT_ACQUIRE_COMPARE_KEY_FLAG.0 - | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG.0 - | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG.0, - ), - None, - &mut h_key, - Some(&mut key_spec), - Some(&mut must_free), - ) - } { - Ok(_) => {} - Err(e) => { - eprintln!("Acquire key error: {}", e); - return false; - } - } - - if h_key.is_invalid() { - return false; - } - - if must_free.as_bool() { - unsafe { NCryptFreeObject(NCRYPT_HANDLE(h_key.0)).unwrap_or(()) }; - } - return true; -} - -/// Convert thumbprint hex string like "AB CD EF 12 ..." into Vec -fn parse_thumbprint(thumbprint: &str) -> Vec { - thumbprint - .as_bytes() - .chunks(2) - .map(|pair| { - let s = std::str::from_utf8(pair).unwrap(); - u8::from_str_radix(s, 16).unwrap() - }) - .collect() -} - -pub fn get_cert_by_thumbprint( - thumbprint_str: &str, - store_name: &str, -) -> windows::core::Result { - let thumbprint = parse_thumbprint(thumbprint_str); - let mut store_name: Vec = store_name.encode_utf16().chain(Some(0)).collect(); - unsafe { - let h_store = CertOpenStore( - CERT_STORE_PROV_SYSTEM_W, - windows::Win32::Security::Cryptography::CERT_QUERY_ENCODING_TYPE(0), - Some(windows::Win32::Security::Cryptography::HCRYPTPROV_LEGACY(0)), - windows::Win32::Security::Cryptography::CERT_OPEN_STORE_FLAGS( - CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_MAXIMUM_ALLOWED_FLAG.0, - ), - Some(store_name.as_mut_ptr() as *mut _), - )?; - - if h_store.is_invalid() { - return Err(windows::core::Error::from_win32()); - } - - let mut hash_blob = CRYPT_INTEGER_BLOB { - cbData: thumbprint.len() as u32, - pbData: thumbprint.as_ptr() as *mut u8, - }; - - // Find cert by thumbprint - let p_cert_context = CertFindCertificateInStore( - h_store, - windows::Win32::Security::Cryptography::CERT_QUERY_ENCODING_TYPE(0), - 0, - CERT_FIND_HASH, - Some(&mut hash_blob as *mut _ as *const _), - None, - ); - - CertCloseStore(Some(h_store), 0)?; - - // Get cert data - let cert_der = std::slice::from_raw_parts( - (*p_cert_context).pbCertEncoded, - (*p_cert_context).cbCertEncoded as usize, - ); - - Ok(CertificateDetails { - public_key_der: cert_der.to_vec(), - p_cert_ctx: p_cert_context, - }) - } -} - -#[cfg(all(test, windows))] -mod tests { - use super::*; - use windows::{ - core::PSTR, - Win32::Security::Cryptography::{ - szOID_NIST_AES256_CBC, CryptEncryptMessage, CRYPT_ENCRYPT_MESSAGE_PARA, - }, - }; - - #[test] - fn test_certificate_decryption() { - let cert = generate_self_signed_certificate(&Uuid::new_v4().to_string()).unwrap(); - - let org_str = "Hello, World!"; - let encrypted = encrypt(&cert, org_str); - - let decrypted = decrypt_from_base64(&encrypted, &cert).unwrap(); - - assert!(decrypted.eq(org_str)) - } - - fn encrypt(cert: &CertificateDetails, org_str: &str) -> String { - let mut info = CRYPT_ENCRYPT_MESSAGE_PARA::default(); - info.cbSize = std::mem::size_of::() as u32; - info.dwMsgEncodingType = X509_ASN_ENCODING.0 | PKCS_7_ASN_ENCODING.0; - info.ContentEncryptionAlgorithm.pszObjId = PSTR(szOID_NIST_AES256_CBC.as_ptr() as *mut _); - info.dwFlags = 0; - let cert_ctx_ptrs = [cert.p_cert_ctx as *const _]; - let mut encrypted_size: u32 = 0; - unsafe { - CryptEncryptMessage( - &info, - &cert_ctx_ptrs, - Some(org_str.as_bytes()), - None, - &mut encrypted_size as *mut u32, - ) - .unwrap(); - } - let mut encrypted_data = vec![0u8; encrypted_size as usize]; - unsafe { - CryptEncryptMessage( - &info, - &cert_ctx_ptrs, - Some(org_str.as_bytes()), - Some(encrypted_data.as_mut_ptr()), - &mut encrypted_size as *mut u32, - ) - .unwrap(); - } - return base64::engine::general_purpose::STANDARD.encode(&encrypted_data); - } -} +} \ No newline at end of file diff --git a/proxy_agent_shared/src/certificate/mod.rs b/proxy_agent_shared/src/certificate/mod.rs new file mode 100644 index 00000000..0c8b06a9 --- /dev/null +++ b/proxy_agent_shared/src/certificate/mod.rs @@ -0,0 +1,3 @@ +pub mod certificate_helper; +#[cfg(windows)] +pub mod certificate_helper_windows; \ No newline at end of file diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs index 42256fa9..5dfab47f 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -1,4 +1,4 @@ -use crate::certificates_helper::{decrypt_from_base64, generate_self_signed_certificate}; +use crate::certificate::certificate_helper::{decrypt_from_base64, generate_self_signed_certificate, CertificateDetails}; use crate::client::data_model::error::ErrorDetails; use crate::client::data_model::hostga_plugin_model::{ Certificates, RawCertificatesPayload, VMSettings, @@ -54,8 +54,8 @@ impl HostGAPluginClient { ) -> Result, ErrorDetails> { let cert = generate_self_signed_certificate(&Uuid::new_v4().to_string())?; //let cert = get_cert_by_thumbprint("9bf93bb248b4504626c5d1247da2e7c5f8e0a03a")?; - let cert_der = cert.public_key_der.clone(); - let cert_base64 = base64::engine::general_purpose::STANDARD.encode(cert_der.clone()); + let cert_der = cert.get_public_cert_der(); + let cert_base64 = base64::engine::general_purpose::STANDARD.encode(cert_der); let mut headers = HeaderMap::new(); headers.insert( diff --git a/proxy_agent_shared/src/lib.rs b/proxy_agent_shared/src/lib.rs index ccc15ac8..df397c75 100644 --- a/proxy_agent_shared/src/lib.rs +++ b/proxy_agent_shared/src/lib.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT -pub mod certificates_helper; +pub mod certificate; pub mod client; pub mod error; #[cfg(windows)] From 4fd830fba1727bd49b5e81856f6b513e6ec093c1 Mon Sep 17 00:00:00 2001 From: yinbing Date: Wed, 17 Sep 2025 15:02:51 -0700 Subject: [PATCH 04/35] fixes for fmt --- .../src/certificate/certificate_helper.rs | 23 +++++++----- .../certificate/certificate_helper_windows.rs | 35 ++++++++++++++++--- proxy_agent_shared/src/certificate/mod.rs | 2 +- .../src/client/hostga_plugin_client.rs | 4 ++- 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index 020d77cf..195082fb 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -1,5 +1,4 @@ - -use crate::{client::data_model::error::ErrorDetails}; +use crate::client::data_model::error::ErrorDetails; pub trait CertificateDetails { type CertificateContext; @@ -10,29 +9,34 @@ pub trait CertificateDetails { #[cfg(windows)] use windows::Win32::Security::Cryptography::CERT_CONTEXT; -#[cfg(windows)] +#[cfg(windows)] type CertCtxType = *mut CERT_CONTEXT; #[cfg(not(windows))] type CertCtxType = (); -pub fn generate_self_signed_certificate(subject_name: &str) -> Result, ErrorDetails> { +pub fn generate_self_signed_certificate( + subject_name: &str, +) -> Result, ErrorDetails> { #[cfg(windows)] { use crate::certificate::certificate_helper_windows::generate_self_signed_certificate_windows; return generate_self_signed_certificate_windows(subject_name); } - + #[cfg(not(windows))] { - Err(ErrorDetails { message: "Not Implemented.".to_string(), code: -1 }) + Err(ErrorDetails { + message: "Not Implemented.".to_string(), + code: -1, + }) } } pub fn decrypt_from_base64( base64_input: &str, - cert_details: &impl CertificateDetails + cert_details: &impl CertificateDetails, ) -> Result { #[cfg(windows)] { @@ -43,6 +47,9 @@ pub fn decrypt_from_base64( #[cfg(not(windows))] { - Err(ErrorDetails { message: "Not Implemented.".to_string(), code: -1 }) + Err(ErrorDetails { + message: "Not Implemented.".to_string(), + code: -1, + }) } } diff --git a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs index 25857553..84dad51e 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs @@ -1,15 +1,40 @@ use base64::Engine; use uuid::Uuid; -use windows::{core::{BOOL, PCWSTR, PSTR}, Win32::{Security::Cryptography::{szOID_KEY_USAGE, szOID_SUBJECT_KEY_IDENTIFIER, CertCreateSelfSignCertificate, CertFreeCertificateContext, CertStrToNameW, CryptAcquireCertificatePrivateKey, CryptEncodeObjectEx, CryptExportPublicKeyInfo, CryptHashPublicKeyInfo, CryptMsgClose, CryptMsgControl, CryptMsgGetParam, CryptMsgOpenToDecode, CryptMsgUpdate, NCryptCreatePersistedKey, NCryptFinalizeKey, NCryptFreeObject, NCryptOpenStorageProvider, NCryptSetProperty, CALG_SHA1, CERT_CONTEXT, CERT_DATA_ENCIPHERMENT_KEY_USAGE, CERT_DIGITAL_SIGNATURE_KEY_USAGE, CERT_EXTENSION, CERT_EXTENSIONS, CERT_KEY_CERT_SIGN_KEY_USAGE, CERT_KEY_ENCIPHERMENT_KEY_USAGE, CERT_KEY_SPEC, CERT_OFFLINE_CRL_SIGN_KEY_USAGE, CERT_PUBLIC_KEY_INFO, CERT_X500_NAME_STR, CMSG_CTRL_DECRYPT, CMSG_CTRL_DECRYPT_PARA, CMSG_CTRL_DECRYPT_PARA_0, CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, CRYPT_ACQUIRE_FLAGS, CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, CRYPT_BIT_BLOB, CRYPT_INTEGER_BLOB, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, MS_KEY_STORAGE_PROVIDER, NCRYPT_ALLOW_EXPORT_FLAG, NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG, NCRYPT_EXPORT_POLICY_PROPERTY, NCRYPT_HANDLE, NCRYPT_KEY_HANDLE, NCRYPT_LENGTH_PROPERTY, NCRYPT_PROV_HANDLE, NCRYPT_RSA_ALGORITHM, PKCS_7_ASN_ENCODING, X509_ASN_ENCODING}, System::SystemInformation::GetSystemTime}}; +use windows::{ + core::{BOOL, PCWSTR, PSTR}, + Win32::{ + Security::Cryptography::{ + szOID_KEY_USAGE, szOID_SUBJECT_KEY_IDENTIFIER, CertCreateSelfSignCertificate, + CertFreeCertificateContext, CertStrToNameW, CryptAcquireCertificatePrivateKey, + CryptEncodeObjectEx, CryptExportPublicKeyInfo, CryptHashPublicKeyInfo, CryptMsgClose, + CryptMsgControl, CryptMsgGetParam, CryptMsgOpenToDecode, CryptMsgUpdate, + NCryptCreatePersistedKey, NCryptFinalizeKey, NCryptFreeObject, + NCryptOpenStorageProvider, NCryptSetProperty, CALG_SHA1, CERT_CONTEXT, + CERT_DATA_ENCIPHERMENT_KEY_USAGE, CERT_DIGITAL_SIGNATURE_KEY_USAGE, CERT_EXTENSION, + CERT_EXTENSIONS, CERT_KEY_CERT_SIGN_KEY_USAGE, CERT_KEY_ENCIPHERMENT_KEY_USAGE, + CERT_KEY_SPEC, CERT_OFFLINE_CRL_SIGN_KEY_USAGE, CERT_PUBLIC_KEY_INFO, + CERT_X500_NAME_STR, CMSG_CTRL_DECRYPT, CMSG_CTRL_DECRYPT_PARA, + CMSG_CTRL_DECRYPT_PARA_0, CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, + CRYPT_ACQUIRE_COMPARE_KEY_FLAG, CRYPT_ACQUIRE_FLAGS, + CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, CRYPT_BIT_BLOB, CRYPT_INTEGER_BLOB, + HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, MS_KEY_STORAGE_PROVIDER, NCRYPT_ALLOW_EXPORT_FLAG, + NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG, NCRYPT_EXPORT_POLICY_PROPERTY, NCRYPT_HANDLE, + NCRYPT_KEY_HANDLE, NCRYPT_LENGTH_PROPERTY, NCRYPT_PROV_HANDLE, NCRYPT_RSA_ALGORITHM, + PKCS_7_ASN_ENCODING, X509_ASN_ENCODING, + }, + System::SystemInformation::GetSystemTime, + }, +}; -use crate::{certificate::certificate_helper::CertificateDetails, client::data_model::error::ErrorDetails}; +use crate::{ + certificate::certificate_helper::CertificateDetails, client::data_model::error::ErrorDetails, +}; pub struct CertificateDetailsWindows { pub public_key_der: Vec, pub p_cert_ctx: *mut CERT_CONTEXT, } - impl Drop for CertificateDetailsWindows { fn drop(&mut self) { if !self.p_cert_ctx.is_null() { @@ -30,7 +55,7 @@ impl CertificateDetails for CertificateDetailsWindows { fn set_certificate_context(&mut self, cert_context: Self::CertificateContext) { self.p_cert_ctx = cert_context; } - + fn get_public_cert_der(&self) -> &[u8] { &self.public_key_der } @@ -380,4 +405,4 @@ fn build_cert_extensions( }, }); Ok(extensions) -} \ No newline at end of file +} diff --git a/proxy_agent_shared/src/certificate/mod.rs b/proxy_agent_shared/src/certificate/mod.rs index 0c8b06a9..9ac1744b 100644 --- a/proxy_agent_shared/src/certificate/mod.rs +++ b/proxy_agent_shared/src/certificate/mod.rs @@ -1,3 +1,3 @@ pub mod certificate_helper; #[cfg(windows)] -pub mod certificate_helper_windows; \ No newline at end of file +pub mod certificate_helper_windows; diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs index 5dfab47f..e1698a02 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -1,4 +1,6 @@ -use crate::certificate::certificate_helper::{decrypt_from_base64, generate_self_signed_certificate, CertificateDetails}; +use crate::certificate::certificate_helper::{ + decrypt_from_base64, generate_self_signed_certificate, CertificateDetails, +}; use crate::client::data_model::error::ErrorDetails; use crate::client::data_model::hostga_plugin_model::{ Certificates, RawCertificatesPayload, VMSettings, From 51edc682bebfbc67bd54e6e4defd1ecd2b1cebd3 Mon Sep 17 00:00:00 2001 From: yinbing Date: Wed, 17 Sep 2025 15:12:36 -0700 Subject: [PATCH 05/35] fixes for win build --- .../src/certificate/certificate_helper.rs | 4 ++-- .../certificate/certificate_helper_windows.rs | 10 +++++----- .../src/client/hostga_plugin_client.rs | 20 +++++++++---------- .../src/client/wire_server_client.rs | 16 +++++++-------- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index 195082fb..c45c5731 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -22,7 +22,7 @@ pub fn generate_self_signed_certificate( { use crate::certificate::certificate_helper_windows::generate_self_signed_certificate_windows; - return generate_self_signed_certificate_windows(subject_name); + generate_self_signed_certificate_windows(subject_name) } #[cfg(not(windows))] @@ -42,7 +42,7 @@ pub fn decrypt_from_base64( { use crate::certificate::certificate_helper_windows::decrypt_from_base64_windows; - return decrypt_from_base64_windows(base64_input, cert_details); + decrypt_from_base64_windows(base64_input, cert_details) } #[cfg(not(windows))] diff --git a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs index 84dad51e..e85c646a 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs @@ -37,10 +37,10 @@ pub struct CertificateDetailsWindows { impl Drop for CertificateDetailsWindows { fn drop(&mut self) { - if !self.p_cert_ctx.is_null() { - if !unsafe { CertFreeCertificateContext(Some(self.p_cert_ctx)) }.as_bool() { - eprintln!("Failed to free certificate context.") - } + if !self.p_cert_ctx.is_null() + && !unsafe { CertFreeCertificateContext(Some(self.p_cert_ctx)) }.as_bool() + { + eprintln!("Failed to free certificate context."); } } } @@ -49,7 +49,7 @@ impl CertificateDetails for CertificateDetailsWindows { type CertificateContext = *mut CERT_CONTEXT; fn get_certificate_context(&self) -> &Self::CertificateContext { - return &self.p_cert_ctx; + &self.p_cert_ctx } fn set_certificate_context(&mut self, cert_context: Self::CertificateContext) { diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs index e1698a02..ccddfd86 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -91,13 +91,13 @@ impl HostGAPluginClient { return Ok(HostGAPluginResponse { body: Some(serde_json::from_str::(&certs)?), etag: raw_certs_resp.etag.clone(), - certificates_revision: raw_certs_resp.certificates_revision.clone(), + certificates_revision: raw_certs_resp.certificates_revision, version: raw_certs_resp.version.clone(), }); } Err(ErrorDetails { - message: format!("certificate payload is empty."), + message: "certificate payload is empty.".to_string(), code: -1, }) } @@ -147,9 +147,9 @@ impl HostGAPluginClient { if resp.status() == StatusCode::NOT_MODIFIED { let _hostgap_response: HostGAPluginResponse = HostGAPluginResponse { body: None, - etag: etag, - version: version, - certificates_revision: certificates_revision, + etag, + version, + certificates_revision, }; return Ok(_hostgap_response); } else if resp.status().is_success() { @@ -163,20 +163,20 @@ impl HostGAPluginClient { })?; return Ok(HostGAPluginResponse { body: Option::Some(body_json), - etag: etag, - version: version, - certificates_revision: certificates_revision, + etag, + version, + certificates_revision, }); } let status = resp.status(); - return Err(ErrorDetails { + Err(ErrorDetails { code: status.as_u16() as i32, message: format!( "Http Error Status: {}, Body: {}", status, resp.text().await.unwrap_or_default() ), - }); + }) } } diff --git a/proxy_agent_shared/src/client/wire_server_client.rs b/proxy_agent_shared/src/client/wire_server_client.rs index b7691862..411f2a85 100644 --- a/proxy_agent_shared/src/client/wire_server_client.rs +++ b/proxy_agent_shared/src/client/wire_server_client.rs @@ -65,25 +65,23 @@ impl WireServerClient { code: -2, message: format!("XML Deserialization Failed: {}", e), })?; - return Ok(result); + Ok(result) } else { let status = resp.status(); - return Err(ErrorDetails { + Err(ErrorDetails { code: status.as_u16() as i32, message: format!( "Http Error Status: {}, Body: {}", status, resp.text().await.unwrap_or_default() ), - }); + }) } } - Err(e) => { - return Err(ErrorDetails { - code: -3, - message: format!("Request Error: {}", e), - }); - } + Err(e) => Err(ErrorDetails { + code: -3, + message: format!("Request Error: {}", e), + }), } } } From c51dc76323cc3b18f1195979010a2555ca8f2ce0 Mon Sep 17 00:00:00 2001 From: yinbing Date: Wed, 17 Sep 2025 22:39:41 -0700 Subject: [PATCH 06/35] fixes for build --- .../src/certificate/certificate_helper.rs | 46 ++++++++++--------- .../certificate/certificate_helper_windows.rs | 32 ++++--------- .../src/client/data_model/error.rs | 11 +++-- .../src/client/hostga_plugin_client.rs | 12 ++--- .../src/client/wire_server_client.rs | 6 +-- 5 files changed, 50 insertions(+), 57 deletions(-) diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index c45c5731..540c9348 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -1,42 +1,49 @@ use crate::client::data_model::error::ErrorDetails; -pub trait CertificateDetails { - type CertificateContext; - fn get_certificate_context(&self) -> &Self::CertificateContext; - fn set_certificate_context(&mut self, cert_context: Self::CertificateContext); - fn get_public_cert_der(&self) -> &[u8]; -} - #[cfg(windows)] -use windows::Win32::Security::Cryptography::CERT_CONTEXT; +use crate::certificate::certificate_helper_windows::CertificateDetailsWindows; + #[cfg(windows)] -type CertCtxType = *mut CERT_CONTEXT; +type CertDetailsType = CertificateDetailsWindows; #[cfg(not(windows))] -type CertCtxType = (); +type CertDetailsType = (); + +pub struct CertificateDetailsWrapper { + pub cert_details: CertDetailsType, +} + +impl CertificateDetailsWrapper { + pub fn get_public_cert_der(&self) -> &[u8] { + #[cfg(windows)] + { + &self.cert_details.public_key_der + } + #[cfg(not(windows))] + { + todo!() + } + } +} pub fn generate_self_signed_certificate( subject_name: &str, -) -> Result, ErrorDetails> { +) -> Result { #[cfg(windows)] { use crate::certificate::certificate_helper_windows::generate_self_signed_certificate_windows; generate_self_signed_certificate_windows(subject_name) } - #[cfg(not(windows))] { - Err(ErrorDetails { - message: "Not Implemented.".to_string(), - code: -1, - }) + todo!() } } pub fn decrypt_from_base64( base64_input: &str, - cert_details: &impl CertificateDetails, + cert_details: &CertificateDetailsWrapper, ) -> Result { #[cfg(windows)] { @@ -47,9 +54,6 @@ pub fn decrypt_from_base64( #[cfg(not(windows))] { - Err(ErrorDetails { - message: "Not Implemented.".to_string(), - code: -1, - }) + todo!() } } diff --git a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs index e85c646a..a534f051 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs @@ -27,7 +27,8 @@ use windows::{ }; use crate::{ - certificate::certificate_helper::CertificateDetails, client::data_model::error::ErrorDetails, + certificate::certificate_helper::CertificateDetailsWrapper, + client::data_model::error::ErrorDetails, }; pub struct CertificateDetailsWindows { @@ -45,25 +46,9 @@ impl Drop for CertificateDetailsWindows { } } -impl CertificateDetails for CertificateDetailsWindows { - type CertificateContext = *mut CERT_CONTEXT; - - fn get_certificate_context(&self) -> &Self::CertificateContext { - &self.p_cert_ctx - } - - fn set_certificate_context(&mut self, cert_context: Self::CertificateContext) { - self.p_cert_ctx = cert_context; - } - - fn get_public_cert_der(&self) -> &[u8] { - &self.public_key_der - } -} - pub fn generate_self_signed_certificate_windows( subject_name: &str, -) -> Result, ErrorDetails> { +) -> Result { // Open KSP let mut h_prov = NCRYPT_PROV_HANDLE(0); unsafe { @@ -110,7 +95,7 @@ pub fn generate_self_signed_certificate_windows( } // Set up subject name for cert - let subject = format!("CN={}", subject_name); + let subject = format!("CN={subject_name}"); let subject_w: Vec = subject.encode_utf16().chain(Some(0)).collect(); let mut size = 0u32; unsafe { @@ -177,10 +162,13 @@ pub fn generate_self_signed_certificate_windows( ) }; - let res = CertificateDetailsWindows { + let cert_detials_windows = CertificateDetailsWindows { public_key_der: cert_der.to_vec(), p_cert_ctx: cert_ctx, }; + let res = CertificateDetailsWrapper { + cert_details: cert_detials_windows, + }; // Cleanup unsafe { @@ -192,11 +180,11 @@ pub fn generate_self_signed_certificate_windows( pub fn decrypt_from_base64_windows( base64_input: &str, - cert_details: &impl CertificateDetails, + cert_details_wrapper: &CertificateDetailsWrapper, ) -> Result { let encrypted = base64_input.replace("\r", "").replace("\n", ""); let encrypted_payload = &base64::engine::general_purpose::STANDARD.decode(encrypted)?; - let p_cert_ctx = *cert_details.get_certificate_context(); + let p_cert_ctx = cert_details_wrapper.cert_details.p_cert_ctx; // Acquire the private key handle using the CNG-compatible function. let mut h_key = HCRYPTPROV_OR_NCRYPT_KEY_HANDLE(0); diff --git a/proxy_agent_shared/src/client/data_model/error.rs b/proxy_agent_shared/src/client/data_model/error.rs index 7270fbe4..f8c74c07 100644 --- a/proxy_agent_shared/src/client/data_model/error.rs +++ b/proxy_agent_shared/src/client/data_model/error.rs @@ -20,7 +20,7 @@ impl std::error::Error for ErrorDetails {} impl From for ErrorDetails { fn from(value: DecodeError) -> Self { ErrorDetails { - message: format!("Decode Error: {:?}", value), + message: format!("Decode Error: {value:?}"), code: -1, } } @@ -29,7 +29,7 @@ impl From for ErrorDetails { impl From for ErrorDetails { fn from(value: FromUtf8Error) -> Self { ErrorDetails { - message: format!("Uft8 Convert Error: {:?}", value), + message: format!("Uft8 Convert Error: {value:?}"), code: -1, } } @@ -38,7 +38,7 @@ impl From for ErrorDetails { impl From for ErrorDetails { fn from(value: InvalidHeaderValue) -> Self { ErrorDetails { - message: format!("Invalid Http Header Value: {:?}", value), + message: format!("Invalid Http Header Value: {value:?}"), code: -1, } } @@ -47,16 +47,17 @@ impl From for ErrorDetails { impl From for ErrorDetails { fn from(value: serde_json::Error) -> Self { ErrorDetails { - message: format!("Json Error: {:?}", value), + message: format!("Json Error: {value:?}"), code: -1, } } } +#[cfg(windows)] impl From for ErrorDetails { fn from(value: windows::core::Error) -> Self { ErrorDetails { - message: format!("Windows API Error: {:?}", value), + message: format!("Windows API Error: {value:?}"), code: -1, } } diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs index ccddfd86..01bb17ff 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -1,5 +1,5 @@ use crate::certificate::certificate_helper::{ - decrypt_from_base64, generate_self_signed_certificate, CertificateDetails, + decrypt_from_base64, generate_self_signed_certificate, }; use crate::client::data_model::error::ErrorDetails; use crate::client::data_model::hostga_plugin_model::{ @@ -68,7 +68,7 @@ impl HostGAPluginClient { Self::TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER, HeaderValue::from_str("AES256_CBC")?, ); - println!("Requesting certificates with headers: {:?}", headers); + println!("Requesting certificates with headers: {headers:?}"); let raw_certs_resp = self .get::( &format!( @@ -111,7 +111,7 @@ impl HostGAPluginClient { for<'a> T: Deserialize<'a>, { let mut request = self.client.get(url); - println!("Requesting URL: {}", url); + println!("Requesting URL: {url}"); if let Some(headers) = headers_map { request = request.headers(headers); } @@ -123,7 +123,7 @@ impl HostGAPluginClient { } ErrorDetails { code: error_code, - message: format!("HostGAPlugin Request Error: {}, url: {}", e, url), + message: format!("HostGAPlugin Request Error: {e}, url: {url}"), } })?; @@ -155,11 +155,11 @@ impl HostGAPluginClient { } else if resp.status().is_success() { let body = resp.text().await.map_err(|e| ErrorDetails { code: -1, - message: format!("Failed to get response body: {}", e), + message: format!("Failed to get response body: {e}"), })?; let body_json = serde_json::from_str::(&body).map_err(|e| ErrorDetails { code: -1, - message: format!("Failed to deserialized json payload, error: {}", e), + message: format!("Failed to deserialized json payload, error: {e}"), })?; return Ok(HostGAPluginResponse { body: Option::Some(body_json), diff --git a/proxy_agent_shared/src/client/wire_server_client.rs b/proxy_agent_shared/src/client/wire_server_client.rs index 411f2a85..0338cee7 100644 --- a/proxy_agent_shared/src/client/wire_server_client.rs +++ b/proxy_agent_shared/src/client/wire_server_client.rs @@ -59,11 +59,11 @@ impl WireServerClient { if resp.status().is_success() { let body = resp.text().await.map_err(|e| ErrorDetails { code: -1, - message: format!("{}", e), + message: format!("{e}"), })?; let result = from_str::(&body).map_err(|e| ErrorDetails { code: -2, - message: format!("XML Deserialization Failed: {}", e), + message: format!("XML Deserialization Failed: {e}"), })?; Ok(result) } else { @@ -80,7 +80,7 @@ impl WireServerClient { } Err(e) => Err(ErrorDetails { code: -3, - message: format!("Request Error: {}", e), + message: format!("Request Error: {e}"), }), } } From f86be1ccbf9836fb1784994c58f07f34ac7bb6a9 Mon Sep 17 00:00:00 2001 From: yinbing Date: Wed, 17 Sep 2025 22:49:49 -0700 Subject: [PATCH 07/35] small changes with one unit test --- .../src/certificate/certificate_helper.rs | 10 ++-- .../certificate/certificate_helper_windows.rs | 55 +++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index 540c9348..a2ac9c61 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -27,13 +27,13 @@ impl CertificateDetailsWrapper { } pub fn generate_self_signed_certificate( - subject_name: &str, + _subject_name: &str, ) -> Result { #[cfg(windows)] { use crate::certificate::certificate_helper_windows::generate_self_signed_certificate_windows; - generate_self_signed_certificate_windows(subject_name) + generate_self_signed_certificate_windows(_subject_name) } #[cfg(not(windows))] { @@ -42,14 +42,14 @@ pub fn generate_self_signed_certificate( } pub fn decrypt_from_base64( - base64_input: &str, - cert_details: &CertificateDetailsWrapper, + _base64_input: &str, + _cert_details: &CertificateDetailsWrapper, ) -> Result { #[cfg(windows)] { use crate::certificate::certificate_helper_windows::decrypt_from_base64_windows; - decrypt_from_base64_windows(base64_input, cert_details) + decrypt_from_base64_windows(_base64_input, _cert_details) } #[cfg(not(windows))] diff --git a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs index a534f051..063c1d93 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs @@ -394,3 +394,58 @@ fn build_cert_extensions( }); Ok(extensions) } + +#[cfg(test)] +mod tests { + use super::*; + use windows::{ + core::PSTR, + Win32::Security::Cryptography::{ + szOID_NIST_AES256_CBC, CryptEncryptMessage, CRYPT_ENCRYPT_MESSAGE_PARA, + }, + }; + + #[test] + fn test_certificate_decryption() { + let cert = generate_self_signed_certificate_windows(&Uuid::new_v4().to_string()).unwrap(); + + let org_str = "Hello, World!"; + let encrypted = encrypt(&cert.cert_details, org_str); + + let decrypted = decrypt_from_base64_windows(&encrypted, &cert).unwrap(); + + assert!(decrypted.eq(org_str)) + } + + pub fn encrypt(cert: &CertificateDetailsWindows, org_str: &str) -> String { + let mut info = CRYPT_ENCRYPT_MESSAGE_PARA::default(); + info.cbSize = std::mem::size_of::() as u32; + info.dwMsgEncodingType = X509_ASN_ENCODING.0 | PKCS_7_ASN_ENCODING.0; + info.ContentEncryptionAlgorithm.pszObjId = PSTR(szOID_NIST_AES256_CBC.as_ptr() as *mut _); + info.dwFlags = 0; + let cert_ctx_ptrs = [cert.p_cert_ctx as *const _]; + let mut encrypted_size: u32 = 0; + unsafe { + CryptEncryptMessage( + &info, + &cert_ctx_ptrs, + Some(org_str.as_bytes()), + None, + &mut encrypted_size as *mut u32, + ) + .unwrap(); + } + let mut encrypted_data = vec![0u8; encrypted_size as usize]; + unsafe { + CryptEncryptMessage( + &info, + &cert_ctx_ptrs, + Some(org_str.as_bytes()), + Some(encrypted_data.as_mut_ptr()), + &mut encrypted_size as *mut u32, + ) + .unwrap(); + } + return base64::engine::general_purpose::STANDARD.encode(&encrypted_data); + } +} From de62b73bd594e78e021941a7690b5c1d45d1ba74 Mon Sep 17 00:00:00 2001 From: yinbing Date: Thu, 18 Sep 2025 14:02:36 -0700 Subject: [PATCH 08/35] move common from proxy agent to shared --- Cargo.lock | 27 ++++++++------ proxy_agent/Cargo.toml | 4 --- proxy_agent/src/acl.rs | 2 +- proxy_agent/src/acl/linux_acl.rs | 2 +- proxy_agent/src/acl/windows_acl.rs | 2 +- proxy_agent/src/host_clients/imds_client.rs | 2 +- .../src/host_clients/wire_server_client.rs | 14 ++++---- proxy_agent/src/key_keeper.rs | 22 ++++++------ proxy_agent/src/key_keeper/key.rs | 19 +++++----- proxy_agent/src/main.rs | 9 +++-- proxy_agent/src/provision.rs | 6 ++-- proxy_agent/src/proxy.rs | 2 +- proxy_agent/src/proxy/authorization_rules.rs | 2 +- proxy_agent/src/proxy/proxy_authorizer.rs | 35 ++++++++++--------- proxy_agent/src/proxy/proxy_connection.rs | 10 +++--- proxy_agent/src/proxy/proxy_server.rs | 23 ++++++------ proxy_agent/src/proxy/windows.rs | 6 ++-- proxy_agent/src/proxy_agent_status.rs | 2 +- proxy_agent/src/redirector.rs | 12 +++---- proxy_agent/src/redirector/linux.rs | 16 ++++----- proxy_agent/src/redirector/linux/ebpf_obj.rs | 2 +- proxy_agent/src/redirector/windows.rs | 4 +-- proxy_agent/src/redirector/windows/bpf_api.rs | 4 +-- .../src/redirector/windows/bpf_prog.rs | 8 ++--- proxy_agent/src/service.rs | 2 +- proxy_agent/src/service/windows_main.rs | 2 +- .../src/shared_state/agent_status_wrapper.rs | 7 ++-- .../src/shared_state/key_keeper_wrapper.rs | 13 +++---- .../src/shared_state/provision_wrapper.rs | 6 ++-- .../src/shared_state/proxy_server_wrapper.rs | 6 ++-- .../src/shared_state/redirector_wrapper.rs | 6 ++-- .../src/shared_state/telemetry_wrapper.rs | 4 +-- proxy_agent/src/telemetry/event_reader.rs | 4 +-- proxy_agent/src/telemetry/telemetry_event.rs | 2 +- proxy_agent/src/test_mock/server_mock.rs | 2 +- proxy_agent_shared/Cargo.toml | 9 +++++ .../src/common.rs | 0 .../src/common/cli.rs | 0 .../src/common/config.rs | 8 ++--- .../src/common/constants.rs | 0 .../src/common/error.rs | 0 .../src/common/helpers.rs | 4 +-- .../src/common/hyper_client.rs | 2 +- .../src/common/logger.rs | 2 +- .../src/common/result.rs | 0 .../src/common/windows.rs | 2 +- proxy_agent_shared/src/lib.rs | 1 + 47 files changed, 167 insertions(+), 150 deletions(-) rename {proxy_agent => proxy_agent_shared}/src/common.rs (100%) rename {proxy_agent => proxy_agent_shared}/src/common/cli.rs (100%) rename {proxy_agent => proxy_agent_shared}/src/common/config.rs (98%) rename {proxy_agent => proxy_agent_shared}/src/common/constants.rs (100%) rename {proxy_agent => proxy_agent_shared}/src/common/error.rs (100%) rename {proxy_agent => proxy_agent_shared}/src/common/helpers.rs (97%) rename {proxy_agent => proxy_agent_shared}/src/common/hyper_client.rs (99%) rename {proxy_agent => proxy_agent_shared}/src/common/logger.rs (98%) rename {proxy_agent => proxy_agent_shared}/src/common/result.rs (100%) rename {proxy_agent => proxy_agent_shared}/src/common/windows.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 9ff4e728..8e432753 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,15 +182,11 @@ version = "9.9.9" dependencies = [ "aya", "bitflags", - "clap", "ctor", - "hex", - "hmac-sha256", "http", "http-body-util", "hyper", "hyper-util", - "itertools", "libc", "libloading", "nix", @@ -474,7 +470,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.0", ] [[package]] @@ -681,9 +677,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hmac-sha256" -version = "1.1.7" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735" +checksum = "ad6880c8d4a9ebf39c6e8b77007ce223f646a4d21ce29d99f70cb16420545425" [[package]] name = "http" @@ -991,9 +987,9 @@ checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libloading" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", "windows-targets", @@ -1287,8 +1283,17 @@ version = "9.9.9" dependencies = [ "base64", "chrono", + "clap", "concurrent-queue", "ctor", + "hex", + "hmac-sha256", + "http", + "http-body-util", + "hyper", + "hyper-util", + "itertools", + "libloading", "log", "once_cell", "os_info", @@ -1592,7 +1597,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.0", ] [[package]] @@ -1877,7 +1882,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.0", ] [[package]] diff --git a/proxy_agent/Cargo.toml b/proxy_agent/Cargo.toml index c5b14598..87203f1f 100644 --- a/proxy_agent/Cargo.toml +++ b/proxy_agent/Cargo.toml @@ -7,15 +7,12 @@ build = "build.rs" [dependencies] proxy_agent_shared = { path ="../proxy_agent_shared"} -itertools = "0.10.5" # use to sort iterator elements into a new iterator in ascending order once_cell = "1.17.0" # use Lazy serde = "1.0.152" serde_derive = "1.0.152" serde_json = "1.0.91" # json Deserializer serde-xml-rs = "0.8.1" # xml Deserializer with xml attribute bitflags = "2.6.0" # support bitflag enum -hmac-sha256 = "1.1.6" # use HMAC using the SHA-256 hash function -hex = "0.4.3" # hex encode regex = "1.11" # match process name in cmdline tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "net", "macros", "sync"] } tokio-util = "0.7.11" @@ -25,7 +22,6 @@ hyper = { version = "1", features = ["server", "http1", "client"] } hyper-util = { version = "0.1", features = ["tokio"] } tower = { version = "0.5.2", features = ["full"] } tower-http = { version = "0.6.2", features = ["limit"] } -clap = { version = "4.5.17", features =["derive"] } # Command Line Argument Parser thiserror = "1.0.64" ctor = "0.3.6" # used for test setup and clean up diff --git a/proxy_agent/src/acl.rs b/proxy_agent/src/acl.rs index f569d803..f4f4e063 100644 --- a/proxy_agent/src/acl.rs +++ b/proxy_agent/src/acl.rs @@ -19,7 +19,7 @@ mod windows_acl; #[cfg(not(windows))] mod linux_acl; -use crate::common::result::Result; +use proxy_agent_shared::common::result::Result; use std::path::PathBuf; pub fn acl_directory(dir_to_acl: PathBuf) -> Result<()> { diff --git a/proxy_agent/src/acl/linux_acl.rs b/proxy_agent/src/acl/linux_acl.rs index d588c3d9..6d9df1cb 100644 --- a/proxy_agent/src/acl/linux_acl.rs +++ b/proxy_agent/src/acl/linux_acl.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT -use crate::common::{logger, result::Result}; use nix::unistd::{chown, Gid, Uid}; +use proxy_agent_shared::common::{logger, result::Result}; use proxy_agent_shared::misc_helpers; use std::fs; use std::os::unix::fs::PermissionsExt; diff --git a/proxy_agent/src/acl/windows_acl.rs b/proxy_agent/src/acl/windows_acl.rs index eb63a896..8aa0757b 100644 --- a/proxy_agent/src/acl/windows_acl.rs +++ b/proxy_agent/src/acl/windows_acl.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT -use crate::common::{ +use proxy_agent_shared::common::{ error::{AclErrorType, Error}, logger, result::Result, diff --git a/proxy_agent/src/host_clients/imds_client.rs b/proxy_agent/src/host_clients/imds_client.rs index 7c506ef1..ceeaa927 100644 --- a/proxy_agent/src/host_clients/imds_client.rs +++ b/proxy_agent/src/host_clients/imds_client.rs @@ -21,9 +21,9 @@ //! ``` use super::instance_info::InstanceInfo; -use crate::common::{error::Error, hyper_client, logger, result::Result}; use crate::shared_state::key_keeper_wrapper::KeyKeeperSharedState; use hyper::Uri; +use proxy_agent_shared::common::{error::Error, hyper_client, logger, result::Result}; use std::collections::HashMap; pub struct ImdsClient { diff --git a/proxy_agent/src/host_clients/wire_server_client.rs b/proxy_agent/src/host_clients/wire_server_client.rs index ced98348..1cee0b11 100644 --- a/proxy_agent/src/host_clients/wire_server_client.rs +++ b/proxy_agent/src/host_clients/wire_server_client.rs @@ -20,16 +20,14 @@ //! ``` use crate::host_clients::goal_state::{GoalState, SharedConfig}; -use crate::{ - common::{ - error::{Error, WireServerErrorType}, - hyper_client, logger, - result::Result, - }, - shared_state::key_keeper_wrapper::KeyKeeperSharedState, -}; +use crate::shared_state::key_keeper_wrapper::KeyKeeperSharedState; use http::Method; use hyper::Uri; +use proxy_agent_shared::common::{ + error::{Error, WireServerErrorType}, + hyper_client, logger, + result::Result, +}; use std::collections::HashMap; pub struct WireServerClient { diff --git a/proxy_agent/src/key_keeper.rs b/proxy_agent/src/key_keeper.rs index 19170e15..0555185d 100644 --- a/proxy_agent/src/key_keeper.rs +++ b/proxy_agent/src/key_keeper.rs @@ -26,9 +26,6 @@ pub mod key; use self::key::Key; -use crate::common::error::{Error, KeyErrorType}; -use crate::common::result::Result; -use crate::common::{constants, helpers, logger}; use crate::provision; use crate::proxy::authorization_rules::{AuthorizationRulesForLogging, ComputedAuthorizationRules}; use crate::shared_state::agent_status_wrapper::{AgentStatusModule, AgentStatusSharedState}; @@ -39,6 +36,9 @@ use crate::shared_state::telemetry_wrapper::TelemetrySharedState; use crate::shared_state::SharedState; use crate::{acl, redirector}; use hyper::Uri; +use proxy_agent_shared::common::error::{Error, KeyErrorType}; +use proxy_agent_shared::common::result::Result; +use proxy_agent_shared::common::{constants, helpers, logger}; use proxy_agent_shared::logger::LoggerLevel; use proxy_agent_shared::misc_helpers; use proxy_agent_shared::proxy_agent_aggregate_status::ModuleState; @@ -607,7 +607,7 @@ impl KeyKeeper { key_file.set_extension("encrypted"); #[cfg(windows)] { - crate::common::store_key_data( + proxy_agent_shared::common::store_key_data( &key_file, serde_json::to_string(&key).map_err(|e| { Error::Key(KeyErrorType::StoreLocalKey(format!( @@ -657,7 +657,7 @@ impl KeyKeeper { if !key_file.exists() { // guid.key file does not exist locally return Err(Error::Key( - crate::common::error::KeyErrorType::FetchLocalKey(format!( + proxy_agent_shared::common::error::KeyErrorType::FetchLocalKey(format!( "Key file '{}' does not exist locally.", key_file.display() )), @@ -674,7 +674,7 @@ impl KeyKeeper { } #[cfg(windows)] { - crate::common::fetch_key_data(&key_file)? + proxy_agent_shared::common::fetch_key_data(&key_file)? } } else { fs::read_to_string(&key_file).map_err(|e| { @@ -683,9 +683,11 @@ impl KeyKeeper { }; serde_json::from_str::(&key_data).map_err(|e| { - Error::Key(crate::common::error::KeyErrorType::FetchLocalKey(format!( - "Parse key data with error: {e}" - ))) + Error::Key( + proxy_agent_shared::common::error::KeyErrorType::FetchLocalKey(format!( + "Parse key data with error: {e}" + )), + ) }) } @@ -726,7 +728,7 @@ impl KeyKeeper { } else { // guid.key file found but guid or key value is not matched Err(Error::Key( - crate::common::error::KeyErrorType::CheckLocalKey( + proxy_agent_shared::common::error::KeyErrorType::CheckLocalKey( "Local key guid or key value is not matched.".to_string(), ), )) diff --git a/proxy_agent/src/key_keeper/key.rs b/proxy_agent/src/key_keeper/key.rs index bf8ba5ac..756fc863 100644 --- a/proxy_agent/src/key_keeper/key.rs +++ b/proxy_agent/src/key_keeper/key.rs @@ -21,18 +21,15 @@ //! Key::attest_key(base_url.clone(), &key).await.unwrap(); //! //! ``` - -use crate::{ - common::{ - constants, - error::{Error, KeyErrorType}, - hyper_client, logger, - result::Result, - }, - proxy::{proxy_connection::ConnectionLogger, Claims}, -}; +use crate::proxy::{proxy_connection::ConnectionLogger, Claims}; use http::{Method, StatusCode}; use hyper::Uri; +use proxy_agent_shared::common::{ + constants, + error::{Error, KeyErrorType}, + hyper_client, logger, + result::Result, +}; use proxy_agent_shared::logger::LoggerLevel; use serde_derive::{Deserialize, Serialize}; use std::ffi::OsString; @@ -831,11 +828,11 @@ mod tests { use super::Key; use super::KeyStatus; - use crate::common::constants; use crate::key_keeper::key::Identity; use crate::key_keeper::key::Privilege; use crate::proxy::proxy_connection::ConnectionLogger; use hyper::Uri; + use proxy_agent_shared::common::constants; use serde_json::json; #[test] diff --git a/proxy_agent/src/main.rs b/proxy_agent/src/main.rs index 4dcc3ce1..a1bd5323 100644 --- a/proxy_agent/src/main.rs +++ b/proxy_agent/src/main.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: MIT pub mod acl; -pub mod common; pub mod host_clients; pub mod key_keeper; pub mod provision; @@ -16,16 +15,16 @@ pub mod telemetry; #[cfg(test)] pub mod test_mock; -use common::cli::{Commands, CLI}; -use common::constants; -use common::helpers; use provision::provision_query::ProvisionQuery; +use proxy_agent_shared::common::cli::{Commands, CLI}; +use proxy_agent_shared::common::constants; +use proxy_agent_shared::common::helpers; use proxy_agent_shared::misc_helpers; use shared_state::SharedState; use std::{process, time::Duration}; #[cfg(windows)] -use common::logger; +use proxy_agent_shared::common::logger; #[cfg(windows)] use service::windows_main; #[cfg(windows)] diff --git a/proxy_agent/src/provision.rs b/proxy_agent/src/provision.rs index bec9c783..f184ad24 100644 --- a/proxy_agent/src/provision.rs +++ b/proxy_agent/src/provision.rs @@ -79,7 +79,6 @@ //! assert_eq!(0, provision_state.1.len()); //! ``` -use crate::common::{config, helpers, logger}; use crate::key_keeper::{DISABLE_STATE, UNKNOWN_STATE}; use crate::proxy_agent_status; use crate::shared_state::agent_status_wrapper::{AgentStatusModule, AgentStatusSharedState}; @@ -87,6 +86,7 @@ use crate::shared_state::key_keeper_wrapper::KeyKeeperSharedState; use crate::shared_state::provision_wrapper::ProvisionSharedState; use crate::shared_state::telemetry_wrapper::TelemetrySharedState; use crate::telemetry::event_reader::EventReader; +use proxy_agent_shared::common::{config, helpers, logger}; use proxy_agent_shared::logger::LoggerLevel; use proxy_agent_shared::telemetry::event_logger; use proxy_agent_shared::{misc_helpers, proxy_agent_aggregate_status}; @@ -535,7 +535,9 @@ pub async fn get_provision_state_internal( /// provision query module designed for GPA command line, serves for --status [--wait seconds] option /// It is used to query the provision status from GPA service via http request pub mod provision_query { - use crate::common::{constants, error::Error, helpers, hyper_client, logger, result::Result}; + use proxy_agent_shared::common::{ + constants, error::Error, helpers, hyper_client, logger, result::Result, + }; use proxy_agent_shared::misc_helpers; use serde_derive::{Deserialize, Serialize}; use std::{collections::HashMap, net::Ipv4Addr, time::Duration}; diff --git a/proxy_agent/src/proxy.rs b/proxy_agent/src/proxy.rs index 23a87f2d..bc4a14be 100644 --- a/proxy_agent/src/proxy.rs +++ b/proxy_agent/src/proxy.rs @@ -40,9 +40,9 @@ pub mod proxy_summary; #[cfg(windows)] mod windows; -use crate::common::result::Result; use crate::redirector::AuditEntry; use crate::shared_state::proxy_server_wrapper::ProxyServerSharedState; +use proxy_agent_shared::common::result::Result; use serde_derive::{Deserialize, Serialize}; use std::{ffi::OsString, net::IpAddr, path::PathBuf}; diff --git a/proxy_agent/src/proxy/authorization_rules.rs b/proxy_agent/src/proxy/authorization_rules.rs index 27ef84fa..b5aed2ab 100644 --- a/proxy_agent/src/proxy/authorization_rules.rs +++ b/proxy_agent/src/proxy/authorization_rules.rs @@ -18,8 +18,8 @@ //! ``` use super::{proxy_connection::ConnectionLogger, Claims}; -use crate::common::logger; use crate::key_keeper::key::{AuthorizationItem, AuthorizationRules, Identity, Privilege, Role}; +use proxy_agent_shared::common::logger; use proxy_agent_shared::logger::LoggerLevel; use proxy_agent_shared::misc_helpers; use serde_derive::{Deserialize, Serialize}; diff --git a/proxy_agent/src/proxy/proxy_authorizer.rs b/proxy_agent/src/proxy/proxy_authorizer.rs index 1707f55e..6ca85403 100644 --- a/proxy_agent/src/proxy/proxy_authorizer.rs +++ b/proxy_agent/src/proxy/proxy_authorizer.rs @@ -21,9 +21,10 @@ use super::authorization_rules::{AuthorizationMode, ComputedAuthorizationItem}; use super::proxy_connection::ConnectionLogger; +use crate::proxy::Claims; use crate::shared_state::key_keeper_wrapper::KeyKeeperSharedState; -use crate::{common::constants, common::result::Result, proxy::Claims}; use proxy_agent_shared::logger::LoggerLevel; +use proxy_agent_shared::{common::constants, common::result::Result}; #[derive(PartialEq)] pub enum AuthorizeResult { @@ -268,8 +269,8 @@ mod tests { }; let mut test_logger = ConnectionLogger::new(0, 0); let auth: Box = super::get_authorizer( - crate::common::constants::WIRE_SERVER_IP.to_string(), - crate::common::constants::WIRE_SERVER_PORT, + proxy_agent_shared::common::constants::WIRE_SERVER_IP.to_string(), + proxy_agent_shared::common::constants::WIRE_SERVER_PORT, claims.clone(), ); let test_uri = hyper::Uri::from_str("test").unwrap(); @@ -283,8 +284,8 @@ mod tests { ); let auth = super::get_authorizer( - crate::common::constants::GA_PLUGIN_IP.to_string(), - crate::common::constants::GA_PLUGIN_PORT, + proxy_agent_shared::common::constants::GA_PLUGIN_IP.to_string(), + proxy_agent_shared::common::constants::GA_PLUGIN_PORT, claims.clone(), ); assert_eq!( @@ -297,8 +298,8 @@ mod tests { ); let auth = super::get_authorizer( - crate::common::constants::IMDS_IP.to_string(), - crate::common::constants::IMDS_PORT, + proxy_agent_shared::common::constants::IMDS_IP.to_string(), + proxy_agent_shared::common::constants::IMDS_PORT, claims.clone(), ); assert_eq!(auth.to_string(), "IMDS"); @@ -308,8 +309,8 @@ mod tests { ); let auth = super::get_authorizer( - crate::common::constants::PROXY_AGENT_IP.to_string(), - crate::common::constants::PROXY_AGENT_PORT, + proxy_agent_shared::common::constants::PROXY_AGENT_IP.to_string(), + proxy_agent_shared::common::constants::PROXY_AGENT_PORT, claims.clone(), ); assert_eq!(auth.to_string(), "ProxyAgent"); @@ -319,8 +320,8 @@ mod tests { ); let auth = super::get_authorizer( - crate::common::constants::PROXY_AGENT_IP.to_string(), - crate::common::constants::PROXY_AGENT_PORT + 1, + proxy_agent_shared::common::constants::PROXY_AGENT_IP.to_string(), + proxy_agent_shared::common::constants::PROXY_AGENT_PORT + 1, claims.clone(), ); assert_eq!(auth.to_string(), "Default"); @@ -342,8 +343,8 @@ mod tests { }; let mut test_logger = ConnectionLogger::new(1, 1); let auth = super::get_authorizer( - crate::common::constants::WIRE_SERVER_IP.to_string(), - crate::common::constants::WIRE_SERVER_PORT, + proxy_agent_shared::common::constants::WIRE_SERVER_IP.to_string(), + proxy_agent_shared::common::constants::WIRE_SERVER_PORT, claims.clone(), ); let url = hyper::Uri::from_str("http://localhost/test?").unwrap(); @@ -467,8 +468,8 @@ mod tests { clientPort: 0, // doesn't matter for this test }; let auth = super::get_authorizer( - crate::common::constants::IMDS_IP.to_string(), - crate::common::constants::IMDS_PORT, + proxy_agent_shared::common::constants::IMDS_IP.to_string(), + proxy_agent_shared::common::constants::IMDS_PORT, claims.clone(), ); let url = hyper::Uri::from_str("http://localhost/test?").unwrap(); @@ -577,8 +578,8 @@ mod tests { }; let mut test_logger = ConnectionLogger::new(1, 1); let auth = super::get_authorizer( - crate::common::constants::GA_PLUGIN_IP.to_string(), - crate::common::constants::GA_PLUGIN_PORT, + proxy_agent_shared::common::constants::GA_PLUGIN_IP.to_string(), + proxy_agent_shared::common::constants::GA_PLUGIN_PORT, claims.clone(), ); let url = hyper::Uri::from_str("http://localhost/test?").unwrap(); diff --git a/proxy_agent/src/proxy/proxy_connection.rs b/proxy_agent/src/proxy/proxy_connection.rs index eeb3b340..1b7bd60a 100644 --- a/proxy_agent/src/proxy/proxy_connection.rs +++ b/proxy_agent/src/proxy/proxy_connection.rs @@ -3,9 +3,6 @@ //! This module contains the connection context struct for the proxy listener, and write proxy processing logs to local file. -use crate::common::error::{Error, HyperErrorType}; -use crate::common::result::Result; -use crate::common::{config, hyper_client}; use crate::proxy::Claims; use crate::redirector::{self, AuditEntry}; use crate::shared_state::proxy_server_wrapper::ProxyServerSharedState; @@ -14,6 +11,9 @@ use http_body_util::Full; use hyper::body::Bytes; use hyper::client::conn::http1; use hyper::Request; +use proxy_agent_shared::common::error::{Error, HyperErrorType}; +use proxy_agent_shared::common::result::Result; +use proxy_agent_shared::common::{config, hyper_client}; use proxy_agent_shared::logger::{self, logger_manager, LoggerLevel}; use proxy_agent_shared::misc_helpers; use std::net::{Ipv4Addr, SocketAddr}; @@ -339,7 +339,9 @@ impl ConnectionLogger { // connection logger file log does not write to system log implicitly. logger_manager::write_system_log(logger_level, message.to_string()); - if let Some(log_for_event) = crate::common::config::get_file_log_level_for_events() { + if let Some(log_for_event) = + proxy_agent_shared::common::config::get_file_log_level_for_events() + { if log_for_event >= logger_level { // write to event proxy_agent_shared::telemetry::event_logger::write_event_only( diff --git a/proxy_agent/src/proxy/proxy_server.rs b/proxy_agent/src/proxy/proxy_server.rs index dc8fd445..918bdc45 100644 --- a/proxy_agent/src/proxy/proxy_server.rs +++ b/proxy_agent/src/proxy/proxy_server.rs @@ -10,7 +10,7 @@ //! //! Example: //! ```rust -//! use crate::common::config; +//! use proxy_agent_shared::common::config; //! use crate::proxy::proxy_server; //! use crate::shared_state::SharedState; //! @@ -22,12 +22,6 @@ use super::proxy_authorizer::AuthorizeResult; use super::proxy_connection::{ConnectionLogger, HttpConnectionContext, TcpConnectionContext}; -use crate::common::{ - constants, - error::{Error, HyperErrorType}, - helpers, hyper_client, logger, - result::Result, -}; use crate::provision; use crate::proxy::{proxy_authorizer, proxy_summary::ProxySummary, Claims}; use crate::shared_state::agent_status_wrapper::{AgentStatusModule, AgentStatusSharedState}; @@ -45,6 +39,12 @@ use hyper::service::service_fn; use hyper::StatusCode; use hyper::{Request, Response}; use hyper_util::rt::TokioIo; +use proxy_agent_shared::common::{ + constants, + error::{Error, HyperErrorType}, + helpers, hyper_client, logger, + result::Result, +}; use proxy_agent_shared::logger::LoggerLevel; use proxy_agent_shared::misc_helpers; use proxy_agent_shared::proxy_agent_aggregate_status::ModuleState; @@ -279,7 +279,10 @@ impl ProxyServer { let large_limited_tower_service = tower::ServiceBuilder::new().layer(large_limit_layer); let tower_service_layer = - if crate::common::hyper_client::should_skip_sig(req.method(), req.uri()) { + if proxy_agent_shared::common::hyper_client::should_skip_sig( + req.method(), + req.uri(), + ) { // skip signature check for large request large_limited_tower_service.clone() } else { @@ -1012,11 +1015,11 @@ impl ProxyServer { #[cfg(test)] mod tests { - use crate::common::hyper_client; - use crate::common::logger; use crate::proxy::proxy_server; use crate::shared_state; use http::Method; + use proxy_agent_shared::common::hyper_client; + use proxy_agent_shared::common::logger; use std::collections::HashMap; use std::time::Duration; diff --git a/proxy_agent/src/proxy/windows.rs b/proxy_agent/src/proxy/windows.rs index 145b84d7..174000b6 100644 --- a/proxy_agent/src/proxy/windows.rs +++ b/proxy_agent/src/proxy/windows.rs @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT -use crate::common::{ +use libloading::{Library, Symbol}; +use once_cell::sync::Lazy; +use proxy_agent_shared::common::{ error::{Error, WindowsApiErrorType}, logger, result::Result, }; -use libloading::{Library, Symbol}; -use once_cell::sync::Lazy; use std::mem::MaybeUninit; use std::ptr::null_mut; use std::{collections::HashMap, ffi::OsString, os::windows::ffi::OsStringExt, path::PathBuf}; diff --git a/proxy_agent/src/proxy_agent_status.rs b/proxy_agent/src/proxy_agent_status.rs index 777c1620..3a6eb429 100644 --- a/proxy_agent/src/proxy_agent_status.rs +++ b/proxy_agent/src/proxy_agent_status.rs @@ -28,10 +28,10 @@ //! tokio::spawn(proxy_agent_status_task.start()); //! ``` -use crate::common::logger; use crate::key_keeper::UNKNOWN_STATE; use crate::shared_state::agent_status_wrapper::{AgentStatusModule, AgentStatusSharedState}; use crate::shared_state::key_keeper_wrapper::KeyKeeperSharedState; +use proxy_agent_shared::common::logger; use proxy_agent_shared::logger::LoggerLevel; use proxy_agent_shared::misc_helpers; use proxy_agent_shared::proxy_agent_aggregate_status::{ diff --git a/proxy_agent/src/redirector.rs b/proxy_agent/src/redirector.rs index 07a33433..bbb05640 100644 --- a/proxy_agent/src/redirector.rs +++ b/proxy_agent/src/redirector.rs @@ -45,12 +45,6 @@ mod windows; #[cfg(not(windows))] mod linux; -use crate::common::constants; -use crate::common::error::BpfErrorType; -use crate::common::error::Error; -use crate::common::helpers; -use crate::common::result::Result; -use crate::common::{config, logger}; use crate::provision; use crate::proxy::authorization_rules::AuthorizationMode; use crate::shared_state::agent_status_wrapper::{AgentStatusModule, AgentStatusSharedState}; @@ -59,6 +53,12 @@ use crate::shared_state::provision_wrapper::ProvisionSharedState; use crate::shared_state::redirector_wrapper::RedirectorSharedState; use crate::shared_state::telemetry_wrapper::TelemetrySharedState; use crate::shared_state::SharedState; +use proxy_agent_shared::common::constants; +use proxy_agent_shared::common::error::BpfErrorType; +use proxy_agent_shared::common::error::Error; +use proxy_agent_shared::common::helpers; +use proxy_agent_shared::common::result::Result; +use proxy_agent_shared::common::{config, logger}; use proxy_agent_shared::logger::LoggerLevel; use proxy_agent_shared::misc_helpers; use proxy_agent_shared::proxy_agent_aggregate_status::ModuleState; diff --git a/proxy_agent/src/redirector/linux.rs b/proxy_agent/src/redirector/linux.rs index f973428e..58193076 100644 --- a/proxy_agent/src/redirector/linux.rs +++ b/proxy_agent/src/redirector/linux.rs @@ -2,12 +2,6 @@ // SPDX-License-Identifier: MIT mod ebpf_obj; -use crate::common::{ - config, constants, - error::{BpfErrorType, Error}, - logger, - result::Result, -}; use crate::redirector::{ip_to_string, AuditEntry}; use crate::shared_state::redirector_wrapper::RedirectorSharedState; use aya::programs::{CgroupSockAddr, KProbe}; @@ -19,6 +13,12 @@ use aya::{Btf, Ebpf, EbpfLoader}; use ebpf_obj::{ destination_entry, sock_addr_audit_entry, sock_addr_audit_key, sock_addr_skip_process_entry, }; +use proxy_agent_shared::common::{ + config, constants, + error::{BpfErrorType, Error}, + logger, + result::Result, +}; use proxy_agent_shared::telemetry::event_logger; use proxy_agent_shared::{logger::LoggerLevel, misc_helpers}; use std::convert::TryFrom; @@ -476,11 +476,11 @@ pub async fn update_hostga_redirect_policy( #[cfg(test)] #[cfg(feature = "test-with-root")] mod tests { - use crate::common::config; - use crate::common::constants; use crate::redirector::linux::ebpf_obj::sock_addr_audit_entry; use crate::redirector::linux::ebpf_obj::sock_addr_audit_key; use aya::maps::HashMap; + use proxy_agent_shared::common::config; + use proxy_agent_shared::common::constants; use proxy_agent_shared::misc_helpers; use std::env; diff --git a/proxy_agent/src/redirector/linux/ebpf_obj.rs b/proxy_agent/src/redirector/linux/ebpf_obj.rs index 706b2eaf..c00a9604 100644 --- a/proxy_agent/src/redirector/linux/ebpf_obj.rs +++ b/proxy_agent/src/redirector/linux/ebpf_obj.rs @@ -147,7 +147,7 @@ impl sock_addr_audit_entry { #[cfg(test)] mod tests { - use crate::common::constants; + use proxy_agent_shared::common::constants; #[test] fn destination_entry_test() { diff --git a/proxy_agent/src/redirector/windows.rs b/proxy_agent/src/redirector/windows.rs index cf23a3b8..66755940 100644 --- a/proxy_agent/src/redirector/windows.rs +++ b/proxy_agent/src/redirector/windows.rs @@ -5,11 +5,11 @@ mod bpf_api; mod bpf_obj; mod bpf_prog; -use crate::common::error::{BpfErrorType, Error, WindowsApiErrorType}; -use crate::common::{constants, logger, result::Result}; use crate::redirector::AuditEntry; use crate::shared_state::redirector_wrapper::RedirectorSharedState; use core::ffi::c_void; +use proxy_agent_shared::common::error::{BpfErrorType, Error, WindowsApiErrorType}; +use proxy_agent_shared::common::{constants, logger, result::Result}; use std::mem; use std::ptr; use windows_sys::Win32::Networking::WinSock; diff --git a/proxy_agent/src/redirector/windows/bpf_api.rs b/proxy_agent/src/redirector/windows/bpf_api.rs index 55b52bcb..0646b9d8 100644 --- a/proxy_agent/src/redirector/windows/bpf_api.rs +++ b/proxy_agent/src/redirector/windows/bpf_api.rs @@ -4,12 +4,12 @@ #![allow(non_snake_case)] use super::bpf_obj::*; -use crate::common::{ +use libloading::{Library, Symbol}; +use proxy_agent_shared::common::{ error::{BpfErrorType, Error}, logger, result::Result, }; -use libloading::{Library, Symbol}; use proxy_agent_shared::{ logger::LoggerLevel, misc_helpers, telemetry::event_logger, version::Version, }; diff --git a/proxy_agent/src/redirector/windows/bpf_prog.rs b/proxy_agent/src/redirector/windows/bpf_prog.rs index 71e51968..1372bcda 100644 --- a/proxy_agent/src/redirector/windows/bpf_prog.rs +++ b/proxy_agent/src/redirector/windows/bpf_prog.rs @@ -3,13 +3,13 @@ use super::bpf_api::*; use super::bpf_obj::*; use super::BpfObject; -use crate::common::constants; -use crate::common::logger; -use crate::common::{ +use crate::redirector::AuditEntry; +use proxy_agent_shared::common::constants; +use proxy_agent_shared::common::logger; +use proxy_agent_shared::common::{ error::{BpfErrorType, Error}, result::Result, }; -use crate::redirector::AuditEntry; use proxy_agent_shared::misc_helpers; use std::ffi::c_void; use std::mem::size_of_val; diff --git a/proxy_agent/src/service.rs b/proxy_agent/src/service.rs index 9de32681..00a5f172 100644 --- a/proxy_agent/src/service.rs +++ b/proxy_agent/src/service.rs @@ -3,12 +3,12 @@ #[cfg(windows)] pub mod windows_main; -use crate::common::{config, constants, helpers, logger}; use crate::key_keeper::KeyKeeper; use crate::proxy::proxy_connection::ConnectionLogger; use crate::proxy::proxy_server::ProxyServer; use crate::redirector::{self, Redirector}; use crate::shared_state::SharedState; +use proxy_agent_shared::common::{config, constants, helpers, logger}; use proxy_agent_shared::logger::rolling_logger::RollingLogger; use proxy_agent_shared::logger::{logger_manager, LoggerLevel}; use proxy_agent_shared::proxy_agent_aggregate_status; diff --git a/proxy_agent/src/service/windows_main.rs b/proxy_agent/src/service/windows_main.rs index d62170c3..2c0175c8 100644 --- a/proxy_agent/src/service/windows_main.rs +++ b/proxy_agent/src/service/windows_main.rs @@ -5,8 +5,8 @@ //! The GPA service is implemented as a Windows service using the windows_service crate. //! It is started, stopped, and controlled by the Windows service manager. -use crate::common::{constants, logger, result::Result}; use crate::{service, shared_state::SharedState}; +use proxy_agent_shared::common::{constants, logger, result::Result}; use std::time::Duration; use windows_service::service::{ ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus, ServiceType, diff --git a/proxy_agent/src/shared_state/agent_status_wrapper.rs b/proxy_agent/src/shared_state/agent_status_wrapper.rs index e7f55aa0..de00ad76 100644 --- a/proxy_agent/src/shared_state/agent_status_wrapper.rs +++ b/proxy_agent/src/shared_state/agent_status_wrapper.rs @@ -7,9 +7,10 @@ //! The proxy agent status contains the 'failed connection summary' of the proxy server. //! The proxy agent status contains the 'connection count' of the proxy server. -use crate::common::logger; -use crate::common::result::Result; -use crate::{common::error::Error, proxy::proxy_summary::ProxySummary}; +use crate::proxy::proxy_summary::ProxySummary; +use proxy_agent_shared::common::error::Error; +use proxy_agent_shared::common::logger; +use proxy_agent_shared::common::result::Result; use proxy_agent_shared::logger::LoggerLevel; use proxy_agent_shared::proxy_agent_aggregate_status::{ ModuleState, ProxyAgentDetailStatus, ProxyConnectionSummary, diff --git a/proxy_agent/src/shared_state/key_keeper_wrapper.rs b/proxy_agent/src/shared_state/key_keeper_wrapper.rs index de75b566..cccbbce3 100644 --- a/proxy_agent/src/shared_state/key_keeper_wrapper.rs +++ b/proxy_agent/src/shared_state/key_keeper_wrapper.rs @@ -1,12 +1,15 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT +use crate::key_keeper::key::AuthorizationItem; +use crate::key_keeper::key::Key; +use crate::proxy::authorization_rules::ComputedAuthorizationItem; /// The KeyKeeperState struct is used to send actions to the KeyKeeper module related shared state fields /// Example: /// ``` /// use crate::shared_state::key_keeper_wrapper::KeyKeeperState; /// use crate::key_keeper::key::Key; -/// use crate::common::result::Result; +/// use proxy_agent_shared::common::result::Result; /// use std::sync::Arc; /// use tokio::sync::Notify; /// @@ -36,11 +39,9 @@ /// Ok(()) /// } /// ``` -use crate::common::error::Error; -use crate::common::result::Result; -use crate::key_keeper::key::AuthorizationItem; -use crate::proxy::authorization_rules::ComputedAuthorizationItem; -use crate::{common::logger, key_keeper::key::Key}; +use proxy_agent_shared::common::error::Error; +use proxy_agent_shared::common::logger; +use proxy_agent_shared::common::result::Result; use std::sync::Arc; use tokio::sync::{mpsc, oneshot, Notify}; diff --git a/proxy_agent/src/shared_state/provision_wrapper.rs b/proxy_agent/src/shared_state/provision_wrapper.rs index e9e92609..7317c1df 100644 --- a/proxy_agent/src/shared_state/provision_wrapper.rs +++ b/proxy_agent/src/shared_state/provision_wrapper.rs @@ -28,10 +28,10 @@ //! assert_eq!(get_provision_finished, false); //! ``` -use crate::common::error::Error; -use crate::common::logger; -use crate::common::result::Result; use crate::provision::ProvisionFlags; +use proxy_agent_shared::common::error::Error; +use proxy_agent_shared::common::logger; +use proxy_agent_shared::common::result::Result; use proxy_agent_shared::misc_helpers; use tokio::sync::{mpsc, oneshot}; diff --git a/proxy_agent/src/shared_state/proxy_server_wrapper.rs b/proxy_agent/src/shared_state/proxy_server_wrapper.rs index e9ef02a2..82802ad9 100644 --- a/proxy_agent/src/shared_state/proxy_server_wrapper.rs +++ b/proxy_agent/src/shared_state/proxy_server_wrapper.rs @@ -16,10 +16,10 @@ //! assert_eq!(user.user_name, "user1"); //! ``` -use crate::common::error::Error; -use crate::common::logger; -use crate::common::result::Result; use crate::proxy::User; +use proxy_agent_shared::common::error::Error; +use proxy_agent_shared::common::logger; +use proxy_agent_shared::common::result::Result; use std::collections::HashMap; use tokio::sync::{mpsc, oneshot}; diff --git a/proxy_agent/src/shared_state/redirector_wrapper.rs b/proxy_agent/src/shared_state/redirector_wrapper.rs index a1e9d2dd..94d178e8 100644 --- a/proxy_agent/src/shared_state/redirector_wrapper.rs +++ b/proxy_agent/src/shared_state/redirector_wrapper.rs @@ -19,10 +19,10 @@ //! let bpf_object = redirector_shared_state.get_bpf_object().await.unwrap().unwrap(); //! ``` -use crate::common::error::Error; -use crate::common::logger; -use crate::common::result::Result; use crate::redirector; +use proxy_agent_shared::common::error::Error; +use proxy_agent_shared::common::logger; +use proxy_agent_shared::common::result::Result; use std::sync::{Arc, Mutex}; use tokio::sync::{mpsc, oneshot}; diff --git a/proxy_agent/src/shared_state/telemetry_wrapper.rs b/proxy_agent/src/shared_state/telemetry_wrapper.rs index f386de37..d4ae7ee3 100644 --- a/proxy_agent/src/shared_state/telemetry_wrapper.rs +++ b/proxy_agent/src/shared_state/telemetry_wrapper.rs @@ -14,9 +14,9 @@ //! assert_eq!(meta_data, vm_meta_data); //! ``` -use crate::common::result::Result; -use crate::common::{error::Error, logger}; use crate::telemetry::event_reader::VmMetaData; +use proxy_agent_shared::common::result::Result; +use proxy_agent_shared::common::{error::Error, logger}; use tokio::sync::{mpsc, oneshot}; enum TelemetryAction { diff --git a/proxy_agent/src/telemetry/event_reader.rs b/proxy_agent/src/telemetry/event_reader.rs index 06baadc7..b57c992f 100644 --- a/proxy_agent/src/telemetry/event_reader.rs +++ b/proxy_agent/src/telemetry/event_reader.rs @@ -41,13 +41,13 @@ use super::telemetry_event::TelemetryData; use super::telemetry_event::TelemetryEvent; -use crate::common::{constants, logger, result::Result}; use crate::host_clients::imds_client::ImdsClient; use crate::host_clients::wire_server_client::WireServerClient; use crate::shared_state::agent_status_wrapper::AgentStatusModule; use crate::shared_state::agent_status_wrapper::AgentStatusSharedState; use crate::shared_state::key_keeper_wrapper::KeyKeeperSharedState; use crate::shared_state::telemetry_wrapper::TelemetrySharedState; +use proxy_agent_shared::common::{constants, logger, result::Result}; use proxy_agent_shared::misc_helpers; use proxy_agent_shared::proxy_agent_aggregate_status::ModuleState; use proxy_agent_shared::telemetry::Event; @@ -376,9 +376,9 @@ impl EventReader { #[cfg(test)] mod tests { use super::*; - use crate::common::logger; use crate::key_keeper::key::Key; use crate::test_mock::server_mock; + use proxy_agent_shared::common::logger; use proxy_agent_shared::misc_helpers; use std::{env, fs}; diff --git a/proxy_agent/src/telemetry/telemetry_event.rs b/proxy_agent/src/telemetry/telemetry_event.rs index 84adee44..573d616f 100644 --- a/proxy_agent/src/telemetry/telemetry_event.rs +++ b/proxy_agent/src/telemetry/telemetry_event.rs @@ -47,8 +47,8 @@ //! ``` use super::event_reader::VmMetaData; -use crate::common::helpers; use once_cell::sync::Lazy; +use proxy_agent_shared::common::helpers; use proxy_agent_shared::telemetry::Event; use serde_derive::{Deserialize, Serialize}; diff --git a/proxy_agent/src/test_mock/server_mock.rs b/proxy_agent/src/test_mock/server_mock.rs index 394c7449..2ce86c1b 100644 --- a/proxy_agent/src/test_mock/server_mock.rs +++ b/proxy_agent/src/test_mock/server_mock.rs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT -use crate::common::{hyper_client, logger, result::Result}; use crate::key_keeper; use crate::key_keeper::key::{Key, KeyStatus}; use http_body_util::combinators::BoxBody; @@ -13,6 +12,7 @@ use hyper::Response; use hyper::StatusCode; use hyper_util::rt::TokioIo; use once_cell::sync::Lazy; +use proxy_agent_shared::common::{hyper_client, logger, result::Result}; use tokio::net::TcpListener; use tokio_util::sync::CancellationToken; use uuid::Uuid; diff --git a/proxy_agent_shared/Cargo.toml b/proxy_agent_shared/Cargo.toml index 6763e134..a49b7e64 100644 --- a/proxy_agent_shared/Cargo.toml +++ b/proxy_agent_shared/Cargo.toml @@ -22,12 +22,21 @@ base64 = "0.22.1" reqwest = { version = "0.12", features = ["json", "blocking", "rustls-tls"] } quick-xml = { version = "0.38.0", features = ["serialize", "serde-types"]} uuid = { version = "1.8", features = ["v4"] } +clap = { version = "4.5.17", features =["derive"] } # Command Line Argument Parser +http = "1.1.0" +http-body-util = "0.1" +hex = "0.4.3" # hex encode +hyper = { version = "1", features = ["server", "http1", "client"] } +hyper-util = { version = "0.1", features = ["tokio"] } +hmac-sha256 = "1.1.6" # use HMAC using the SHA-256 hash function +itertools = "0.10.5" # use to sort iterator elements into a new iterator in ascending order [target.'cfg(windows)'.dependencies] windows-service = "0.7.0" # windows NT service winreg = "0.11.0" # windows reg read/write serde-xml-rs = "0.8.1" # xml Deserializer with xml attribute chrono = "0.4.41" # parse date time string +libloading = "0.8.0" # for dynamic load libraries [target.'cfg(windows)'.dependencies.windows-sys] version = "0.52.0" diff --git a/proxy_agent/src/common.rs b/proxy_agent_shared/src/common.rs similarity index 100% rename from proxy_agent/src/common.rs rename to proxy_agent_shared/src/common.rs diff --git a/proxy_agent/src/common/cli.rs b/proxy_agent_shared/src/common/cli.rs similarity index 100% rename from proxy_agent/src/common/cli.rs rename to proxy_agent_shared/src/common/cli.rs diff --git a/proxy_agent/src/common/config.rs b/proxy_agent_shared/src/common/config.rs similarity index 98% rename from proxy_agent/src/common/config.rs rename to proxy_agent_shared/src/common/config.rs index d9d1fdab..9fa22ab5 100644 --- a/proxy_agent/src/common/config.rs +++ b/proxy_agent_shared/src/common/config.rs @@ -17,8 +17,8 @@ //! ``` use crate::common::constants; +use crate::{logger::LoggerLevel, misc_helpers}; use once_cell::sync::Lazy; -use proxy_agent_shared::{logger::LoggerLevel, misc_helpers}; use serde_derive::{Deserialize, Serialize}; use std::str::FromStr; use std::{path::PathBuf, time::Duration}; @@ -221,7 +221,7 @@ impl Config { mod tests { use crate::common::config::Config; use crate::common::constants; - use proxy_agent_shared::misc_helpers; + use crate::misc_helpers; use std::fs::File; use std::io::Write; use std::path::PathBuf; @@ -297,13 +297,13 @@ mod tests { } assert_eq!( - proxy_agent_shared::logger::LoggerLevel::Info, + crate::logger::LoggerLevel::Info, config.get_file_log_level_for_events().unwrap(), "get_file_log_level_for_events mismatch" ); assert_eq!( - proxy_agent_shared::logger::LoggerLevel::Info, + crate::logger::LoggerLevel::Info, config.get_file_log_level_for_system_events().unwrap(), "get_file_log_level_for_system_events mismatch" ); diff --git a/proxy_agent/src/common/constants.rs b/proxy_agent_shared/src/common/constants.rs similarity index 100% rename from proxy_agent/src/common/constants.rs rename to proxy_agent_shared/src/common/constants.rs diff --git a/proxy_agent/src/common/error.rs b/proxy_agent_shared/src/common/error.rs similarity index 100% rename from proxy_agent/src/common/error.rs rename to proxy_agent_shared/src/common/error.rs diff --git a/proxy_agent/src/common/helpers.rs b/proxy_agent_shared/src/common/helpers.rs similarity index 97% rename from proxy_agent/src/common/helpers.rs rename to proxy_agent_shared/src/common/helpers.rs index 112c957a..225505e8 100644 --- a/proxy_agent/src/common/helpers.rs +++ b/proxy_agent_shared/src/common/helpers.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: MIT use super::result::Result; use super::{error::Error, logger}; +use crate::misc_helpers; +use crate::telemetry::span::SimpleSpan; use once_cell::sync::Lazy; -use proxy_agent_shared::misc_helpers; -use proxy_agent_shared::telemetry::span::SimpleSpan; #[cfg(not(windows))] use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System}; diff --git a/proxy_agent/src/common/hyper_client.rs b/proxy_agent_shared/src/common/hyper_client.rs similarity index 99% rename from proxy_agent/src/common/hyper_client.rs rename to proxy_agent_shared/src/common/hyper_client.rs index bc52c375..01b5c2b9 100644 --- a/proxy_agent/src/common/hyper_client.rs +++ b/proxy_agent_shared/src/common/hyper_client.rs @@ -37,6 +37,7 @@ use super::error::{Error, HyperErrorType}; use super::result::Result; use super::{constants, helpers}; +use crate::misc_helpers; use http::request::Builder; use http::request::Parts; use http::Method; @@ -49,7 +50,6 @@ use hyper::Request; use hyper::Uri; use hyper_util::rt::TokioIo; use itertools::Itertools; -use proxy_agent_shared::misc_helpers; use serde::de::DeserializeOwned; use std::collections::HashMap; use tokio::net::TcpStream; diff --git a/proxy_agent/src/common/logger.rs b/proxy_agent_shared/src/common/logger.rs similarity index 98% rename from proxy_agent/src/common/logger.rs rename to proxy_agent_shared/src/common/logger.rs index 437a3f69..6a69a2de 100644 --- a/proxy_agent/src/common/logger.rs +++ b/proxy_agent_shared/src/common/logger.rs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT -use proxy_agent_shared::{ +use crate::{ logger::{logger_manager, LoggerLevel}, telemetry::event_logger, }; diff --git a/proxy_agent/src/common/result.rs b/proxy_agent_shared/src/common/result.rs similarity index 100% rename from proxy_agent/src/common/result.rs rename to proxy_agent_shared/src/common/result.rs diff --git a/proxy_agent/src/common/windows.rs b/proxy_agent_shared/src/common/windows.rs similarity index 99% rename from proxy_agent/src/common/windows.rs rename to proxy_agent_shared/src/common/windows.rs index 5513695e..aa433ce1 100644 --- a/proxy_agent/src/common/windows.rs +++ b/proxy_agent_shared/src/common/windows.rs @@ -139,7 +139,7 @@ pub fn fetch_key_data(encrypted_file_path: &Path) -> Result { mod tests { use std::{env, fs}; - use proxy_agent_shared::misc_helpers; + use crate::misc_helpers; #[test] fn get_processor_count_test() { diff --git a/proxy_agent_shared/src/lib.rs b/proxy_agent_shared/src/lib.rs index df397c75..46311cc5 100644 --- a/proxy_agent_shared/src/lib.rs +++ b/proxy_agent_shared/src/lib.rs @@ -3,6 +3,7 @@ pub mod certificate; pub mod client; +pub mod common; pub mod error; #[cfg(windows)] pub mod etw; From 0497690b891c433d14c9aad97c707e12ef6c7ef9 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 09:00:29 -0700 Subject: [PATCH 09/35] refactor --- Cargo.lock | 1134 +---------------- proxy_agent_shared/Cargo.toml | 1 - .../src/certificate/certificate_helper.rs | 7 +- .../certificate/certificate_helper_windows.rs | 10 +- .../src/client/data_model/mod.rs | 1 - .../src/client/hostga_plugin_client.rs | 131 +- .../src/client/wire_server_client.rs | 65 +- proxy_agent_shared/src/common.rs | 1 + proxy_agent_shared/src/common/error.rs | 11 + .../error.rs => common/formatted_error.rs} | 34 +- proxy_agent_shared/src/common/hyper_client.rs | 60 +- 11 files changed, 193 insertions(+), 1262 deletions(-) rename proxy_agent_shared/src/{client/data_model/error.rs => common/formatted_error.rs} (53%) diff --git a/Cargo.lock b/Cargo.lock index 8e432753..68895f78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,7 +15,7 @@ dependencies = [ "serde_json", "static_vcruntime", "sysinfo", - "thiserror 1.0.64", + "thiserror", "tokio", "windows-service", "winres", @@ -114,7 +114,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -124,7 +124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -133,12 +133,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" version = "1.3.0" @@ -159,7 +153,7 @@ dependencies = [ "log", "object", "once_cell", - "thiserror 1.0.64", + "thiserror", ] [[package]] @@ -173,7 +167,7 @@ dependencies = [ "hashbrown 0.15.2", "log", "object", - "thiserror 1.0.64", + "thiserror", ] [[package]] @@ -199,17 +193,17 @@ dependencies = [ "serde_json", "static_vcruntime", "sysinfo", - "thiserror 1.0.64", + "thiserror", "tokio", "tokio-util", - "tower 0.5.2", + "tower", "tower-http", "uuid", "uzers", "winapi", "windows-acl", "windows-service", - "windows-sys 0.52.0", + "windows-sys", "winres", ] @@ -290,7 +284,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -357,16 +351,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -431,54 +415,18 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.0", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "field-offset" version = "0.3.6" @@ -501,30 +449,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - [[package]] name = "futures-channel" version = "0.3.30" @@ -532,7 +456,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -541,12 +464,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - [[package]] name = "futures-sink" version = "0.3.31" @@ -566,13 +483,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", - "futures-io", - "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", - "slab", ] [[package]] @@ -582,24 +495,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if", - "js-sys", "libc", - "r-efi", - "wasi 0.14.7+wasi-0.2.4", - "wasm-bindgen", + "wasi", ] [[package]] @@ -608,25 +505,6 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" -[[package]] -name = "h2" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -736,7 +614,6 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2", "http", "http-body", "httparse", @@ -748,39 +625,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots 1.0.2", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.8" @@ -788,17 +632,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" dependencies = [ "bytes", - "futures-channel", "futures-util", "http", "http-body", "hyper", "pin-project-lite", - "socket2", "tokio", - "tower 0.4.13", - "tower-service", - "tracing", ] [[package]] @@ -825,113 +664,6 @@ dependencies = [ "cc", ] -[[package]] -name = "icu_collections" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" - -[[package]] -name = "icu_properties" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "potential_utf", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" - -[[package]] -name = "icu_provider" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" -dependencies = [ - "displaydoc", - "icu_locale_core", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - [[package]] name = "indexmap" version = "2.7.1" @@ -942,12 +674,6 @@ dependencies = [ "hashbrown 0.15.2", ] -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -995,30 +721,12 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - [[package]] name = "log" version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - [[package]] name = "memchr" version = "2.7.4" @@ -1034,12 +742,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1057,25 +759,8 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", -] - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", + "wasi", + "windows-sys", ] [[package]] @@ -1133,50 +818,6 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" -[[package]] -name = "openssl" -version = "0.10.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "os_info" version = "3.8.2" @@ -1185,33 +826,7 @@ checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" dependencies = [ "log", "serde", - "windows-sys 0.52.0", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "windows-sys", ] [[package]] @@ -1226,21 +841,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "potential_utf" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" -dependencies = [ - "zerovec", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -1272,7 +872,7 @@ dependencies = [ "clap", "proxy_agent_shared", "static_vcruntime", - "thiserror 1.0.64", + "thiserror", "tokio", "winres", ] @@ -1299,19 +899,18 @@ dependencies = [ "os_info", "quick-xml", "regex", - "reqwest", "serde", "serde-xml-rs", "serde_derive", "serde_json", - "thiserror 1.0.64", + "thiserror", "thread-id", "time", "tokio", "uuid", "windows 0.61.3", "windows-service", - "windows-sys 0.52.0", + "windows-sys", "winreg", ] @@ -1326,114 +925,33 @@ dependencies = [ ] [[package]] -name = "quinn" -version = "0.11.9" +name = "quote" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.16", - "tokio", - "tracing", - "web-time", + "proc-macro2", ] [[package]] -name = "quinn-proto" -version = "0.11.13" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "bytes", - "getrandom 0.3.3", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.16", - "tinyvec", - "tracing", - "web-time", + "libc", + "rand_chacha", + "rand_core", ] [[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand_chacha" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[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 0.9.3", + "rand_core", ] [[package]] @@ -1442,16 +960,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.3", + "getrandom", ] [[package]] @@ -1503,81 +1012,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "reqwest" -version = "0.12.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.26.11", - "windows-registry", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.15", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - [[package]] name = "rustc-demangle" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - [[package]] name = "rustc_version" version = "0.4.1" @@ -1587,63 +1027,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.0", -] - -[[package]] -name = "rustls" -version = "0.23.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.21" @@ -1656,38 +1039,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.0", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "semver" version = "1.0.23" @@ -1711,7 +1062,7 @@ checksum = "53630160a98edebde0123eb4dfd0fce6adff091b2305db3154a9e920206eb510" dependencies = [ "log", "serde", - "thiserror 1.0.64", + "thiserror", "xml-rs", ] @@ -1738,18 +1089,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "shlex" version = "1.3.0" @@ -1778,15 +1117,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "static_vcruntime" version = "2.0.0" @@ -1799,12 +1132,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "2.0.96" @@ -1821,20 +1148,6 @@ name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "sysinfo" @@ -1851,56 +1164,13 @@ dependencies = [ "windows 0.52.0", ] -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" -dependencies = [ - "fastrand", - "getrandom 0.3.3", - "once_cell", - "rustix", - "windows-sys 0.61.0", -] - [[package]] name = "thiserror" version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl 1.0.64", -] - -[[package]] -name = "thiserror" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" -dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl", ] [[package]] @@ -1914,17 +1184,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thiserror-impl" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "thread-id" version = "4.2.2" @@ -1966,31 +1225,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tinystr" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.43.1" @@ -1998,13 +1232,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "492a604e2fd7f814268a378409e6c92b5525d747d10db9a229723f55a417958c" dependencies = [ "backtrace", - "bytes", "libc", "mio", "pin-project-lite", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2018,26 +1251,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" -dependencies = [ - "rustls", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.12" @@ -2060,21 +1273,6 @@ dependencies = [ "serde", ] -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "pin-project", - "pin-project-lite", - "tokio", - "tower-layer", - "tower-service", -] - [[package]] name = "tower" version = "0.5.2" @@ -2154,30 +1352,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.2" @@ -2190,8 +1364,8 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ - "getrandom 0.2.15", - "rand 0.8.5", + "getrandom", + "rand", "uuid-macro-internal", ] @@ -2216,12 +1390,6 @@ dependencies = [ "log", ] -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.5" @@ -2243,24 +1411,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -2287,19 +1437,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -2332,44 +1469,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.2", -] - -[[package]] -name = "webpki-roots" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "widestring" version = "0.4.3" @@ -2423,7 +1522,7 @@ dependencies = [ "windows-collections", "windows-core 0.61.2", "windows-future", - "windows-link 0.1.3", + "windows-link", "windows-numerics", ] @@ -2465,9 +1564,9 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -2477,7 +1576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", - "windows-link 0.1.3", + "windows-link", "windows-threading", ] @@ -2509,12 +1608,6 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" -[[package]] -name = "windows-link" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" - [[package]] name = "windows-numerics" version = "0.2.0" @@ -2522,27 +1615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core 0.61.2", - "windows-link 0.1.3", -] - -[[package]] -name = "windows-registry" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" -dependencies = [ - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets", -] - -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets", + "windows-link", ] [[package]] @@ -2551,7 +1624,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -2562,17 +1635,7 @@ checksum = "d24d6bcc7f734a4091ecf8d7a64c5f7d7066f45585c1861eba06449909609c8a" dependencies = [ "bitflags", "widestring 1.1.0", - "windows-sys 0.52.0", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result 0.2.0", - "windows-targets", + "windows-sys", ] [[package]] @@ -2581,7 +1644,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -2593,15 +1656,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.61.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" -dependencies = [ - "windows-link 0.2.0", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -2624,7 +1678,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link 0.1.3", + "windows-link", ] [[package]] @@ -2694,48 +1748,12 @@ dependencies = [ "toml", ] -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "writeable" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" - [[package]] name = "xml-rs" version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" -[[package]] -name = "yoke" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.7.35" @@ -2756,63 +1774,3 @@ dependencies = [ "quote", "syn", ] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" - -[[package]] -name = "zerotrie" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/proxy_agent_shared/Cargo.toml b/proxy_agent_shared/Cargo.toml index a49b7e64..0912fab4 100644 --- a/proxy_agent_shared/Cargo.toml +++ b/proxy_agent_shared/Cargo.toml @@ -19,7 +19,6 @@ tokio = { version = "1", features = ["rt", "macros", "sync", "time"] } log = { version = "0.4.26", features = ["std"] } ctor = "0.3.6" # used for test setup and clean up base64 = "0.22.1" -reqwest = { version = "0.12", features = ["json", "blocking", "rustls-tls"] } quick-xml = { version = "0.38.0", features = ["serialize", "serde-types"]} uuid = { version = "1.8", features = ["v4"] } clap = { version = "4.5.17", features =["derive"] } # Command Line Argument Parser diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index a2ac9c61..c97d1c12 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -1,7 +1,6 @@ -use crate::client::data_model::error::ErrorDetails; - #[cfg(windows)] use crate::certificate::certificate_helper_windows::CertificateDetailsWindows; +use crate::common::formatted_error::FormattedError; #[cfg(windows)] type CertDetailsType = CertificateDetailsWindows; @@ -28,7 +27,7 @@ impl CertificateDetailsWrapper { pub fn generate_self_signed_certificate( _subject_name: &str, -) -> Result { +) -> Result { #[cfg(windows)] { use crate::certificate::certificate_helper_windows::generate_self_signed_certificate_windows; @@ -44,7 +43,7 @@ pub fn generate_self_signed_certificate( pub fn decrypt_from_base64( _base64_input: &str, _cert_details: &CertificateDetailsWrapper, -) -> Result { +) -> Result { #[cfg(windows)] { use crate::certificate::certificate_helper_windows::decrypt_from_base64_windows; diff --git a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs index 063c1d93..a2d310af 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper_windows.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper_windows.rs @@ -28,7 +28,7 @@ use windows::{ use crate::{ certificate::certificate_helper::CertificateDetailsWrapper, - client::data_model::error::ErrorDetails, + common::formatted_error::FormattedError, }; pub struct CertificateDetailsWindows { @@ -48,7 +48,7 @@ impl Drop for CertificateDetailsWindows { pub fn generate_self_signed_certificate_windows( subject_name: &str, -) -> Result { +) -> Result { // Open KSP let mut h_prov = NCRYPT_PROV_HANDLE(0); unsafe { @@ -181,7 +181,7 @@ pub fn generate_self_signed_certificate_windows( pub fn decrypt_from_base64_windows( base64_input: &str, cert_details_wrapper: &CertificateDetailsWrapper, -) -> Result { +) -> Result { let encrypted = base64_input.replace("\r", "").replace("\n", ""); let encrypted_payload = &base64::engine::general_purpose::STANDARD.decode(encrypted)?; let p_cert_ctx = cert_details_wrapper.cert_details.p_cert_ctx; @@ -218,7 +218,7 @@ pub fn decrypt_from_base64_windows( }; if msg_handle.is_null() { - return Err(ErrorDetails { + return Err(FormattedError { code: -1, message: "Failed to open message handle to decrypt.".to_string(), }); @@ -273,7 +273,7 @@ pub fn decrypt_from_base64_windows( fn build_cert_extensions( h_key: HCRYPTPROV_OR_NCRYPT_KEY_HANDLE, -) -> Result, ErrorDetails> { +) -> Result, FormattedError> { let mut extensions: Vec = Vec::new(); // Key Usage diff --git a/proxy_agent_shared/src/client/data_model/mod.rs b/proxy_agent_shared/src/client/data_model/mod.rs index 9664ab58..9f46f218 100644 --- a/proxy_agent_shared/src/client/data_model/mod.rs +++ b/proxy_agent_shared/src/client/data_model/mod.rs @@ -1,3 +1,2 @@ -pub mod error; pub mod hostga_plugin_model; pub mod wire_server_model; diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs index 01bb17ff..bdff2229 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -1,20 +1,23 @@ +use std::collections::HashMap; + use crate::certificate::certificate_helper::{ decrypt_from_base64, generate_self_signed_certificate, }; -use crate::client::data_model::error::ErrorDetails; use crate::client::data_model::hostga_plugin_model::{ Certificates, RawCertificatesPayload, VMSettings, }; +use crate::common::error::Error; +use crate::common::formatted_error::FormattedError; +use crate::common::hyper_client::read_response_body_as_string; +use crate::common::result::Result; +use crate::common::{hyper_client, logger}; use base64::Engine; -use reqwest::header::HeaderMap; -use reqwest::header::HeaderValue; -use reqwest::{Client, StatusCode}; +use http::{Method, StatusCode, Uri}; use serde::{Deserialize, Serialize}; use uuid::Uuid; pub struct HostGAPluginClient { base_url: String, - client: Client, } #[derive(Debug, Serialize, Deserialize)] @@ -28,6 +31,7 @@ pub struct HostGAPluginResponse { impl HostGAPluginClient { const CERTIFICATES_URL: &'static str = "certificates"; const VMSETTINGS_URL: &'static str = "vmSettings"; + const HOSTGAP_CIPHER: &'static str = "AES256_CBC"; const ETAG_HEADER: &'static str = "etag"; const X_MS_SERVER_VERSION_HEADER: &'static str = "x-ms-server-version"; @@ -38,14 +42,20 @@ impl HostGAPluginClient { pub fn new(base_url: &str) -> HostGAPluginClient { HostGAPluginClient { base_url: base_url.to_string(), - client: Client::new(), } } - pub async fn get_vmsettings(&self) -> Result, ErrorDetails> { + pub async fn get_vmsettings( + &self, + etag: Option, + ) -> Result> { + let mut headers = HashMap::new(); + if let Some(etag) = etag { + headers.insert(Self::ETAG_HEADER.to_string(), etag); + } self.get::( &format!("{}/{}", self.base_url, Self::VMSETTINGS_URL), - Option::None, + &headers, ) .await } @@ -53,21 +63,19 @@ impl HostGAPluginClient { pub async fn get_certificates( &self, cert_revision: u32, - ) -> Result, ErrorDetails> { + ) -> Result> { let cert = generate_self_signed_certificate(&Uuid::new_v4().to_string())?; - //let cert = get_cert_by_thumbprint("9bf93bb248b4504626c5d1247da2e7c5f8e0a03a")?; let cert_der = cert.get_public_cert_der(); let cert_base64 = base64::engine::general_purpose::STANDARD.encode(cert_der); - let mut headers = HeaderMap::new(); + let mut headers = HashMap::new(); + headers.insert(Self::TRANSPORT_CERTIFICATE_HEADER.to_string(), cert_base64); headers.insert( - Self::TRANSPORT_CERTIFICATE_HEADER, - HeaderValue::from_str(&cert_base64)?, - ); - headers.insert( - Self::TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER, - HeaderValue::from_str("AES256_CBC")?, + Self::TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER.to_string(), + Self::HOSTGAP_CIPHER.to_string(), ); + + // to-do: use logger println!("Requesting certificates with headers: {headers:?}"); let raw_certs_resp = self .get::( @@ -77,7 +85,7 @@ impl HostGAPluginClient { Self::CERTIFICATES_URL, cert_revision ), - Some(headers), + &headers, ) .await?; @@ -89,62 +97,58 @@ impl HostGAPluginClient { let certs = decrypt_from_base64(cert_base64, &cert)?; return Ok(HostGAPluginResponse { - body: Some(serde_json::from_str::(&certs)?), + body: Some( + serde_json::from_str::(&certs) + .map_err(|e| FormattedError::from(e))?, + ), etag: raw_certs_resp.etag.clone(), certificates_revision: raw_certs_resp.certificates_revision, version: raw_certs_resp.version.clone(), }); } - - Err(ErrorDetails { + Err(FormattedError { message: "certificate payload is empty.".to_string(), code: -1, - }) + } + .into()) } pub async fn get( &self, url: &str, - headers_map: Option, - ) -> Result, ErrorDetails> + headers: &HashMap, + ) -> Result> where for<'a> T: Deserialize<'a>, { - let mut request = self.client.get(url); - println!("Requesting URL: {url}"); - if let Some(headers) = headers_map { - request = request.headers(headers); - } + let url: Uri = url + .parse::() + .map_err(|e| Error::ParseUrl(url.to_string(), e.to_string()))?; - let resp = request.send().await.map_err(|e| { - let mut error_code = -1; - if let Some(status) = e.status() { - error_code = status.as_u16() as i32; - } - ErrorDetails { - code: error_code, - message: format!("HostGAPlugin Request Error: {e}, url: {url}"), - } - })?; + let request = hyper_client::build_request(Method::GET, &url, headers, None, None, None)?; - let headers = resp.headers(); + let (host, port) = hyper_client::host_port_from_uri(&url)?; + let response = + hyper_client::send_request(&host, port, request, logger::write_warning).await?; - let etag = headers + let etag = response + .headers() .get(Self::ETAG_HEADER) .and_then(|v| v.to_str().ok()) .map(|v| v.to_string()); - - let certificates_revision = headers - .get(Self::X_MS_CERTIFICATES_REVISION_HEADER) - .and_then(|v| v.to_str().ok()) - .and_then(|s| s.parse::().ok()); - - let version = headers + let version = response + .headers() .get(Self::X_MS_SERVER_VERSION_HEADER) .and_then(|v| v.to_str().ok()) .map(|v| v.to_string()); + let certificates_revision = response + .headers() + .get(Self::X_MS_CERTIFICATES_REVISION_HEADER) + .and_then(|v| v.to_str().ok()) + .and_then(|s| s.parse::().ok()); - if resp.status() == StatusCode::NOT_MODIFIED { + let status = response.status(); + if status == StatusCode::NOT_MODIFIED { let _hostgap_response: HostGAPluginResponse = HostGAPluginResponse { body: None, etag, @@ -152,31 +156,20 @@ impl HostGAPluginClient { certificates_revision, }; return Ok(_hostgap_response); - } else if resp.status().is_success() { - let body = resp.text().await.map_err(|e| ErrorDetails { - code: -1, - message: format!("Failed to get response body: {e}"), - })?; - let body_json = serde_json::from_str::(&body).map_err(|e| ErrorDetails { - code: -1, - message: format!("Failed to deserialized json payload, error: {e}"), - })?; + } else if status.is_success() { + let body_obj = hyper_client::read_response_body::(response).await?; return Ok(HostGAPluginResponse { - body: Option::Some(body_json), + body: Some(body_obj), etag, - version, certificates_revision, + version, }); } - - let status = resp.status(); - Err(ErrorDetails { + let body_string = read_response_body_as_string(response, "utf-8").await?; + Err(FormattedError { code: status.as_u16() as i32, - message: format!( - "Http Error Status: {}, Body: {}", - status, - resp.text().await.unwrap_or_default() - ), - }) + message: format!("Http Error Status: {}, Body: {}", status, &body_string), + } + .into()) } } diff --git a/proxy_agent_shared/src/client/wire_server_client.rs b/proxy_agent_shared/src/client/wire_server_client.rs index 0338cee7..4afb73b5 100644 --- a/proxy_agent_shared/src/client/wire_server_client.rs +++ b/proxy_agent_shared/src/client/wire_server_client.rs @@ -1,16 +1,16 @@ -use quick_xml::de::from_str; -use reqwest::Client; +use std::collections::HashMap; + +use http::Uri; use serde::Deserialize; -use crate::client::data_model::{ - error::ErrorDetails, - wire_server_model::{GoalState, Versions}, +use crate::{ + client::data_model::wire_server_model::{GoalState, Versions}, + common::{error::Error, hyper_client, logger, result::Result}, }; pub struct WireServerClient { base_url: String, version: String, - client: Client, } impl WireServerClient { @@ -22,21 +22,20 @@ impl WireServerClient { WireServerClient { base_url: base_url.to_string(), version: "2015-04-05".to_string(), - client: Client::new(), } } // http://168.63.129.16?comp=Versions - pub async fn get_versions(&self) -> Result { + pub async fn get_versions(&self) -> Result { self.get::(Self::VERSIONS_URL).await } // http://168.63.129.16/machine?comp=goalstate - pub async fn get_goal_state(&self) -> Result { + pub async fn get_goal_state(&self) -> Result { self.get::(Self::GOAL_STATE_URL).await } - pub async fn get(&self, sub_url: &str) -> Result + pub async fn get(&self, sub_url: &str) -> Result where T: for<'a> Deserialize<'a>, { @@ -44,44 +43,20 @@ impl WireServerClient { .await } - pub async fn get_url(&self, url: &str) -> Result + pub async fn get_url(&self, url: &str) -> Result where T: for<'a> Deserialize<'a>, { - match self - .client - .get(url) - .header(Self::X_MS_VERSION_HEADER, &self.version) - .send() + let url: Uri = url + .parse::() + .map_err(|e| Error::ParseUrl(url.to_string(), e.to_string()))?; + + let mut headers = HashMap::new(); + headers.insert(Self::X_MS_VERSION_HEADER.to_string(), self.version.clone()); + + let res = hyper_client::get(&url, &headers, None, None, logger::write_warning) .await - { - Ok(resp) => { - if resp.status().is_success() { - let body = resp.text().await.map_err(|e| ErrorDetails { - code: -1, - message: format!("{e}"), - })?; - let result = from_str::(&body).map_err(|e| ErrorDetails { - code: -2, - message: format!("XML Deserialization Failed: {e}"), - })?; - Ok(result) - } else { - let status = resp.status(); - Err(ErrorDetails { - code: status.as_u16() as i32, - message: format!( - "Http Error Status: {}, Body: {}", - status, - resp.text().await.unwrap_or_default() - ), - }) - } - } - Err(e) => Err(ErrorDetails { - code: -3, - message: format!("Request Error: {e}"), - }), - } + .unwrap(); + Ok(res) } } diff --git a/proxy_agent_shared/src/common.rs b/proxy_agent_shared/src/common.rs index 3afa2dac..37e2ebdd 100644 --- a/proxy_agent_shared/src/common.rs +++ b/proxy_agent_shared/src/common.rs @@ -4,6 +4,7 @@ pub mod cli; pub mod config; pub mod constants; pub mod error; +pub mod formatted_error; pub mod helpers; pub mod hyper_client; pub mod logger; diff --git a/proxy_agent_shared/src/common/error.rs b/proxy_agent_shared/src/common/error.rs index 4dee7fb0..75c87c7c 100644 --- a/proxy_agent_shared/src/common/error.rs +++ b/proxy_agent_shared/src/common/error.rs @@ -3,6 +3,8 @@ use http::{uri::InvalidUri, StatusCode}; +use crate::common::formatted_error::FormattedError; + #[derive(Debug, thiserror::Error)] pub enum Error { #[error("IO error: {0}: {1}")] @@ -48,6 +50,15 @@ pub enum Error { #[error("{0}")] FindAuditEntryError(String), + + #[error("{0}")] + OtherError(FormattedError), +} + +impl From for Error { + fn from(value: FormattedError) -> Self { + Error::OtherError(value) + } } #[derive(Debug, thiserror::Error)] diff --git a/proxy_agent_shared/src/client/data_model/error.rs b/proxy_agent_shared/src/common/formatted_error.rs similarity index 53% rename from proxy_agent_shared/src/client/data_model/error.rs rename to proxy_agent_shared/src/common/formatted_error.rs index f8c74c07..aa2e85b4 100644 --- a/proxy_agent_shared/src/client/data_model/error.rs +++ b/proxy_agent_shared/src/common/formatted_error.rs @@ -1,52 +1,42 @@ use std::{fmt, string::FromUtf8Error}; use base64::DecodeError; -use reqwest::header::InvalidHeaderValue; #[derive(Debug, Clone)] -pub struct ErrorDetails { +pub struct FormattedError { pub message: String, pub code: i32, } -impl fmt::Display for ErrorDetails { +impl fmt::Display for FormattedError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message) + write!(f, "code: {}, message: {}", self.code, self.message) } } -impl std::error::Error for ErrorDetails {} +impl std::error::Error for FormattedError {} -impl From for ErrorDetails { +impl From for FormattedError { fn from(value: DecodeError) -> Self { - ErrorDetails { + FormattedError { message: format!("Decode Error: {value:?}"), code: -1, } } } -impl From for ErrorDetails { +impl From for FormattedError { fn from(value: FromUtf8Error) -> Self { - ErrorDetails { + FormattedError { message: format!("Uft8 Convert Error: {value:?}"), code: -1, } } } -impl From for ErrorDetails { - fn from(value: InvalidHeaderValue) -> Self { - ErrorDetails { - message: format!("Invalid Http Header Value: {value:?}"), - code: -1, - } - } -} - -impl From for ErrorDetails { +impl From for FormattedError { fn from(value: serde_json::Error) -> Self { - ErrorDetails { + FormattedError { message: format!("Json Error: {value:?}"), code: -1, } @@ -54,9 +44,9 @@ impl From for ErrorDetails { } #[cfg(windows)] -impl From for ErrorDetails { +impl From for FormattedError { fn from(value: windows::core::Error) -> Self { - ErrorDetails { + FormattedError { message: format!("Windows API Error: {value:?}"), code: -1, } diff --git a/proxy_agent_shared/src/common/hyper_client.rs b/proxy_agent_shared/src/common/hyper_client.rs index 01b5c2b9..a9b2c450 100644 --- a/proxy_agent_shared/src/common/hyper_client.rs +++ b/proxy_agent_shared/src/common/hyper_client.rs @@ -82,9 +82,7 @@ where read_response_body(response).await } -pub async fn read_response_body( - mut response: hyper::Response, -) -> Result +pub async fn read_response_body(response: hyper::Response) -> Result where T: DeserializeOwned, { @@ -123,6 +121,37 @@ where ("unknown", "unknown") }; + let body_string = read_response_body_as_string(response, charset_type).await?; + + match content_type { + "xml" => match serde_xml_rs::from_str(&body_string) { + Ok(t) => Ok(t), + Err(e) => Err(Error::Hyper( + HyperErrorType::Deserialize( + format!( + "Failed to xml deserialize response body with content_type {content_type} from: {body_string} with error {e}" + ) + ), + )), + }, + // default to json + _ => match serde_json::from_str(&body_string) { + Ok(t) => Ok(t), + Err(e) => Err(Error::Hyper( + HyperErrorType::Deserialize( + format!( + "Failed to json deserialize response body with {content_type} from: {body_string} with error {e}" + ) + ), + )), + }, + } +} + +pub async fn read_response_body_as_string( + mut response: hyper::Response, + charset_type: &str, +) -> Result { let mut body_string = String::new(); while let Some(next) = response.frame().await { let frame = match next { @@ -159,30 +188,7 @@ where }; } } - - match content_type { - "xml" => match serde_xml_rs::from_str(&body_string) { - Ok(t) => Ok(t), - Err(e) => Err(Error::Hyper( - HyperErrorType::Deserialize( - format!( - "Failed to xml deserialize response body with content_type {content_type} from: {body_string} with error {e}" - ) - ), - )), - }, - // default to json - _ => match serde_json::from_str(&body_string) { - Ok(t) => Ok(t), - Err(e) => Err(Error::Hyper( - HyperErrorType::Deserialize( - format!( - "Failed to json deserialize response body with {content_type} from: {body_string} with error {e}" - ) - ), - )), - }, - } + Ok(body_string) } pub fn build_request( From 36ff3877002927d2c18e543a7d734e6a5ab0f039 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 09:21:14 -0700 Subject: [PATCH 10/35] build fixes --- proxy_agent_shared/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy_agent_shared/Cargo.toml b/proxy_agent_shared/Cargo.toml index 0912fab4..f0fc8947 100644 --- a/proxy_agent_shared/Cargo.toml +++ b/proxy_agent_shared/Cargo.toml @@ -29,11 +29,11 @@ hyper = { version = "1", features = ["server", "http1", "client"] } hyper-util = { version = "0.1", features = ["tokio"] } hmac-sha256 = "1.1.6" # use HMAC using the SHA-256 hash function itertools = "0.10.5" # use to sort iterator elements into a new iterator in ascending order +serde-xml-rs = "0.8.1" # xml Deserializer with xml attribute [target.'cfg(windows)'.dependencies] windows-service = "0.7.0" # windows NT service winreg = "0.11.0" # windows reg read/write -serde-xml-rs = "0.8.1" # xml Deserializer with xml attribute chrono = "0.4.41" # parse date time string libloading = "0.8.0" # for dynamic load libraries @@ -50,6 +50,7 @@ features = [ "Win32_System_Diagnostics_Debug", "Win32_System_SystemInformation", "Win32_Storage_FileSystem", + "Win32_Security_Cryptography", ] [target.'cfg(not(windows))'.dependencies] From 0bd95bd247e4a832810133227789a6254b892105 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 09:30:45 -0700 Subject: [PATCH 11/35] fixes --- Cargo.lock | 1 + proxy_agent_shared/Cargo.toml | 1 + proxy_agent_shared/src/client/hostga_plugin_client.rs | 3 +-- proxy_agent_shared/src/common/logger.rs | 2 +- proxy_agent_shared/src/misc_helpers.rs | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 68895f78..14483e3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -903,6 +903,7 @@ dependencies = [ "serde-xml-rs", "serde_derive", "serde_json", + "sysinfo", "thiserror", "thread-id", "time", diff --git a/proxy_agent_shared/Cargo.toml b/proxy_agent_shared/Cargo.toml index f0fc8947..63ce6a59 100644 --- a/proxy_agent_shared/Cargo.toml +++ b/proxy_agent_shared/Cargo.toml @@ -55,6 +55,7 @@ features = [ [target.'cfg(not(windows))'.dependencies] os_info = "3.7.0" # read Linux OS version and arch +sysinfo = "0.30.13" # read process information for Linux [target.'cfg(windows)'.dependencies.windows] version = "0.61.3" diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs index bdff2229..4fdd452f 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -98,8 +98,7 @@ impl HostGAPluginClient { return Ok(HostGAPluginResponse { body: Some( - serde_json::from_str::(&certs) - .map_err(|e| FormattedError::from(e))?, + serde_json::from_str::(&certs).map_err(FormattedError::from)?, ), etag: raw_certs_resp.etag.clone(), certificates_revision: raw_certs_resp.certificates_revision, diff --git a/proxy_agent_shared/src/common/logger.rs b/proxy_agent_shared/src/common/logger.rs index 6a69a2de..27fe6f80 100644 --- a/proxy_agent_shared/src/common/logger.rs +++ b/proxy_agent_shared/src/common/logger.rs @@ -43,7 +43,7 @@ fn log(log_level: LoggerLevel, message: String) { #[cfg(not(windows))] pub fn write_serial_console_log(message: String) { - use proxy_agent_shared::misc_helpers; + use crate::misc_helpers; use std::io::Write; let message = format!( diff --git a/proxy_agent_shared/src/misc_helpers.rs b/proxy_agent_shared/src/misc_helpers.rs index 676a8e15..ca61fae0 100644 --- a/proxy_agent_shared/src/misc_helpers.rs +++ b/proxy_agent_shared/src/misc_helpers.rs @@ -183,7 +183,7 @@ pub fn get_files(dir: &Path) -> Result> { /// # Example /// ```rust /// use std::path::PathBuf; -/// use proxy_agent_shared::misc_helpers; +/// use crate::misc_helpers; /// let dir = PathBuf::from("C:\\"); /// let search_regex_pattern = r"^(.*\.log)$"; // search for files with .log extension /// let files = misc_helpers::search_files(&dir, search_regex_pattern).unwrap(); From 8729c4c245630eddda323c55a411740a0d3e47c1 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 09:44:30 -0700 Subject: [PATCH 12/35] build fixes on spell --- .github/actions/spelling/expect.txt | 19 ++++++++++++++++++- .../src/common/formatted_error.rs | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 7809ed51..2cde7b3b 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -364,4 +364,21 @@ xsi xxxx xxxxxxxx xxxxxxxxxxx -zipsas \ No newline at end of file +zipsas +CALG +CNG +detials +HCRYPTPROV +hostgap +innerbandwidth +innerlatency +KSP +ncrypt +outwardbandwidth +outwardlatency +PCWSTR +PSTR +psz +rsm +SELFSIGN +vmsettings \ No newline at end of file diff --git a/proxy_agent_shared/src/common/formatted_error.rs b/proxy_agent_shared/src/common/formatted_error.rs index aa2e85b4..3053fe92 100644 --- a/proxy_agent_shared/src/common/formatted_error.rs +++ b/proxy_agent_shared/src/common/formatted_error.rs @@ -28,7 +28,7 @@ impl From for FormattedError { impl From for FormattedError { fn from(value: FromUtf8Error) -> Self { FormattedError { - message: format!("Uft8 Convert Error: {value:?}"), + message: format!("Utf-8 Convert Error: {value:?}"), code: -1, } } From 7e3dc240072fdbde536f6edb87f4fed4d5862dee Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 10:12:23 -0700 Subject: [PATCH 13/35] add logger to client --- .../src/client/hostga_plugin_client.rs | 25 +++++++++++++++---- .../src/client/wire_server_client.rs | 16 ++++++++---- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/client/hostga_plugin_client.rs index 4fdd452f..0c729847 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/client/hostga_plugin_client.rs @@ -8,9 +8,10 @@ use crate::client::data_model::hostga_plugin_model::{ }; use crate::common::error::Error; use crate::common::formatted_error::FormattedError; +use crate::common::hyper_client; use crate::common::hyper_client::read_response_body_as_string; use crate::common::result::Result; -use crate::common::{hyper_client, logger}; +use crate::logger::LoggerLevel; use base64::Engine; use http::{Method, StatusCode, Uri}; use serde::{Deserialize, Serialize}; @@ -18,6 +19,7 @@ use uuid::Uuid; pub struct HostGAPluginClient { base_url: String, + logger: fn(LoggerLevel, String) -> (), } #[derive(Debug, Serialize, Deserialize)] @@ -39,9 +41,10 @@ impl HostGAPluginClient { const TRANSPORT_CERTIFICATE_HEADER: &'static str = "x-ms-guest-agent-public-x509-cert"; const TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER: &'static str = "x-ms-cipher-name"; - pub fn new(base_url: &str) -> HostGAPluginClient { + pub fn new(base_url: &str, logger: fn(LoggerLevel, String) -> ()) -> HostGAPluginClient { HostGAPluginClient { base_url: base_url.to_string(), + logger, } } @@ -49,6 +52,12 @@ impl HostGAPluginClient { &self, etag: Option, ) -> Result> { + let logger = self.logger; + + logger( + LoggerLevel::Info, + format!("Requesting VMSettings with etag: {etag:?}"), + ); let mut headers = HashMap::new(); if let Some(etag) = etag { headers.insert(Self::ETAG_HEADER.to_string(), etag); @@ -64,6 +73,12 @@ impl HostGAPluginClient { &self, cert_revision: u32, ) -> Result> { + let logger = self.logger; + logger( + LoggerLevel::Info, + format!("Requesting certificates with revision: {cert_revision}"), + ); + let cert = generate_self_signed_certificate(&Uuid::new_v4().to_string())?; let cert_der = cert.get_public_cert_der(); let cert_base64 = base64::engine::general_purpose::STANDARD.encode(cert_der); @@ -75,8 +90,6 @@ impl HostGAPluginClient { Self::HOSTGAP_CIPHER.to_string(), ); - // to-do: use logger - println!("Requesting certificates with headers: {headers:?}"); let raw_certs_resp = self .get::( &format!( @@ -120,6 +133,7 @@ impl HostGAPluginClient { where for<'a> T: Deserialize<'a>, { + let logger = self.logger; let url: Uri = url .parse::() .map_err(|e| Error::ParseUrl(url.to_string(), e.to_string()))?; @@ -128,7 +142,8 @@ impl HostGAPluginClient { let (host, port) = hyper_client::host_port_from_uri(&url)?; let response = - hyper_client::send_request(&host, port, request, logger::write_warning).await?; + hyper_client::send_request(&host, port, request, move |m| logger(LoggerLevel::Warn, m)) + .await?; let etag = response .headers() diff --git a/proxy_agent_shared/src/client/wire_server_client.rs b/proxy_agent_shared/src/client/wire_server_client.rs index 4afb73b5..be5af205 100644 --- a/proxy_agent_shared/src/client/wire_server_client.rs +++ b/proxy_agent_shared/src/client/wire_server_client.rs @@ -5,12 +5,14 @@ use serde::Deserialize; use crate::{ client::data_model::wire_server_model::{GoalState, Versions}, - common::{error::Error, hyper_client, logger, result::Result}, + common::{error::Error, hyper_client, result::Result}, + logger::LoggerLevel, }; pub struct WireServerClient { base_url: String, version: String, + logger: fn(LoggerLevel, String) -> (), } impl WireServerClient { @@ -18,10 +20,11 @@ impl WireServerClient { const VERSIONS_URL: &'static str = "?comp=Versions"; const GOAL_STATE_URL: &'static str = "machine?comp=goalstate"; - pub fn new(base_url: &str) -> WireServerClient { + pub fn new(base_url: &str, logger: fn(LoggerLevel, String) -> ()) -> WireServerClient { WireServerClient { base_url: base_url.to_string(), version: "2015-04-05".to_string(), + logger, } } @@ -47,6 +50,7 @@ impl WireServerClient { where T: for<'a> Deserialize<'a>, { + let logger = self.logger; let url: Uri = url .parse::() .map_err(|e| Error::ParseUrl(url.to_string(), e.to_string()))?; @@ -54,9 +58,11 @@ impl WireServerClient { let mut headers = HashMap::new(); headers.insert(Self::X_MS_VERSION_HEADER.to_string(), self.version.clone()); - let res = hyper_client::get(&url, &headers, None, None, logger::write_warning) - .await - .unwrap(); + let res = hyper_client::get(&url, &headers, None, None, move |message| { + logger(LoggerLevel::Warn, message) + }) + .await + .unwrap(); Ok(res) } } From de8cc32dd600cdb472fb18966c974af54b3302b2 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 11:03:58 -0700 Subject: [PATCH 14/35] rename client folder, to-do: unit test coverage --- .../data_model/hostga_plugin_model.rs | 0 .../src/{client => host_clients}/data_model/mod.rs | 0 .../data_model/wire_server_model.rs | 0 .../src/{client => host_clients}/hostga_plugin_client.rs | 6 +++--- proxy_agent_shared/src/{client => host_clients}/mod.rs | 0 .../src/{client => host_clients}/wire_server_client.rs | 2 +- proxy_agent_shared/src/lib.rs | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename proxy_agent_shared/src/{client => host_clients}/data_model/hostga_plugin_model.rs (100%) rename proxy_agent_shared/src/{client => host_clients}/data_model/mod.rs (100%) rename proxy_agent_shared/src/{client => host_clients}/data_model/wire_server_model.rs (100%) rename proxy_agent_shared/src/{client => host_clients}/hostga_plugin_client.rs (99%) rename proxy_agent_shared/src/{client => host_clients}/mod.rs (100%) rename proxy_agent_shared/src/{client => host_clients}/wire_server_client.rs (96%) diff --git a/proxy_agent_shared/src/client/data_model/hostga_plugin_model.rs b/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs similarity index 100% rename from proxy_agent_shared/src/client/data_model/hostga_plugin_model.rs rename to proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs diff --git a/proxy_agent_shared/src/client/data_model/mod.rs b/proxy_agent_shared/src/host_clients/data_model/mod.rs similarity index 100% rename from proxy_agent_shared/src/client/data_model/mod.rs rename to proxy_agent_shared/src/host_clients/data_model/mod.rs diff --git a/proxy_agent_shared/src/client/data_model/wire_server_model.rs b/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs similarity index 100% rename from proxy_agent_shared/src/client/data_model/wire_server_model.rs rename to proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs diff --git a/proxy_agent_shared/src/client/hostga_plugin_client.rs b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs similarity index 99% rename from proxy_agent_shared/src/client/hostga_plugin_client.rs rename to proxy_agent_shared/src/host_clients/hostga_plugin_client.rs index 0c729847..4a84c90c 100644 --- a/proxy_agent_shared/src/client/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs @@ -3,14 +3,14 @@ use std::collections::HashMap; use crate::certificate::certificate_helper::{ decrypt_from_base64, generate_self_signed_certificate, }; -use crate::client::data_model::hostga_plugin_model::{ - Certificates, RawCertificatesPayload, VMSettings, -}; use crate::common::error::Error; use crate::common::formatted_error::FormattedError; use crate::common::hyper_client; use crate::common::hyper_client::read_response_body_as_string; use crate::common::result::Result; +use crate::host_clients::data_model::hostga_plugin_model::{ + Certificates, RawCertificatesPayload, VMSettings, +}; use crate::logger::LoggerLevel; use base64::Engine; use http::{Method, StatusCode, Uri}; diff --git a/proxy_agent_shared/src/client/mod.rs b/proxy_agent_shared/src/host_clients/mod.rs similarity index 100% rename from proxy_agent_shared/src/client/mod.rs rename to proxy_agent_shared/src/host_clients/mod.rs diff --git a/proxy_agent_shared/src/client/wire_server_client.rs b/proxy_agent_shared/src/host_clients/wire_server_client.rs similarity index 96% rename from proxy_agent_shared/src/client/wire_server_client.rs rename to proxy_agent_shared/src/host_clients/wire_server_client.rs index be5af205..68472715 100644 --- a/proxy_agent_shared/src/client/wire_server_client.rs +++ b/proxy_agent_shared/src/host_clients/wire_server_client.rs @@ -4,8 +4,8 @@ use http::Uri; use serde::Deserialize; use crate::{ - client::data_model::wire_server_model::{GoalState, Versions}, common::{error::Error, hyper_client, result::Result}, + host_clients::data_model::wire_server_model::{GoalState, Versions}, logger::LoggerLevel, }; diff --git a/proxy_agent_shared/src/lib.rs b/proxy_agent_shared/src/lib.rs index 46311cc5..d81a54c3 100644 --- a/proxy_agent_shared/src/lib.rs +++ b/proxy_agent_shared/src/lib.rs @@ -2,11 +2,11 @@ // SPDX-License-Identifier: MIT pub mod certificate; -pub mod client; pub mod common; pub mod error; #[cfg(windows)] pub mod etw; +pub mod host_clients; pub mod logger; pub mod misc_helpers; pub mod proxy_agent_aggregate_status; From 48507328cd6128983e143e79861916cfc1048df3 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 11:28:37 -0700 Subject: [PATCH 15/35] unit test --- .../src/host_clients/hostga_plugin_client.rs | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs index 4a84c90c..e57b795c 100644 --- a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs @@ -187,3 +187,146 @@ impl HostGAPluginClient { .into()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_hostgaplugin_certificates_response_test() { + let response = r#" + { + "body": { + "activityId": "c92847b6-1c5d-402e-b919-c0157f4bda74", + "correlationId": "80e22e3b-3f9a-424e-b300-6cda2dd7e718", + "certificates": [ + { + "name": "TenantEncryptionCert", + "storeName": "My", + "configurationLevel": "System", + "certificateInBase64": "certificateInBase64_test", + "includePrivateKey": false, + "thumbprint": "thumbprint_test", + "certificateBlobFormatType": "PfxInClear" + } + ] + }, + "etag": null, + "certificates_revision": null, + "version": "1.0.8.179" + } + "#; + let resp: HostGAPluginResponse = + serde_json::from_str(response).expect("Deserialize HostGAPluginResponse failed"); + assert!(resp.body.is_some()); + let certs = resp.body.unwrap(); + assert_eq!( + certs.activity_id.unwrap(), + "c92847b6-1c5d-402e-b919-c0157f4bda74" + ); + assert_eq!( + certs.correlation_id.unwrap(), + "80e22e3b-3f9a-424e-b300-6cda2dd7e718" + ); + assert!(certs.certificates.is_some()); + let cert_list = certs.certificates.unwrap(); + assert_eq!(cert_list.len(), 1); + let cert = &cert_list[0]; + assert_eq!(cert.name.as_ref().unwrap(), "TenantEncryptionCert"); + assert_eq!( + cert.certificate_in_base64.as_ref().unwrap(), + "certificateInBase64_test" + ); + assert_eq!(cert.thumbprint.as_ref().unwrap(), "thumbprint_test"); + } + + #[test] + fn get_hostgaplugin_vmsettings_response_test() { + let response = r#" + { + "body": { + "hostGAPluginVersion": "1.0.8.179", + "activityId": "38d627ab-5656-4762-bad3-03c77c22faee", + "correlationId": "ad342255-60e3-edfc-8b8d-298e5dd6909b", + "inSvdSeqNo": 1, + "certificatesRevision": 0, + "extensionsLastModifiedTickCount": 638931417044754873, + "extensionGoalStatesSource": "FastTrack", + "statusUploadBlob": { + "statusBlobType": "PageBlob", + "value": "string" + }, + "gaFamilies": [ + { + "name": "Win7", + "version": "2.7.41491.1176", + "isVersionFromRSM": false, + "isVMEnabledForRSMUpgrades": true, + "uris": [ + "uri" + ] + }, + { + "name": "Win8", + "version": "2.7.41491.1176", + "isVersionFromRSM": false, + "isVMEnabledForRSMUpgrades": true, + "uris": [ + "uri" + ] + } + ], + "extensionGoalStates": [ + { + "name": "extension.test", + "version": "1.0.1", + "location": "localtion", + "failoverLocation": "location", + "additionalLocations": [ + "location" + ], + "state": "enabled", + "autoUpgrade": true, + "runAsStartupTask": false, + "isJson": true, + "useExactVersion": true, + "settingsSeqNo": 0, + "isMultiConfig": false, + "settings": [ + { + "protectedSettingsCertThumbprint": null, + "protectedSettings": null, + "publicSettings": "{}" + } + ] + } + ] + }, + "etag": "5048704324908356042", + "certificates_revision": 1, + "version": "1.0.8.179" + } + "#; + let resp: HostGAPluginResponse = + serde_json::from_str(response).expect("Deserialize HostGAPluginResponse failed"); + assert!(resp.body.is_some()); + assert_eq!(resp.etag.unwrap(), "5048704324908356042"); + assert_eq!(resp.certificates_revision.unwrap(), 1); + + let vmsettings = resp.body.unwrap(); + assert_eq!( + vmsettings.activity_id.unwrap(), + "38d627ab-5656-4762-bad3-03c77c22faee" + ); + assert_eq!( + vmsettings.extensions_last_modified_tick_count.unwrap(), + 638931417044754873 + ); + assert_eq!(vmsettings.extension_goal_states.as_ref().unwrap().len(), 1); + assert_eq!(vmsettings.ga_families.as_ref().unwrap().len(), 2); + + let extension = &vmsettings.extension_goal_states.as_ref().unwrap()[0]; + assert_eq!(extension.name.as_ref().unwrap(), "extension.test"); + assert_eq!(extension.version.as_ref().unwrap(), "1.0.1"); + } +} From 21b69c8689690ee22c6ca9f0a78431a2b5693e92 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 15:36:06 -0700 Subject: [PATCH 16/35] unit tess --- .../data_model/hostga_plugin_model.rs | 132 +++++++++++++++ .../data_model/wire_server_model.rs | 152 ++++++++++++++++++ 2 files changed, 284 insertions(+) diff --git a/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs b/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs index 81c680ef..59d01e2c 100644 --- a/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs +++ b/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs @@ -117,3 +117,135 @@ pub struct Certificate { #[serde(rename = "certificateBlobFormatType")] pub certificate_blob_format_type: Option, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn certificates_deserialization_test() { + let certificates_json = r#" + { + "activityId": "c92847b6-1c5d-402e-b919-c0157f4bda74", + "correlationId": "80e22e3b-3f9a-424e-b300-6cda2dd7e718", + "certificates": [ + { + "name": "TenantEncryptionCert", + "storeName": "My", + "configurationLevel": "System", + "certificateInBase64": "certificateInBase64_test", + "includePrivateKey": false, + "thumbprint": "thumbprint_test", + "certificateBlobFormatType": "PfxInClear" + } + ] + } + "#; + let certificates: Certificates = serde_json::from_str(certificates_json).unwrap(); + assert_eq!( + certificates.activity_id.unwrap(), + "c92847b6-1c5d-402e-b919-c0157f4bda74" + ); + assert_eq!( + certificates.correlation_id.unwrap(), + "80e22e3b-3f9a-424e-b300-6cda2dd7e718" + ); + assert_eq!(certificates.certificates.as_ref().unwrap().len(), 1); + assert_eq!( + certificates.certificates.as_ref().unwrap()[0] + .certificate_in_base64 + .as_ref() + .unwrap(), + "certificateInBase64_test" + ); + assert_eq!( + certificates.certificates.as_ref().unwrap()[0] + .thumbprint + .as_ref() + .unwrap(), + "thumbprint_test" + ); + } + + #[test] + fn vmsettings_deserialization_test() { + let vmsettings_json = r#" + { + "hostGAPluginVersion": "1.0.8.179", + "activityId": "38d627ab-5656-4762-bad3-03c77c22faee", + "correlationId": "ad342255-60e3-edfc-8b8d-298e5dd6909b", + "inSvdSeqNo": 1, + "certificatesRevision": 0, + "extensionsLastModifiedTickCount": 638931417044754873, + "extensionGoalStatesSource": "FastTrack", + "statusUploadBlob": { + "statusBlobType": "PageBlob", + "value": "string" + }, + "gaFamilies": [ + { + "name": "Win7", + "version": "2.7.41491.1176", + "isVersionFromRSM": false, + "isVMEnabledForRSMUpgrades": true, + "uris": [ + "uri" + ] + }, + { + "name": "Win8", + "version": "2.7.41491.1176", + "isVersionFromRSM": false, + "isVMEnabledForRSMUpgrades": true, + "uris": [ + "uri" + ] + } + ], + "extensionGoalStates": [ + { + "name": "test", + "version": "1.0.1", + "location": "localtion", + "failoverLocation": "location", + "additionalLocations": [ + "location" + ], + "state": "enabled", + "autoUpgrade": true, + "runAsStartupTask": false, + "isJson": true, + "useExactVersion": true, + "settingsSeqNo": 0, + "isMultiConfig": false, + "settings": [ + { + "protectedSettingsCertThumbprint": null, + "protectedSettings": null, + "publicSettings": "{}" + } + ] + } + ] + }"#; + + let vmsettings: VMSettings = serde_json::from_str(vmsettings_json).unwrap(); + assert_eq!(vmsettings.host_ga_plugin_version.unwrap(), "1.0.8.179"); + assert_eq!( + vmsettings.activity_id.unwrap(), + "38d627ab-5656-4762-bad3-03c77c22faee" + ); + assert_eq!( + vmsettings.correlation_id.unwrap(), + "ad342255-60e3-edfc-8b8d-298e5dd6909b" + ); + assert_eq!(vmsettings.in_svd_seq_no.unwrap(), 1); + assert_eq!(vmsettings.certificates_revision.unwrap(), 0); + assert_eq!( + vmsettings.extensions_last_modified_tick_count.unwrap(), + 638931417044754873 + ); + assert_eq!(vmsettings.ga_families.as_ref().unwrap().len(), 2); + assert_eq!(vmsettings.extension_goal_states.as_ref().unwrap().len(), 1); + } +} diff --git a/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs b/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs index 04d546ce..06fca9f4 100644 --- a/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs +++ b/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs @@ -361,3 +361,155 @@ pub struct HostingEnvironmentConfig { #[serde(rename = "ApplicationSettings")] pub application_settings: Option, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn versions_deserialization_test() { + let xml_data = r#" + + + 2015-04-05 + + + 2015-04-05 + 2012-11-30 + 2012-09-15 + 2012-05-15 + 2011-12-31 + 2011-10-15 + 2011-08-31 + 2011-04-07 + 2010-12-15 + 2010-28-10 + + + "#; + + let versions: Versions = quick_xml::de::from_str(xml_data).unwrap(); + assert_eq!(versions.preferred.version, "2015-04-05"); + assert_eq!(versions.supported.versions.len(), 10); + } + + #[test] + fn goal_state_deserialization_test() { + let xml_data = r#" + + 2015-04-05 + 1 + + Started + 300000 + + 16001 + + FALSE + + + c9514be2-ff0a-4dee-a059-45a0452268e7 + + + 896a1f5d-459b-4e58-a337-d113f9e97d25._siyinVM + Started + + HostingEnvironmentConfig_uri + SharedConfig_uri + ExtensionsConfig_uri + FullConfig_uri + Certificates_uri + ConfigName.xml + + + + + + "#; + let goal_state: GoalState = quick_xml::de::from_str(xml_data).unwrap(); + assert_eq!(goal_state.version.unwrap(), "2015-04-05"); + assert_eq!(goal_state.incarnation.unwrap(), 1); + + let role_instances = goal_state + .container + .unwrap() + .role_instance_list + .unwrap() + .role_instance + .unwrap(); + assert_eq!(role_instances.len(), 1); + let role_instance = &role_instances[0]; + assert_eq!( + role_instance.instance_id.as_ref().unwrap(), + "896a1f5d-459b-4e58-a337-d113f9e97d25._siyinVM" + ); + let configuration = role_instance.configuration.as_ref().unwrap(); + assert_eq!( + configuration.full_config.as_ref().unwrap(), + "FullConfig_uri" + ); + assert_eq!( + configuration.certificates.as_ref().unwrap(), + "Certificates_uri" + ); + } + + #[test] + fn full_config_deserialization_test() { + let xml_data = r#" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "#; + let rd_config: RDConfig = quick_xml::de::from_str(xml_data).unwrap(); + assert_eq!(rd_config.version.unwrap(), "1.0.0.0"); + assert_eq!( + rd_config + .stored_certificates + .as_ref() + .unwrap() + .stored_certificate + .len(), + 1 + ); + + let certificate = &rd_config + .stored_certificates + .as_ref() + .unwrap() + .stored_certificate[0]; + assert_eq!(certificate.name.as_ref().unwrap(), "TenantEncryptionCert"); + assert_eq!( + certificate.certificate_id.as_ref().unwrap(), + "sha1:45750FFF384A47DEC65C9C7BB829B27E0562726F" + ); + } +} From 8ba4a2217d04342ec45452eb1b51d11f76692b18 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 15:46:18 -0700 Subject: [PATCH 17/35] fix spell --- .github/actions/spelling/expect.txt | 3 ++- .../host_clients/data_model/hostga_plugin_model.rs | 14 +++++++------- .../src/host_clients/hostga_plugin_client.rs | 12 ++++++------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 2cde7b3b..0b73b241 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -381,4 +381,5 @@ PSTR psz rsm SELFSIGN -vmsettings \ No newline at end of file +vmsettings +hostgaplugin \ No newline at end of file diff --git a/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs b/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs index 59d01e2c..7cb38cd9 100644 --- a/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs +++ b/proxy_agent_shared/src/host_clients/data_model/hostga_plugin_model.rs @@ -126,7 +126,7 @@ mod tests { fn certificates_deserialization_test() { let certificates_json = r#" { - "activityId": "c92847b6-1c5d-402e-b919-c0157f4bda74", + "activityId": "11111111-1111111-11111111111-111111", "correlationId": "80e22e3b-3f9a-424e-b300-6cda2dd7e718", "certificates": [ { @@ -144,7 +144,7 @@ mod tests { let certificates: Certificates = serde_json::from_str(certificates_json).unwrap(); assert_eq!( certificates.activity_id.unwrap(), - "c92847b6-1c5d-402e-b919-c0157f4bda74" + "11111111-1111111-11111111111-111111" ); assert_eq!( certificates.correlation_id.unwrap(), @@ -172,8 +172,8 @@ mod tests { let vmsettings_json = r#" { "hostGAPluginVersion": "1.0.8.179", - "activityId": "38d627ab-5656-4762-bad3-03c77c22faee", - "correlationId": "ad342255-60e3-edfc-8b8d-298e5dd6909b", + "activityId": "1111-11111111-1111-11-1111", + "correlationId": "000000-00000000-000000-0000", "inSvdSeqNo": 1, "certificatesRevision": 0, "extensionsLastModifiedTickCount": 638931417044754873, @@ -206,7 +206,7 @@ mod tests { { "name": "test", "version": "1.0.1", - "location": "localtion", + "location": "location", "failoverLocation": "location", "additionalLocations": [ "location" @@ -233,11 +233,11 @@ mod tests { assert_eq!(vmsettings.host_ga_plugin_version.unwrap(), "1.0.8.179"); assert_eq!( vmsettings.activity_id.unwrap(), - "38d627ab-5656-4762-bad3-03c77c22faee" + "1111-11111111-1111-11-1111" ); assert_eq!( vmsettings.correlation_id.unwrap(), - "ad342255-60e3-edfc-8b8d-298e5dd6909b" + "000000-00000000-000000-0000" ); assert_eq!(vmsettings.in_svd_seq_no.unwrap(), 1); assert_eq!(vmsettings.certificates_revision.unwrap(), 0); diff --git a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs index e57b795c..ceaf2a6e 100644 --- a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs @@ -197,7 +197,7 @@ mod tests { let response = r#" { "body": { - "activityId": "c92847b6-1c5d-402e-b919-c0157f4bda74", + "activityId": "11111111-1111111-11111111111-111111", "correlationId": "80e22e3b-3f9a-424e-b300-6cda2dd7e718", "certificates": [ { @@ -222,7 +222,7 @@ mod tests { let certs = resp.body.unwrap(); assert_eq!( certs.activity_id.unwrap(), - "c92847b6-1c5d-402e-b919-c0157f4bda74" + "11111111-1111111-11111111111-111111" ); assert_eq!( certs.correlation_id.unwrap(), @@ -246,8 +246,8 @@ mod tests { { "body": { "hostGAPluginVersion": "1.0.8.179", - "activityId": "38d627ab-5656-4762-bad3-03c77c22faee", - "correlationId": "ad342255-60e3-edfc-8b8d-298e5dd6909b", + "activityId": "1111-11111111-1111-11-1111", + "correlationId": "000000-00000000-000000-0000", "inSvdSeqNo": 1, "certificatesRevision": 0, "extensionsLastModifiedTickCount": 638931417044754873, @@ -280,7 +280,7 @@ mod tests { { "name": "extension.test", "version": "1.0.1", - "location": "localtion", + "location": "location", "failoverLocation": "location", "additionalLocations": [ "location" @@ -316,7 +316,7 @@ mod tests { let vmsettings = resp.body.unwrap(); assert_eq!( vmsettings.activity_id.unwrap(), - "38d627ab-5656-4762-bad3-03c77c22faee" + "1111-11111111-1111-11-1111" ); assert_eq!( vmsettings.extensions_last_modified_tick_count.unwrap(), From dffbc7e08f0cbf9eb1e88b90cb0d516ca7650a17 Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 16:09:59 -0700 Subject: [PATCH 18/35] fix spell --- proxy_agent_shared/src/common/config.rs | 2 +- proxy_agent_shared/src/common/hyper_client.rs | 5 +++-- .../data_model/wire_server_model.rs | 18 ++++++------------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/proxy_agent_shared/src/common/config.rs b/proxy_agent_shared/src/common/config.rs index 9fa22ab5..d81aaaea 100644 --- a/proxy_agent_shared/src/common/config.rs +++ b/proxy_agent_shared/src/common/config.rs @@ -6,7 +6,7 @@ //! //! Example //! ```rust -//! use proxy_agent::config; +//! use crate::common::config; //! //! // Get the logs directory //! let logs_dir = config::get_logs_dir(); diff --git a/proxy_agent_shared/src/common/hyper_client.rs b/proxy_agent_shared/src/common/hyper_client.rs index a9b2c450..ec9b5994 100644 --- a/proxy_agent_shared/src/common/hyper_client.rs +++ b/proxy_agent_shared/src/common/hyper_client.rs @@ -5,11 +5,12 @@ //! //! Example //! ```rust -//! use proxy_agent::hyper_client; -//! use host_clients::goal_state::GoalState; +//! use crate::common::hyper_client; +//! use crate::host_clients::data_model::wire_server_model::GoalState; //! use std::collections::HashMap; //! use hyper::Uri; //! use std::str::FromStr; +//! use http::Method; //! //! let mut headers = HashMap::new(); //! headers.insert("x-ms-version".to_string(), "2012-11-30".to_string()); diff --git a/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs b/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs index 06fca9f4..595addb3 100644 --- a/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs +++ b/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs @@ -411,7 +411,7 @@ mod tests { c9514be2-ff0a-4dee-a059-45a0452268e7 - 896a1f5d-459b-4e58-a337-d113f9e97d25._siyinVM + 896a1f5d-459b-4e58-a337-d113f9e97d25.instance Started HostingEnvironmentConfig_uri @@ -441,7 +441,7 @@ mod tests { let role_instance = &role_instances[0]; assert_eq!( role_instance.instance_id.as_ref().unwrap(), - "896a1f5d-459b-4e58-a337-d113f9e97d25._siyinVM" + "896a1f5d-459b-4e58-a337-d113f9e97d25.instance" ); let configuration = role_instance.configuration.as_ref().unwrap(); assert_eq!( @@ -461,11 +461,11 @@ mod tests { - + - + - + @@ -476,17 +476,11 @@ mod tests { - + - - - - - - "#; let rd_config: RDConfig = quick_xml::de::from_str(xml_data).unwrap(); From 7f5e433a5c46305df20173cbcee31d07fa25665a Mon Sep 17 00:00:00 2001 From: yinbing Date: Fri, 19 Sep 2025 16:12:41 -0700 Subject: [PATCH 19/35] spell fixes --- .../src/host_clients/data_model/wire_server_model.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs b/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs index 595addb3..e798e943 100644 --- a/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs +++ b/proxy_agent_shared/src/host_clients/data_model/wire_server_model.rs @@ -476,7 +476,7 @@ mod tests { - + From b91c2b40fcd9e236725677b490b4f5a3c0be2dc0 Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 22 Sep 2025 13:35:07 -0700 Subject: [PATCH 20/35] more unit test --- .../src/certificate/certificate_helper.rs | 25 ++++++++++++ .../src/common/formatted_error.rs | 40 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index c97d1c12..371b4003 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -56,3 +56,28 @@ pub fn decrypt_from_base64( todo!() } } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn certificate_details_wrapper_test() { + #[cfg(windows)] + { + let subject_name = "TestSubject"; + let cert_details_result = generate_self_signed_certificate(subject_name); + assert!(cert_details_result.is_ok()); + let cert_details = cert_details_result.unwrap(); + let public_cert_der = cert_details.get_public_cert_der(); + assert!(!public_cert_der.is_empty()); + } + #[cfg(not(windows))] + { + // On non-Windows platforms, the function is not implemented. + // This test will simply ensure that the function is called without panic. + let subject_name = "TestSubject"; + let cert_details_result = generate_self_signed_certificate(subject_name); + assert!(cert_details_result.is_err()); + } + } +} diff --git a/proxy_agent_shared/src/common/formatted_error.rs b/proxy_agent_shared/src/common/formatted_error.rs index 3053fe92..b2d808ce 100644 --- a/proxy_agent_shared/src/common/formatted_error.rs +++ b/proxy_agent_shared/src/common/formatted_error.rs @@ -52,3 +52,43 @@ impl From for FormattedError { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn formatted_error_display_test() { + let error = FormattedError { + message: "An error occurred".to_string(), + code: 404, + }; + assert_eq!( + format!("{}", error), + "code: 404, message: An error occurred" + ); + } + + #[test] + fn formatted_error_from_test() { + let decode_error = DecodeError::InvalidLength(0); + let formatted_error: FormattedError = decode_error.into(); + assert_eq!(formatted_error.message, "Decode Error: InvalidLength(0)"); + + let utf8_bytes = vec![0, 159, 146, 150]; + let utf8_error = String::from_utf8(utf8_bytes).unwrap_err(); + let formatted_error: FormattedError = utf8_error.into(); + assert!(formatted_error.message.starts_with("Utf-8 Convert Error:")); + + let json_error = serde_json::from_str::("invalid json").unwrap_err(); + let formatted_error: FormattedError = json_error.into(); + assert!(formatted_error.message.starts_with("Json Error:")); + + #[cfg(windows)] + { + let windows_error = windows::core::Error::from_win32(); + let formatted_error: FormattedError = windows_error.into(); + assert!(formatted_error.message.starts_with("Windows API Error:")); + } + } +} From 9c13ab12a514a4ec4e34001f738f3ad0829de7b7 Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 22 Sep 2025 14:34:40 -0700 Subject: [PATCH 21/35] client unit tests --- .../src/host_clients/hostga_plugin_client.rs | 49 ++++++++++++++++--- .../src/host_clients/wire_server_client.rs | 32 +++++++++++- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs index ceaf2a6e..795b797c 100644 --- a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs @@ -83,12 +83,7 @@ impl HostGAPluginClient { let cert_der = cert.get_public_cert_der(); let cert_base64 = base64::engine::general_purpose::STANDARD.encode(cert_der); - let mut headers = HashMap::new(); - headers.insert(Self::TRANSPORT_CERTIFICATE_HEADER.to_string(), cert_base64); - headers.insert( - Self::TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER.to_string(), - Self::HOSTGAP_CIPHER.to_string(), - ); + let headers = self.certificate_request_headers(&cert_base64); let raw_certs_resp = self .get::( @@ -186,12 +181,54 @@ impl HostGAPluginClient { } .into()) } + + fn certificate_request_headers(&self, cert: &str) -> HashMap { + let mut headers = HashMap::new(); + headers.insert( + Self::TRANSPORT_CERTIFICATE_HEADER.to_string(), + cert.to_string(), + ); + headers.insert( + Self::TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER.to_string(), + Self::HOSTGAP_CIPHER.to_string(), + ); + headers + } } #[cfg(test)] mod tests { use super::*; + #[test] + fn hostgaplugin_client_creation_test() { + let client = HostGAPluginClient::new("http://localhost:8080", |level, message| { + println!("{:?}: {}", level, message); + }); + assert_eq!(client.base_url, "http://localhost:8080"); + } + + #[test] + fn certificate_request_headers_test() { + let client = HostGAPluginClient::new("http://localhost:8080", |level, message| { + println!("{:?}: {}", level, message); + }); + let cert = "test_cert"; + let headers = client.certificate_request_headers(cert); + assert_eq!( + headers + .get(HostGAPluginClient::TRANSPORT_CERTIFICATE_HEADER) + .unwrap(), + cert + ); + assert_eq!( + headers + .get(HostGAPluginClient::TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER) + .unwrap(), + HostGAPluginClient::HOSTGAP_CIPHER + ); + } + #[test] fn get_hostgaplugin_certificates_response_test() { let response = r#" diff --git a/proxy_agent_shared/src/host_clients/wire_server_client.rs b/proxy_agent_shared/src/host_clients/wire_server_client.rs index 68472715..1539fede 100644 --- a/proxy_agent_shared/src/host_clients/wire_server_client.rs +++ b/proxy_agent_shared/src/host_clients/wire_server_client.rs @@ -55,8 +55,7 @@ impl WireServerClient { .parse::() .map_err(|e| Error::ParseUrl(url.to_string(), e.to_string()))?; - let mut headers = HashMap::new(); - headers.insert(Self::X_MS_VERSION_HEADER.to_string(), self.version.clone()); + let headers = self.common_headers(); let res = hyper_client::get(&url, &headers, None, None, move |message| { logger(LoggerLevel::Warn, message) @@ -65,4 +64,33 @@ impl WireServerClient { .unwrap(); Ok(res) } + + fn common_headers(&self) -> HashMap { + let mut headers = HashMap::new(); + headers.insert(Self::X_MS_VERSION_HEADER.to_string(), self.version.clone()); + headers + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn wire_server_client_creation_test() { + let client = WireServerClient::new("http://localhost:8080", |level, message| { + println!("{:?}: {}", level, message); + }); + assert_eq!(client.base_url, "http://localhost:8080"); + assert_eq!(client.version, "2015-04-05"); + } + + #[test] + fn wire_server_client_common_headers_test() { + let client = WireServerClient::new("http://localhost:8080", |level, message| { + println!("{:?}: {}", level, message); + }); + let headers = client.common_headers(); + assert_eq!(headers.get("x-ms-version").unwrap(), "2015-04-05"); + } } From c925a7db6ecb401bde7ded0db6fe368c7f8b6ba0 Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 22 Sep 2025 16:13:13 -0700 Subject: [PATCH 22/35] aggregate status test --- .../src/proxy_agent_aggregate_status.rs | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/proxy_agent_shared/src/proxy_agent_aggregate_status.rs b/proxy_agent_shared/src/proxy_agent_aggregate_status.rs index 5a3f2e6a..8c2feb0b 100644 --- a/proxy_agent_shared/src/proxy_agent_aggregate_status.rs +++ b/proxy_agent_shared/src/proxy_agent_aggregate_status.rs @@ -88,3 +88,121 @@ pub struct GuestProxyAgentAggregateStatus { pub proxyConnectionSummary: Vec, pub failedAuthenticateSummary: Vec, } + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn get_proxy_agent_aggregate_status_folder_test() { + let path = misc_helpers::resolve_env_variables(PROXY_AGENT_AGGREGATE_STATUS_FOLDER); + assert!(path.is_ok()); + let path_buf = get_proxy_agent_aggregate_status_folder(); + assert_eq!(path_buf.to_string_lossy().into_owned(), path.unwrap()); + } + + #[test] + fn guest_proxy_agent_aggregate_status_deserialize_test() { + let json_str = r#" + { + "timestamp": "2025-10-10T12:00:00Z", + "proxyAgentStatus": { + "version": "1.0.0", + "status": "SUCCESS", + "monitorStatus": { + "status": "RUNNING", + "message": "Monitor is running", + "states": { + "monitorState": "TestState" + } + }, + "keyLatchStatus": { + "status": "STOPPED", + "message": "Key latch is stopped" + }, + "ebpfProgramStatus": { + "status": "UNKNOWN", + "message": "eBPF program status unknown" + }, + "proxyListenerStatus": { + "status": "RUNNING", + "message": "Proxy listener is active" + }, + "telemetryLoggerStatus": { + "status": "RUNNING", + "message": "Telemetry logger is operational" + }, + "proxyConnectionsCount": 42 + }, + "proxyConnectionSummary": [ + { + "userName": "user1", + "ip": "192.168.1.1", + "port": 8080, + "processCmdLine": "cmd.exe /c whoami", + "responseStatus": "Success", + "count": 1, + "userGroups": ["Administrators"], + "processFullPath": "C:\\Windows\\System32\\cmd.exe" + } + ], + "failedAuthenticateSummary": [] + }"#; + let status: Result = + serde_json::from_str(json_str); + assert!(status.is_ok()); + let status = status.unwrap(); + assert_eq!(status.timestamp, "2025-10-10T12:00:00Z"); + assert_eq!(status.proxyAgentStatus.version, "1.0.0"); + assert_eq!(status.proxyAgentStatus.status, OverallState::SUCCESS); + assert_eq!( + status.proxyAgentStatus.monitorStatus.status, + ModuleState::RUNNING + ); + assert_eq!( + status.proxyAgentStatus.monitorStatus.message, + "Monitor is running" + ); + assert_eq!( + status + .proxyAgentStatus + .monitorStatus + .states + .unwrap() + .get("monitorState") + .unwrap(), + "TestState" + ); + assert_eq!( + status.proxyAgentStatus.keyLatchStatus.status, + ModuleState::STOPPED + ); + assert_eq!( + status.proxyAgentStatus.keyLatchStatus.message, + "Key latch is stopped" + ); + assert_eq!( + status.proxyAgentStatus.ebpfProgramStatus.status, + ModuleState::UNKNOWN + ); + assert_eq!( + status.proxyAgentStatus.ebpfProgramStatus.message, + "eBPF program status unknown" + ); + assert_eq!( + status.proxyAgentStatus.proxyListenerStatus.status, + ModuleState::RUNNING + ); + assert_eq!( + status.proxyAgentStatus.proxyListenerStatus.message, + "Proxy listener is active" + ); + assert_eq!( + status.proxyAgentStatus.telemetryLoggerStatus.status, + ModuleState::RUNNING + ); + assert_eq!( + status.proxyAgentStatus.telemetryLoggerStatus.message, + "Telemetry logger is operational" + ); + } +} From b54d7c9b9500bb71fe9d7a52d41d8c337d5152ae Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 22 Sep 2025 16:50:49 -0700 Subject: [PATCH 23/35] more ut --- .../src/host_clients/hostga_plugin_client.rs | 33 ++++++++++++++++--- .../src/host_clients/wire_server_client.rs | 33 +++++++++++++++++-- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs index 795b797c..a2b83ba5 100644 --- a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs @@ -58,10 +58,9 @@ impl HostGAPluginClient { LoggerLevel::Info, format!("Requesting VMSettings with etag: {etag:?}"), ); - let mut headers = HashMap::new(); - if let Some(etag) = etag { - headers.insert(Self::ETAG_HEADER.to_string(), etag); - } + + let headers = self.vmsettings_request_headers(etag); + self.get::( &format!("{}/{}", self.base_url, Self::VMSETTINGS_URL), &headers, @@ -194,6 +193,14 @@ impl HostGAPluginClient { ); headers } + + fn vmsettings_request_headers(&self, etag: Option) -> HashMap { + let mut headers = HashMap::new(); + if let Some(etag) = etag { + headers.insert(Self::ETAG_HEADER.to_string(), etag); + } + headers + } } #[cfg(test)] @@ -229,6 +236,24 @@ mod tests { ); } + #[test] + fn vmsettings_request_headers_test() { + let client = HostGAPluginClient::new("http://localhost:8080", |level, message| { + println!("{:?}: {}", level, message); + }); + let etag = Some("test_etag".to_string()); + let headers = client.vmsettings_request_headers(etag.clone()); + assert_eq!( + headers.get(HostGAPluginClient::ETAG_HEADER).unwrap(), + etag.as_ref().unwrap() + ); + + let headers_no_etag = client.vmsettings_request_headers(None); + assert!(headers_no_etag + .get(HostGAPluginClient::ETAG_HEADER) + .is_none()); + } + #[test] fn get_hostgaplugin_certificates_response_test() { let response = r#" diff --git a/proxy_agent_shared/src/host_clients/wire_server_client.rs b/proxy_agent_shared/src/host_clients/wire_server_client.rs index 1539fede..b2ad1f2f 100644 --- a/proxy_agent_shared/src/host_clients/wire_server_client.rs +++ b/proxy_agent_shared/src/host_clients/wire_server_client.rs @@ -20,10 +20,12 @@ impl WireServerClient { const VERSIONS_URL: &'static str = "?comp=Versions"; const GOAL_STATE_URL: &'static str = "machine?comp=goalstate"; + const DEFAULT_WIRE_VERSION: &'static str = "2012-11-30"; + pub fn new(base_url: &str, logger: fn(LoggerLevel, String) -> ()) -> WireServerClient { WireServerClient { base_url: base_url.to_string(), - version: "2015-04-05".to_string(), + version: Self::DEFAULT_WIRE_VERSION.to_string(), logger, } } @@ -38,6 +40,12 @@ impl WireServerClient { self.get::(Self::GOAL_STATE_URL).await } + pub async fn refresh_wire_server_version(&mut self) -> Result<()> { + let versions = self.get_versions().await?; + self.update_version(versions); + Ok(()) + } + pub async fn get(&self, sub_url: &str) -> Result where T: for<'a> Deserialize<'a>, @@ -70,6 +78,10 @@ impl WireServerClient { headers.insert(Self::X_MS_VERSION_HEADER.to_string(), self.version.clone()); headers } + + fn update_version(&mut self, versions: Versions) { + self.version = versions.preferred.version; + } } #[cfg(test)] @@ -82,7 +94,7 @@ mod tests { println!("{:?}: {}", level, message); }); assert_eq!(client.base_url, "http://localhost:8080"); - assert_eq!(client.version, "2015-04-05"); + assert_eq!(client.version, WireServerClient::DEFAULT_WIRE_VERSION); } #[test] @@ -93,4 +105,21 @@ mod tests { let headers = client.common_headers(); assert_eq!(headers.get("x-ms-version").unwrap(), "2015-04-05"); } + + #[test] + fn wire_server_client_update_version_test() { + let mut client = WireServerClient::new("http://localhost:8080", |level, message| { + println!("{:?}: {}", level, message); + }); + let versions = Versions { + preferred: crate::host_clients::data_model::wire_server_model::Preferred { + version: "2021-01-01".to_string(), + }, + supported: crate::host_clients::data_model::wire_server_model::Supported { + versions: vec![], + }, + }; + client.update_version(versions); + assert_eq!(client.version, "2021-01-01"); + } } From d1b2312f044ee9be17509911e9168f05d956e17c Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 22 Sep 2025 19:31:09 -0700 Subject: [PATCH 24/35] fix ut --- proxy_agent_shared/src/host_clients/wire_server_client.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proxy_agent_shared/src/host_clients/wire_server_client.rs b/proxy_agent_shared/src/host_clients/wire_server_client.rs index b2ad1f2f..8747920d 100644 --- a/proxy_agent_shared/src/host_clients/wire_server_client.rs +++ b/proxy_agent_shared/src/host_clients/wire_server_client.rs @@ -103,7 +103,10 @@ mod tests { println!("{:?}: {}", level, message); }); let headers = client.common_headers(); - assert_eq!(headers.get("x-ms-version").unwrap(), "2015-04-05"); + assert_eq!( + headers.get("x-ms-version").unwrap(), + WireServerClient::DEFAULT_WIRE_VERSION + ); } #[test] From d5e68d52058f1157e92932b3caf4110016ff6102 Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 29 Sep 2025 13:07:47 -0700 Subject: [PATCH 25/35] fix for ut in linux --- proxy_agent_shared/src/certificate/certificate_helper.rs | 7 +++---- proxy_agent_shared/src/common/formatted_error.rs | 9 +++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index 371b4003..d60913f2 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -20,7 +20,7 @@ impl CertificateDetailsWrapper { } #[cfg(not(windows))] { - todo!() + Err("Linux version is not implemented.".to_string().into()) } } } @@ -36,7 +36,7 @@ pub fn generate_self_signed_certificate( } #[cfg(not(windows))] { - todo!() + Err("Linux version is not implemented.".to_string().into()) } } @@ -50,10 +50,9 @@ pub fn decrypt_from_base64( decrypt_from_base64_windows(_base64_input, _cert_details) } - #[cfg(not(windows))] { - todo!() + Err("Linux version is not implemented.".to_string().into()) } } diff --git a/proxy_agent_shared/src/common/formatted_error.rs b/proxy_agent_shared/src/common/formatted_error.rs index b2d808ce..b1428e7f 100644 --- a/proxy_agent_shared/src/common/formatted_error.rs +++ b/proxy_agent_shared/src/common/formatted_error.rs @@ -43,6 +43,15 @@ impl From for FormattedError { } } +impl From for FormattedError { + fn from(value: String) -> Self { + FormattedError { + message: format!("GeneralError: {}", value), + code: -1, + } + } +} + #[cfg(windows)] impl From for FormattedError { fn from(value: windows::core::Error) -> Self { From 2aac05b7ec428313db5c4eec6f94b253ad2338c1 Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 29 Sep 2025 13:15:06 -0700 Subject: [PATCH 26/35] more unit test --- proxy_agent_shared/src/common/cli.rs | 68 ++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/proxy_agent_shared/src/common/cli.rs b/proxy_agent_shared/src/common/cli.rs index ead9eb74..aba9239f 100644 --- a/proxy_agent_shared/src/common/cli.rs +++ b/proxy_agent_shared/src/common/cli.rs @@ -49,3 +49,71 @@ impl Cli { } pub static CLI: Lazy = Lazy::new(Cli::parse); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_version_flag() { + let cli = Cli::try_parse_from(["command", "--version"]).unwrap(); + assert!(cli.version); + assert!(!cli.status); + assert!(cli.wait.is_none()); + assert!(cli.command.is_none()); + } + + #[test] + fn test_parse_status_with_wait() { + let cli = Cli::try_parse_from(["command", "--status", "--wait", "10"]).unwrap(); + assert!(cli.status); + assert_eq!(cli.wait, Some(10)); + assert!(!cli.version); + assert!(cli.command.is_none()); + } + + #[test] + fn test_parse_console_command() { + let cli = Cli::try_parse_from(["command", "console"]).unwrap(); + assert!(cli.is_console_mode()); + match cli.command { + Some(Commands::Console) => {} + _ => panic!("Expected Commands::Console"), + } + } + + #[test] + fn test_parse_status_without_wait() { + let cli = Cli::try_parse_from(["command", "--status"]).unwrap(); + assert!(cli.status); + assert!(cli.wait.is_none()); + assert!(!cli.version); + } + + #[test] + fn test_conflicting_wait_without_status_fails() { + let result = Cli::try_parse_from(["command", "--wait", "5"]); + assert!(result.is_err(), "wait without --status should fail"); + } + + #[test] + fn test_no_args_defaults() { + let cli = Cli::try_parse_from(["command"]).unwrap(); + assert!(!cli.status); + assert!(!cli.version); + assert!(cli.wait.is_none()); + assert!(cli.command.is_none()); + assert!(!cli.is_console_mode()); + } + + #[cfg(test)] + #[test] + fn test_test_only_flags() { + let cli = + Cli::try_parse_from(["command", "--status", "--test-threads", "4", "--nocapture"]) + .unwrap(); + assert!(cli.status); + assert_eq!(cli.test_threads, Some(4)); + assert!(cli.nocapture); + } +} From 2a9d332cf56d75c33c54af4e22786188cac7f13b Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 29 Sep 2025 13:19:35 -0700 Subject: [PATCH 27/35] fixes --- proxy_agent_shared/src/certificate/certificate_helper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index d60913f2..98a934bb 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -20,7 +20,7 @@ impl CertificateDetailsWrapper { } #[cfg(not(windows))] { - Err("Linux version is not implemented.".to_string().into()) + todo!() } } } From 0a0db7810ffe84da6d80ffaa7e56a18f00c435fe Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 29 Sep 2025 13:34:38 -0700 Subject: [PATCH 28/35] clippy fixes --- proxy_agent_shared/src/common/formatted_error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy_agent_shared/src/common/formatted_error.rs b/proxy_agent_shared/src/common/formatted_error.rs index b1428e7f..7fe682fb 100644 --- a/proxy_agent_shared/src/common/formatted_error.rs +++ b/proxy_agent_shared/src/common/formatted_error.rs @@ -46,7 +46,7 @@ impl From for FormattedError { impl From for FormattedError { fn from(value: String) -> Self { FormattedError { - message: format!("GeneralError: {}", value), + message: format!("GeneralError: {value}"), code: -1, } } From 45b6614b98e18239f23dd7dcdd548090aaae5901 Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 29 Sep 2025 14:50:05 -0700 Subject: [PATCH 29/35] more unit test --- proxy_agent_shared/src/common/hyper_client.rs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/proxy_agent_shared/src/common/hyper_client.rs b/proxy_agent_shared/src/common/hyper_client.rs index ec9b5994..1bbbecfe 100644 --- a/proxy_agent_shared/src/common/hyper_client.rs +++ b/proxy_agent_shared/src/common/hyper_client.rs @@ -508,6 +508,10 @@ pub fn should_skip_sig(method: &hyper::Method, relative_uri: &Uri) -> bool { #[cfg(test)] mod tests { + use http::{HeaderMap, HeaderValue, Method}; + + use crate::common::constants; + #[test] fn get_path_and_canonicalized_parameters_test() { let url_str = "/machine/a8016240-7286-49ef-8981-63520cb8f6d0/49c242ba%2Dc18a%2D4f6c%2D8cf8%2D85ff790b6431.%5Fzpeng%2Debpf%2Dvm2?comp=config&keyOnly&comp=again&type=hostingEnvironmentConfig&incarnation=1&resource=https%3a%2f%2fstorage.azure.com%2f"; @@ -520,4 +524,68 @@ mod tests { "query parameters mismatch" ); } + + #[test] + fn query_pairs_basic() { + let uri = "/test?name=test&key=value&empty=".parse().unwrap(); + let pairs = super::query_pairs(&uri); + assert_eq!( + pairs, + vec![ + ("name".to_string(), "test".to_string()), + ("key".to_string(), "value".to_string()), + ("empty".to_string(), "".to_string()), + ] + ); + } + + #[test] + fn query_pairs_ignore_empty_key() { + let uri = "/test?=novalue&valid=1".parse().unwrap(); + let pairs = super::query_pairs(&uri); + assert_eq!(pairs, vec![("valid".to_string(), "1".to_string())]); + } + + #[test] + fn headers_to_canonicalized_string_sorts_and_skips_auth() { + let mut headers = HeaderMap::new(); + headers.insert("Test-Header", HeaderValue::from_static("test")); + headers.insert( + constants::AUTHORIZATION_HEADER, + HeaderValue::from_static("should-skip"), + ); + + let result = super::headers_to_canonicalized_string(&headers); + // "a-header" should come first, auth header skipped + assert!(result.starts_with("test-header:test\n")); + assert!(!result.contains("should-skip")); + } + + #[test] + fn host_port_from_uri_defaults_port() { + let uri = "http://example.com/test".parse().unwrap(); + let (host, port) = super::host_port_from_uri(&uri).unwrap(); + assert_eq!(host, "example.com"); + assert_eq!(port, 80); + } + + #[test] + fn host_port_from_uri_with_port() { + let uri = "http://example.com:8080/test".parse().unwrap(); + let (host, port) = super::host_port_from_uri(&uri).unwrap(); + assert_eq!(host, "example.com"); + assert_eq!(port, 8080); + } + + #[test] + fn should_skip_sig_matches() { + let put_uri = "/vmAgentLog".parse().unwrap(); + assert!(super::should_skip_sig(&Method::PUT, &put_uri)); + + let post_uri = "/machine/?comp=telemetrydata".parse().unwrap(); + assert!(super::should_skip_sig(&Method::POST, &post_uri)); + + let other_uri = "/machine/?comp=goalstate".parse().unwrap(); + assert!(!super::should_skip_sig(&Method::GET, &other_uri)); + } } From 13cb9f16b472d59f3532186314500d7f83e9225b Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 29 Sep 2025 15:41:32 -0700 Subject: [PATCH 30/35] more unit tests --- proxy_agent_shared/src/common/config.rs | 116 ++++++++++++++++++ proxy_agent_shared/src/common/hyper_client.rs | 2 +- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/proxy_agent_shared/src/common/config.rs b/proxy_agent_shared/src/common/config.rs index d81aaaea..aa1b63f2 100644 --- a/proxy_agent_shared/src/common/config.rs +++ b/proxy_agent_shared/src/common/config.rs @@ -221,6 +221,7 @@ impl Config { mod tests { use crate::common::config::Config; use crate::common::constants; + use crate::logger::LoggerLevel; use crate::misc_helpers; use std::fs::File; use std::io::Write; @@ -354,4 +355,119 @@ mod tests { .unwrap(); Config::from_json_file(file_path) } + + #[test] + fn test_optional_fields_some_values() { + let mut temp_path = env::temp_dir(); + temp_path.push("config_optional_some"); + _ = fs::remove_dir_all(&temp_path); + fs::create_dir_all(&temp_path).unwrap(); + let config_path = temp_path.join("config.json"); + + let data = r#"{ + "logFolder": "C:\\logFolderName", + "eventFolder": "C:\\eventFolderName", + "latchKeyFolder": "C:\\latchKeyFolderName", + "monitorIntervalInSeconds": 120, + "pollKeyStatusIntervalInSeconds": 30, + "hostGAPluginSupport": 2, + "maxEventFileCount": 42, + "ebpfFileFullPath": "C:\\ebpf.o", + "ebpfProgramName": "ebpfCustom", + "fileLogLevel": "Error", + "fileLogLevelForEvents": "Warn", + "fileLogLevelForSystemEvents": "Debug", + "enableHttpProxyTrace": true + }"#; + + let config = create_custom_config_file(config_path, data); + + assert_eq!(config.get_max_event_file_count(), 42); + assert_eq!( + config.get_ebpf_file_full_path(), + Some(PathBuf::from("C:\\ebpf.o")) + ); + assert_eq!(config.get_ebpf_program_name(), "ebpfCustom"); + assert_eq!(config.get_file_log_level(), LoggerLevel::Error); + assert_eq!( + config.get_file_log_level_for_events().unwrap(), + LoggerLevel::Warn + ); + assert_eq!( + config.get_file_log_level_for_system_events().unwrap(), + LoggerLevel::Debug + ); + assert_eq!(config.enableHttpProxyTrace.unwrap(), true); + + _ = fs::remove_dir_all(&temp_path); + } + + #[test] + fn test_optional_fields_none_values() { + let mut temp_path = env::temp_dir(); + temp_path.push("config_optional_none"); + _ = fs::remove_dir_all(&temp_path); + fs::create_dir_all(&temp_path).unwrap(); + let config_path = temp_path.join("config.json"); + + let data = r#"{ + "logFolder": "C:\\logFolderName", + "eventFolder": "C:\\eventFolderName", + "latchKeyFolder": "C:\\latchKeyFolderName", + "monitorIntervalInSeconds": 60, + "pollKeyStatusIntervalInSeconds": 15, + "hostGAPluginSupport": 1, + "ebpfProgramName": "ebpfProgramName" + }"#; + + let config = create_custom_config_file(config_path, data); + + // optional fields fallback + assert_eq!( + config.get_max_event_file_count(), + constants::DEFAULT_MAX_EVENT_FILE_COUNT + ); + assert_eq!(config.get_ebpf_file_full_path(), None); + assert_eq!(config.get_file_log_level(), LoggerLevel::Info); + assert_eq!(config.get_file_log_level_for_events(), None); + assert_eq!(config.get_file_log_level_for_system_events(), None); + + _ = fs::remove_dir_all(&temp_path); + } + + #[test] + fn test_invalid_log_level_fallback() { + let config = Config { + logFolder: "log".to_string(), + eventFolder: "event".to_string(), + latchKeyFolder: "latch".to_string(), + monitorIntervalInSeconds: 0, + pollKeyStatusIntervalInSeconds: 0, + hostGAPluginSupport: 0, + maxEventFileCount: None, + ebpfFileFullPath: None, + ebpfProgramName: "ebpf".to_string(), + fileLogLevel: Some("InvalidLevel".to_string()), + fileLogLevelForEvents: Some("InvalidLevel".to_string()), + fileLogLevelForSystemEvents: Some("InvalidLevel".to_string()), + enableHttpProxyTrace: None, + }; + + assert_eq!(config.get_file_log_level(), LoggerLevel::Info); + assert_eq!( + config.get_file_log_level_for_events().unwrap(), + LoggerLevel::Info + ); + assert_eq!( + config.get_file_log_level_for_system_events().unwrap(), + LoggerLevel::Info + ); + } + + fn create_custom_config_file(file_path: PathBuf, content: &str) -> Config { + fs::create_dir_all(file_path.parent().unwrap()).unwrap(); + let mut file = fs::File::create(&file_path).unwrap(); + file.write_all(content.as_bytes()).unwrap(); + Config::from_json_file(file_path) + } } diff --git a/proxy_agent_shared/src/common/hyper_client.rs b/proxy_agent_shared/src/common/hyper_client.rs index 1bbbecfe..a89116ad 100644 --- a/proxy_agent_shared/src/common/hyper_client.rs +++ b/proxy_agent_shared/src/common/hyper_client.rs @@ -541,7 +541,7 @@ mod tests { #[test] fn query_pairs_ignore_empty_key() { - let uri = "/test?=novalue&valid=1".parse().unwrap(); + let uri = "/test?=value&valid=1".parse().unwrap(); let pairs = super::query_pairs(&uri); assert_eq!(pairs, vec![("valid".to_string(), "1".to_string())]); } From 92f2c420d832760788b07ef08344c16b4d6ae74c Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 29 Sep 2025 15:54:29 -0700 Subject: [PATCH 31/35] linux test fixes --- proxy_agent_shared/src/common/config.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proxy_agent_shared/src/common/config.rs b/proxy_agent_shared/src/common/config.rs index aa1b63f2..ff8dd16d 100644 --- a/proxy_agent_shared/src/common/config.rs +++ b/proxy_agent_shared/src/common/config.rs @@ -451,6 +451,8 @@ mod tests { fileLogLevelForEvents: Some("InvalidLevel".to_string()), fileLogLevelForSystemEvents: Some("InvalidLevel".to_string()), enableHttpProxyTrace: None, + #[cfg(not(windows))] + cgroupRoot: None, }; assert_eq!(config.get_file_log_level(), LoggerLevel::Info); From bde00ad01a1abb05591c075a4592b5a69deaa305 Mon Sep 17 00:00:00 2001 From: yinbing Date: Mon, 29 Sep 2025 19:54:25 -0700 Subject: [PATCH 32/35] linux tests --- .../src/certificate/certificate_helper.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/proxy_agent_shared/src/certificate/certificate_helper.rs b/proxy_agent_shared/src/certificate/certificate_helper.rs index 98a934bb..ce25b1e0 100644 --- a/proxy_agent_shared/src/certificate/certificate_helper.rs +++ b/proxy_agent_shared/src/certificate/certificate_helper.rs @@ -60,7 +60,7 @@ pub fn decrypt_from_base64( mod tests { use super::*; #[test] - fn certificate_details_wrapper_test() { + fn generate_self_signed_certificate_test() { #[cfg(windows)] { let subject_name = "TestSubject"; @@ -79,4 +79,25 @@ mod tests { assert!(cert_details_result.is_err()); } } + + #[test] + fn decrypt_from_base64_test() { + #[cfg(windows)] + { + let subject_name = "TestSubject"; + let cert_details_result = generate_self_signed_certificate(subject_name); + assert!(cert_details_result.is_ok()); + let cert_details = cert_details_result.unwrap(); + let result = decrypt_from_base64("invalid input", &cert_details); + assert!(result.is_err()); + } + #[cfg(not(windows))] + { + let result = decrypt_from_base64( + "invalid input", + &CertificateDetailsWrapper { cert_details: () }, + ); + assert!(result.is_err()); + } + } } From 8d491af7a50725e4ea84d16b0efed1bd459584ba Mon Sep 17 00:00:00 2001 From: yinbing Date: Tue, 30 Sep 2025 09:39:24 -0700 Subject: [PATCH 33/35] ut for linux --- .../src/host_clients/wire_server_client.rs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/proxy_agent_shared/src/host_clients/wire_server_client.rs b/proxy_agent_shared/src/host_clients/wire_server_client.rs index 8747920d..77343211 100644 --- a/proxy_agent_shared/src/host_clients/wire_server_client.rs +++ b/proxy_agent_shared/src/host_clients/wire_server_client.rs @@ -90,18 +90,14 @@ mod tests { #[test] fn wire_server_client_creation_test() { - let client = WireServerClient::new("http://localhost:8080", |level, message| { - println!("{:?}: {}", level, message); - }); + let client = WireServerClient::new("http://localhost:8080", test_logger); assert_eq!(client.base_url, "http://localhost:8080"); assert_eq!(client.version, WireServerClient::DEFAULT_WIRE_VERSION); } #[test] fn wire_server_client_common_headers_test() { - let client = WireServerClient::new("http://localhost:8080", |level, message| { - println!("{:?}: {}", level, message); - }); + let client = WireServerClient::new("http://localhost:8080", test_logger); let headers = client.common_headers(); assert_eq!( headers.get("x-ms-version").unwrap(), @@ -111,9 +107,7 @@ mod tests { #[test] fn wire_server_client_update_version_test() { - let mut client = WireServerClient::new("http://localhost:8080", |level, message| { - println!("{:?}: {}", level, message); - }); + let mut client = WireServerClient::new("http://localhost:8080", test_logger); let versions = Versions { preferred: crate::host_clients::data_model::wire_server_model::Preferred { version: "2021-01-01".to_string(), @@ -125,4 +119,21 @@ mod tests { client.update_version(versions); assert_eq!(client.version, "2021-01-01"); } + + #[tokio::test] + async fn wire_server_client_get_url_invalid_uri() { + let client = WireServerClient::new("http://localhost:8080", test_logger); + + let res: Result = client.get_url("http://invalid uri").await; + assert!(res.is_err()); + + match res { + Err(Error::ParseUrl(_, _)) => {} // expected + _ => panic!("Expected Parse Url Error"), + } + } + + fn test_logger(level: LoggerLevel, message: String) { + println!("{:?}: {}", level, message); + } } From b7552aa5a946669252cc253591bd712307c25c68 Mon Sep 17 00:00:00 2001 From: yinbing Date: Tue, 30 Sep 2025 15:40:56 -0700 Subject: [PATCH 34/35] more unit tests --- .../src/common/formatted_error.rs | 27 +++++- .../src/host_clients/hostga_plugin_client.rs | 82 ++++++++++++++++--- .../src/host_clients/wire_server_client.rs | 56 +++++++++---- 3 files changed, 136 insertions(+), 29 deletions(-) diff --git a/proxy_agent_shared/src/common/formatted_error.rs b/proxy_agent_shared/src/common/formatted_error.rs index 7fe682fb..ec09ed47 100644 --- a/proxy_agent_shared/src/common/formatted_error.rs +++ b/proxy_agent_shared/src/common/formatted_error.rs @@ -1,6 +1,7 @@ use std::{fmt, string::FromUtf8Error}; use base64::DecodeError; +use tokio::time::error::Elapsed; #[derive(Debug, Clone)] pub struct FormattedError { @@ -52,6 +53,15 @@ impl From for FormattedError { } } +impl From for FormattedError { + fn from(value: Elapsed) -> Self { + FormattedError { + message: format!("Operation timeout: {value}"), + code: -1, + } + } +} + #[cfg(windows)] impl From for FormattedError { fn from(value: windows::core::Error) -> Self { @@ -64,6 +74,10 @@ impl From for FormattedError { #[cfg(test)] mod tests { + use std::time::Duration; + + use tokio::time::timeout; + use super::*; #[test] @@ -78,8 +92,8 @@ mod tests { ); } - #[test] - fn formatted_error_from_test() { + #[tokio::test] + async fn formatted_error_from_test() { let decode_error = DecodeError::InvalidLength(0); let formatted_error: FormattedError = decode_error.into(); assert_eq!(formatted_error.message, "Decode Error: InvalidLength(0)"); @@ -93,6 +107,15 @@ mod tests { let formatted_error: FormattedError = json_error.into(); assert!(formatted_error.message.starts_with("Json Error:")); + let elapsed_error = timeout(Duration::from_millis(10), async { + tokio::time::sleep(Duration::from_secs(1)).await; + }) + .await; + + let elapsed_error = elapsed_error.unwrap_err(); + let formatted_error: FormattedError = elapsed_error.into(); + assert!(formatted_error.message.starts_with("Operation timeout")); + #[cfg(windows)] { let windows_error = windows::core::Error::from_win32(); diff --git a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs index a2b83ba5..5c351cad 100644 --- a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::time::Duration; use crate::certificate::certificate_helper::{ decrypt_from_base64, generate_self_signed_certificate, @@ -15,11 +16,13 @@ use crate::logger::LoggerLevel; use base64::Engine; use http::{Method, StatusCode, Uri}; use serde::{Deserialize, Serialize}; +use tokio::time::timeout; use uuid::Uuid; pub struct HostGAPluginClient { base_url: String, logger: fn(LoggerLevel, String) -> (), + timeout_in_seconds: Option, } #[derive(Debug, Serialize, Deserialize)] @@ -41,10 +44,15 @@ impl HostGAPluginClient { const TRANSPORT_CERTIFICATE_HEADER: &'static str = "x-ms-guest-agent-public-x509-cert"; const TRANSPORT_CERTIFICATE_ENCRYPT_CIPHER_HEADER: &'static str = "x-ms-cipher-name"; - pub fn new(base_url: &str, logger: fn(LoggerLevel, String) -> ()) -> HostGAPluginClient { + pub fn new( + base_url: &str, + logger: fn(LoggerLevel, String) -> (), + timeout_in_seconds: Option, + ) -> HostGAPluginClient { HostGAPluginClient { base_url: base_url.to_string(), logger, + timeout_in_seconds, } } @@ -135,9 +143,20 @@ impl HostGAPluginClient { let request = hyper_client::build_request(Method::GET, &url, headers, None, None, None)?; let (host, port) = hyper_client::host_port_from_uri(&url)?; - let response = + + let response = if let Some(timeout_in_seconds) = self.timeout_in_seconds { + timeout( + Duration::from_secs(timeout_in_seconds as u64), + hyper_client::send_request(&host, port, request, move |m| { + logger(LoggerLevel::Warn, m) + }), + ) + .await + .map_err(|e| Into::::into(e))?? + } else { hyper_client::send_request(&host, port, request, move |m| logger(LoggerLevel::Warn, m)) - .await?; + .await? + }; let etag = response .headers() @@ -209,17 +228,26 @@ mod tests { #[test] fn hostgaplugin_client_creation_test() { - let client = HostGAPluginClient::new("http://localhost:8080", |level, message| { - println!("{:?}: {}", level, message); - }); + let client = HostGAPluginClient::new( + "http://localhost:8080", + |level, message| { + println!("{:?}: {}", level, message); + }, + None, + ); assert_eq!(client.base_url, "http://localhost:8080"); + assert_eq!(client.timeout_in_seconds, None); } #[test] fn certificate_request_headers_test() { - let client = HostGAPluginClient::new("http://localhost:8080", |level, message| { - println!("{:?}: {}", level, message); - }); + let client = HostGAPluginClient::new( + "http://localhost:8080", + |level, message| { + println!("{:?}: {}", level, message); + }, + None, + ); let cert = "test_cert"; let headers = client.certificate_request_headers(cert); assert_eq!( @@ -238,9 +266,13 @@ mod tests { #[test] fn vmsettings_request_headers_test() { - let client = HostGAPluginClient::new("http://localhost:8080", |level, message| { - println!("{:?}: {}", level, message); - }); + let client = HostGAPluginClient::new( + "http://localhost:8080", + |level, message| { + println!("{:?}: {}", level, message); + }, + None, + ); let etag = Some("test_etag".to_string()); let headers = client.vmsettings_request_headers(etag.clone()); assert_eq!( @@ -254,6 +286,32 @@ mod tests { .is_none()); } + #[tokio::test] + async fn get_vmsettings_negative_test() { + let client = HostGAPluginClient::new( + "http://invalid:8080", + |level, message| { + println!("{:?}: {}", level, message); + }, + Some(2), + ); + let response = client.get_vmsettings(None).await; + assert!(response.is_err()); + } + + #[tokio::test] + async fn get_certificates_negative_test() { + let client = HostGAPluginClient::new( + "http://invalid:8080", + |level, message| { + println!("{:?}: {}", level, message); + }, + Some(2), + ); + let response = client.get_certificates(0).await; + assert!(response.is_err()); + } + #[test] fn get_hostgaplugin_certificates_response_test() { let response = r#" diff --git a/proxy_agent_shared/src/host_clients/wire_server_client.rs b/proxy_agent_shared/src/host_clients/wire_server_client.rs index 77343211..e8608ac2 100644 --- a/proxy_agent_shared/src/host_clients/wire_server_client.rs +++ b/proxy_agent_shared/src/host_clients/wire_server_client.rs @@ -1,10 +1,11 @@ -use std::collections::HashMap; +use std::{collections::HashMap, time::Duration}; use http::Uri; use serde::Deserialize; +use tokio::time::timeout; use crate::{ - common::{error::Error, hyper_client, result::Result}, + common::{error::Error, formatted_error::FormattedError, hyper_client, result::Result}, host_clients::data_model::wire_server_model::{GoalState, Versions}, logger::LoggerLevel, }; @@ -13,6 +14,7 @@ pub struct WireServerClient { base_url: String, version: String, logger: fn(LoggerLevel, String) -> (), + timeout_in_seconds: Option, } impl WireServerClient { @@ -22,22 +24,27 @@ impl WireServerClient { const DEFAULT_WIRE_VERSION: &'static str = "2012-11-30"; - pub fn new(base_url: &str, logger: fn(LoggerLevel, String) -> ()) -> WireServerClient { + pub fn new( + base_url: &str, + logger: fn(LoggerLevel, String) -> (), + timeout_in_seconds: Option, + ) -> WireServerClient { WireServerClient { base_url: base_url.to_string(), version: Self::DEFAULT_WIRE_VERSION.to_string(), logger, + timeout_in_seconds, } } // http://168.63.129.16?comp=Versions pub async fn get_versions(&self) -> Result { - self.get::(Self::VERSIONS_URL).await + self.get_sub_url::(Self::VERSIONS_URL).await } // http://168.63.129.16/machine?comp=goalstate pub async fn get_goal_state(&self) -> Result { - self.get::(Self::GOAL_STATE_URL).await + self.get_sub_url::(Self::GOAL_STATE_URL).await } pub async fn refresh_wire_server_version(&mut self) -> Result<()> { @@ -46,7 +53,7 @@ impl WireServerClient { Ok(()) } - pub async fn get(&self, sub_url: &str) -> Result + pub async fn get_sub_url(&self, sub_url: &str) -> Result where T: for<'a> Deserialize<'a>, { @@ -65,11 +72,22 @@ impl WireServerClient { let headers = self.common_headers(); - let res = hyper_client::get(&url, &headers, None, None, move |message| { - logger(LoggerLevel::Warn, message) - }) - .await - .unwrap(); + let res = if let Some(timeout_in_seconds) = self.timeout_in_seconds { + timeout( + Duration::from_secs(timeout_in_seconds as u64), + hyper_client::get(&url, &headers, None, None, move |message| { + logger(LoggerLevel::Warn, message) + }), + ) + .await + .map_err(|e| Into::::into(e))?? + } else { + hyper_client::get(&url, &headers, None, None, move |message| { + logger(LoggerLevel::Warn, message) + }) + .await? + }; + Ok(res) } @@ -90,14 +108,14 @@ mod tests { #[test] fn wire_server_client_creation_test() { - let client = WireServerClient::new("http://localhost:8080", test_logger); + let client = WireServerClient::new("http://localhost:8080", test_logger, None); assert_eq!(client.base_url, "http://localhost:8080"); assert_eq!(client.version, WireServerClient::DEFAULT_WIRE_VERSION); } #[test] fn wire_server_client_common_headers_test() { - let client = WireServerClient::new("http://localhost:8080", test_logger); + let client = WireServerClient::new("http://localhost:8080", test_logger, None); let headers = client.common_headers(); assert_eq!( headers.get("x-ms-version").unwrap(), @@ -107,7 +125,7 @@ mod tests { #[test] fn wire_server_client_update_version_test() { - let mut client = WireServerClient::new("http://localhost:8080", test_logger); + let mut client = WireServerClient::new("http://localhost:8080", test_logger, None); let versions = Versions { preferred: crate::host_clients::data_model::wire_server_model::Preferred { version: "2021-01-01".to_string(), @@ -120,9 +138,17 @@ mod tests { assert_eq!(client.version, "2021-01-01"); } + #[tokio::test] + async fn wire_server_client_negative_test() { + let mut client = WireServerClient::new("http://invalid:8080", test_logger, Some(1)); + assert!(client.get_goal_state().await.is_err()); + assert!(client.get_versions().await.is_err()); + assert!(client.refresh_wire_server_version().await.is_err()); + } + #[tokio::test] async fn wire_server_client_get_url_invalid_uri() { - let client = WireServerClient::new("http://localhost:8080", test_logger); + let client = WireServerClient::new("http://localhost:8080", test_logger, None); let res: Result = client.get_url("http://invalid uri").await; assert!(res.is_err()); From ab7ecad22b15194c4730b2e4b521765493f5ebed Mon Sep 17 00:00:00 2001 From: yinbing Date: Tue, 30 Sep 2025 15:51:31 -0700 Subject: [PATCH 35/35] clippy fixes --- proxy_agent_shared/src/host_clients/hostga_plugin_client.rs | 2 +- proxy_agent_shared/src/host_clients/wire_server_client.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs index 5c351cad..8b37d059 100644 --- a/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs +++ b/proxy_agent_shared/src/host_clients/hostga_plugin_client.rs @@ -152,7 +152,7 @@ impl HostGAPluginClient { }), ) .await - .map_err(|e| Into::::into(e))?? + .map_err(Into::::into)?? } else { hyper_client::send_request(&host, port, request, move |m| logger(LoggerLevel::Warn, m)) .await? diff --git a/proxy_agent_shared/src/host_clients/wire_server_client.rs b/proxy_agent_shared/src/host_clients/wire_server_client.rs index e8608ac2..0980239f 100644 --- a/proxy_agent_shared/src/host_clients/wire_server_client.rs +++ b/proxy_agent_shared/src/host_clients/wire_server_client.rs @@ -80,7 +80,7 @@ impl WireServerClient { }), ) .await - .map_err(|e| Into::::into(e))?? + .map_err(Into::::into)?? } else { hyper_client::get(&url, &headers, None, None, move |message| { logger(LoggerLevel::Warn, message)