From 2db655bd6d8482fa94529aa9d88357e1f4149a56 Mon Sep 17 00:00:00 2001 From: KKRainbow <443152178@qq.com> Date: Sun, 19 Apr 2026 10:37:39 +0800 Subject: [PATCH] fix: refresh ACL groups and enable TCP_NODELAY for WebSocket (#2118) * fix: refresh ACL groups and enable TCP_NODELAY for WebSocket * add remove_peers to remove list of peer id in ospf route * fix secure tunnel for unreliable udp tunnel * fix(web-client): timeout secure tunnel handshake * fix(web-server): tolerate delayed secure hello * fix quic endpoint panic * fix replay check --- Cargo.lock | 5890 +++++++++++++++++++- easytier-web/src/client_manager/mod.rs | 21 +- easytier-web/src/client_manager/session.rs | 2 +- easytier/src/gateway/quic_proxy.rs | 5 + easytier/src/instance/instance.rs | 4 + easytier/src/peers/mod.rs | 1 + easytier/src/peers/peer_ospf_route.rs | 734 ++- easytier/src/peers/peer_session.rs | 834 +-- easytier/src/peers/route_trait.rs | 2 + easytier/src/peers/secure_datagram.rs | 1020 ++++ easytier/src/peers/tests.rs | 103 +- easytier/src/tunnel/websocket.rs | 3 + easytier/src/web_client/mod.rs | 19 +- easytier/src/web_client/security.rs | 224 +- 14 files changed, 7824 insertions(+), 1038 deletions(-) create mode 100644 easytier/src/peers/secure_datagram.rs diff --git a/Cargo.lock b/Cargo.lock index 07268683..0e9a6104 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + [[package]] name = "adler2" version = "2.0.0" @@ -43,6 +49,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -52,6 +69,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.18" @@ -64,6 +102,24 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" +dependencies = [ + "android_log-sys", + "env_logger", + "log", + "once_cell", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -128,6 +184,15 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "arbitrary" version = "1.4.1" @@ -137,18 +202,149 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image 0.25.10", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "wl-clipboard-rs", + "x11rb", +] + [[package]] name = "arc-swap" version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-compression" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2" +dependencies = [ + "compression-codecs", + "compression-core", + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.0.7", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.0.7", +] + [[package]] name = "async-recursion" version = "1.1.1" @@ -170,6 +366,24 @@ dependencies = [ "ringbuf", ] +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.0.7", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -192,6 +406,12 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.89" @@ -203,6 +423,38 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-shim" version = "0.2.0" @@ -248,24 +500,66 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.5", + "axum-macros 0.4.2", "bytes", "futures-util", "http", "http-body", "http-body-util", + "hyper", + "hyper-util", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", "pin-project-lite", "rustversion", "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", "sync_wrapper", + "tokio", "tower 0.5.2", "tower-layer", "tower-service", + "tracing", +] + +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core 0.5.6", + "axum-macros 0.5.1", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit 0.8.4", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", ] [[package]] @@ -286,8 +580,132 @@ dependencies = [ "sync_wrapper", "tower-layer", "tower-service", + "tracing", ] +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-embed" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077959a7f8cf438676af90b483304528eb7e16eadadb7f44e9ada4f9dceb9e62" +dependencies = [ + "axum-core 0.4.5", + "chrono", + "http", + "mime_guess", + "rust-embed", + "tower-service", +] + +[[package]] +name = "axum-extra" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96" +dependencies = [ + "axum 0.8.9", + "axum-core 0.5.6", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "serde_core", + "serde_html_form", + "serde_path_to_error", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-login" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5260ed0ecc8ace8e7e61a7406672faba598c8a86b8f4742fcdde0ddc979a318f" +dependencies = [ + "async-trait", + "axum 0.7.7", + "form_urlencoded", + "serde", + "subtle", + "thiserror 1.0.63", + "tower-cookies", + "tower-layer", + "tower-service", + "tower-sessions", + "tracing", + "urlencoding", +] + +[[package]] +name = "axum-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "axum-macros" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "axum-messages" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40e85c86a8bd84f54833bca296a0204bd865958ade62bacadeae92dda34cfb8a" +dependencies = [ + "async-trait", + "axum-core 0.4.5", + "http", + "parking_lot", + "serde", + "serde_json", + "tower 0.4.13", + "tower-sessions-core", + "tracing", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base62" version = "2.0.2" @@ -312,6 +730,26 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bigdecimal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695" +dependencies = [ + "autocfg", + "libm", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + [[package]] name = "bindgen" version = "0.72.1" @@ -341,6 +779,21 @@ name = "bitflags" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] [[package]] name = "blake2" @@ -360,6 +813,28 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bon" version = "3.9.1" @@ -376,7 +851,7 @@ version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ - "darling", + "darling 0.23.0", "ident_case", "prettyplease", "proc-macro2", @@ -410,6 +885,51 @@ dependencies = [ "x25519-dalek", ] +[[package]] +name = "borsh" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +dependencies = [ + "borsh-derive", + "bytes", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +dependencies = [ + "once_cell", + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "1.10.0" @@ -426,6 +946,28 @@ version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytecodec" version = "0.4.15" @@ -454,11 +996,20 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +dependencies = [ + "serde", +] [[package]] name = "bzip2" @@ -499,6 +1050,73 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.8.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.63", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.11", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + [[package]] name = "cc" version = "1.2.10" @@ -522,7 +1140,28 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", +] + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", ] [[package]] @@ -604,7 +1243,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.5", ] [[package]] @@ -669,6 +1308,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + [[package]] name = "codepage" version = "0.1.2" @@ -678,6 +1326,12 @@ dependencies = [ "encoding_rs", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.2" @@ -694,6 +1348,35 @@ dependencies = [ "memchr", ] +[[package]] +name = "compression-codecs" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console-api" version = "0.8.1" @@ -733,12 +1416,33 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "constant_time_eq" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "conv" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" +dependencies = [ + "custom_derive", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "convert_case" version = "0.10.0" @@ -748,6 +1452,22 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "base64 0.22.1", + "hmac", + "percent-encoding", + "rand 0.8.5", + "sha2", + "subtle", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -774,6 +1494,30 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.10.0", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.10.0", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.13" @@ -869,6 +1613,24 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -880,6 +1642,43 @@ dependencies = [ "typenum", ] +[[package]] +name = "cssparser" +version = "0.29.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "matches", + "phf 0.10.1", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.87", +] + +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn 2.0.87", +] + [[package]] name = "ctor" version = "0.8.0" @@ -914,6 +1713,7 @@ dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", + "digest", "fiat-crypto", "rustc_version", "subtle", @@ -931,14 +1731,30 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "custom_derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" + [[package]] name = "darling" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.10", + "darling_macro 0.20.10", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -955,13 +1771,37 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.87", +] + [[package]] name = "darling_macro" version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core", + "darling_core 0.20.10", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", "quote", "syn 2.0.87", ] @@ -1056,12 +1896,35 @@ dependencies = [ ] [[package]] -name = "deranged" -version = "0.4.0" +name = "delegate" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", + "serde_core", ] [[package]] @@ -1101,7 +1964,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling", + "darling 0.20.10", "proc-macro2", "quote", "syn 2.0.87", @@ -1117,6 +1980,19 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.87", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -1132,7 +2008,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case", + "convert_case 0.10.0", "proc-macro2", "quote", "rustc_version", @@ -1147,6 +2023,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -1192,6 +2069,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.8.0", + "block2", + "libc", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1203,6 +2092,71 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + [[package]] name = "dtor" version = "0.3.0" @@ -1218,6 +2172,18 @@ version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "easytier" version = "2.6.0" @@ -1239,7 +2205,6 @@ dependencies = [ "bytecodec", "byteorder", "bytes", - "cfg-if", "cfg_aliases", "chrono", "cidr", @@ -1248,23 +2213,23 @@ dependencies = [ "clap_complete_nushell", "console-subscriber", "crossbeam", - "ctor", + "ctor 0.8.0", "dashmap", "dbus", "defguard_wireguard_rs", + "delegate", "derivative", "derive_builder", - "derive_more", + "derive_more 2.1.1", "easytier-rpc-build", "encoding", - "flume", + "flume 0.12.0", "forwarded-header-value", "futures", "futures-util", - "gethostname", + "gethostname 0.5.0", "git-version", "globwalk", - "hashbrown 0.15.3", "hickory-client", "hickory-proto", "hickory-resolver", @@ -1274,7 +2239,7 @@ dependencies = [ "http_req", "humansize", "humantime-serde", - "idna", + "idna 1.0.3", "indoc", "itertools 0.14.0", "kcp-sys", @@ -1310,7 +2275,7 @@ dependencies = [ "rand 0.8.5", "rcgen", "regex", - "reqwest", + "reqwest 0.12.12", "resolv-conf", "ring", "ringbuf", @@ -1326,7 +2291,7 @@ dependencies = [ "smoltcp", "snow", "socket2 0.5.10", - "strum", + "strum 0.27.2", "stun_codec", "sys-locale", "tabled", @@ -1363,11 +2328,69 @@ dependencies = [ "windows-sys 0.52.0", "winreg 0.52.0", "x25519-dalek", - "zerocopy", + "zerocopy 0.7.35", "zip", "zstd", ] +[[package]] +name = "easytier-android-jni" +version = "0.1.0" +dependencies = [ + "android_logger", + "easytier", + "jni", + "log", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "easytier-ffi" +version = "0.1.0" +dependencies = [ + "dashmap", + "easytier", + "once_cell", + "serde", + "serde_json", + "uuid", +] + +[[package]] +name = "easytier-gui" +version = "2.6.0" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "dashmap", + "dunce", + "easytier", + "gethostname 1.1.0", + "libc", + "once_cell", + "security-framework-sys", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-clipboard-manager", + "tauri-plugin-os", + "tauri-plugin-positioner", + "tauri-plugin-process", + "tauri-plugin-shell", + "tauri-plugin-single-instance", + "tauri-plugin-vpnservice", + "thunk-rs", + "tokio", + "url", + "uuid", + "winapi", + "windows 0.52.0", +] + [[package]] name = "easytier-rpc-build" version = "0.1.0" @@ -1376,11 +2399,177 @@ dependencies = [ "prost-build", ] +[[package]] +name = "easytier-uptime" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "axum 0.8.9", + "axum-extra", + "chrono", + "clap", + "dashmap", + "easytier", + "futures", + "jsonwebtoken", + "mimalloc", + "mockall", + "once_cell", + "parking_lot", + "reqwest 0.12.12", + "sea-orm", + "sea-orm-migration", + "serde", + "serde_json", + "serde_yaml", + "sqlx", + "tempfile", + "thiserror 1.0.63", + "tokio", + "tokio-test", + "tokio-util", + "toml 0.8.19", + "tower 0.5.2", + "tower-http", + "tracing", + "tracing-subscriber", + "uuid", + "validator", +] + +[[package]] +name = "easytier-web" +version = "2.6.0" +dependencies = [ + "anyhow", + "async-trait", + "axum 0.7.7", + "axum-embed", + "axum-login", + "axum-messages", + "base64 0.22.1", + "chrono", + "clap", + "dashmap", + "easytier", + "image 0.24.9", + "imageproc", + "maxminddb", + "mimalloc", + "once_cell", + "openidconnect", + "password-auth", + "rand 0.8.5", + "reqwest 0.12.12", + "rust-embed", + "rust-i18n", + "rusttype", + "sea-orm", + "sea-orm-migration", + "serde", + "serde_json", + "sqlx", + "subtle", + "sys-locale", + "thiserror 1.0.63", + "thunk-rs", + "tokio", + "tower-http", + "tower-sessions", + "tower-sessions-sqlx-store", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "embed-resource" +version = "3.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63a1d0de4f2249aa0ff5884d7080814f446bb241a559af6c170a41e878ed2d45" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.9.12+spec-1.1.0", + "vswhom", + "winreg 0.55.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" [[package]] name = "encoding" @@ -1466,6 +2655,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + [[package]] name = "endian-type" version = "0.1.2" @@ -1484,12 +2679,43 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "env_home" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "log", + "regex", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1516,6 +2742,23 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + [[package]] name = "etherparse" version = "0.13.0" @@ -1525,6 +2768,27 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastbloom" version = "0.9.0" @@ -1533,7 +2797,7 @@ checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" dependencies = [ "getrandom 0.3.2", "rand 0.9.1", - "siphasher", + "siphasher 1.0.1", "wide", ] @@ -1546,12 +2810,61 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "fax" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" +dependencies = [ + "fax_derive", +] + +[[package]] +name = "fax_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -1575,6 +2888,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "flume" version = "0.12.0" @@ -1605,7 +2929,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -1614,6 +2959,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1633,6 +2984,31 @@ dependencies = [ "thiserror 1.0.63", ] +[[package]] +name = "fragile" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8878864ba14bb86e818a412bfd6f18f9eabd4ec0f008a28e8f7eb61db532fcf9" +dependencies = [ + "futures-core", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.30" @@ -1675,12 +3051,36 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -1728,6 +3128,114 @@ dependencies = [ "slab", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + [[package]] name = "generator" version = "0.8.4" @@ -1749,6 +3257,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1761,6 +3270,27 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.0.7", + "windows-link 0.2.1", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -1798,6 +3328,38 @@ dependencies = [ "polyval", ] +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.63", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + [[package]] name = "git-version" version = "0.3.9" @@ -1818,6 +3380,53 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.8.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.63", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + [[package]] name = "glob" version = "0.3.1" @@ -1848,6 +3457,80 @@ dependencies = [ "walkdir", ] +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "h2" version = "0.4.7" @@ -1860,13 +3543,24 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.7.1", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy 0.8.48", +] + [[package]] name = "hash32" version = "0.3.1" @@ -1881,6 +3575,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" @@ -1899,6 +3596,21 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.3", +] + [[package]] name = "hdrhistogram" version = "7.5.4" @@ -1908,7 +3620,7 @@ dependencies = [ "base64 0.21.7", "byteorder", "flate2", - "nom", + "nom 7.1.3", "num-traits", ] @@ -1940,6 +3652,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -1978,7 +3696,7 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna", + "idna 1.0.3", "ipnet", "once_cell", "rand 0.9.1", @@ -2037,6 +3755,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -2055,6 +3782,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "html5ever" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" +dependencies = [ + "log", + "mac", + "markup5ever", + "match_token", +] + [[package]] name = "http" version = "1.1.0" @@ -2144,13 +3883,14 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", @@ -2178,6 +3918,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots 0.26.3", ] [[package]] @@ -2211,18 +3952,22 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", "futures-util", "http", "http-body", "hyper", + "ipnet", + "libc", + "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.1", "tokio", "tower-service", "tracing", @@ -2251,6 +3996,16 @@ dependencies = [ "cc", ] +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png 0.17.16", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -2375,6 +4130,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "1.0.3" @@ -2412,6 +4177,51 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-traits", + "png 0.17.16", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.1", + "tiff", +] + +[[package]] +name = "imageproc" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aee993351d466301a29655d628bfc6f5a35a0d062b6160ca0808f425805fd7" +dependencies = [ + "approx", + "conv", + "image 0.24.9", + "itertools 0.10.5", + "nalgebra", + "num", + "rand 0.7.3", + "rand_distr", + "rayon", + "rusttype", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2420,16 +4230,19 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] name = "indexmap" -version = "2.7.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.17.0", + "serde", + "serde_core", ] [[package]] @@ -2441,6 +4254,26 @@ dependencies = [ "rustversion", ] +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "inherent" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c727f80bfa4a6c6e2508d2f05b6f4bfce242030bd88ed15ae5331c5b5d30fba7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "inout" version = "0.1.3" @@ -2511,12 +4344,50 @@ dependencies = [ "serde", ] +[[package]] +name = "iri-string" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.11.0" @@ -2550,6 +4421,29 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "jni" version = "0.21.1" @@ -2583,14 +4477,53 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.63", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64 0.22.1", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "kcp-sys" version = "0.1.0" @@ -2610,7 +4543,30 @@ dependencies = [ "tokio-util", "tracing", "tracing-subscriber", - "zerocopy", + "zerocopy 0.7.35", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.8.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.8-speedreader" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 2.14.0", + "selectors", ] [[package]] @@ -2618,6 +4574,33 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] [[package]] name = "libc" @@ -2635,6 +4618,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libloading" version = "0.8.5" @@ -2689,6 +4682,18 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.8.0", "libc", + "redox_syscall", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", ] [[package]] @@ -2732,6 +4737,7 @@ checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", + "serde", ] [[package]] @@ -2759,6 +4765,23 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix 0.29.0", + "serde", + "winapi", +] + [[package]] name = "machine-uid" version = "0.5.3" @@ -2781,6 +4804,31 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "markup5ever" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" +dependencies = [ + "log", + "phf 0.11.3", + "phf_codegen 0.11.3", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2790,12 +4838,56 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "matchit" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "maxminddb" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6087e5d8ea14861bb7c7f573afbc7be3798d3ef0fae87ec4fd9a4de9a127c3c" +dependencies = [ + "ipnetwork", + "log", + "memchr", + "serde", +] + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "md5" version = "0.7.0" @@ -2832,6 +4924,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2845,6 +4947,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -2853,12 +4956,39 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] +[[package]] +name = "mockall" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "moka" version = "0.12.10" @@ -2878,6 +5008,37 @@ dependencies = [ "uuid", ] +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "muda" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c9fec5a4e89860383d778d10563a605838f8f0b2f9303868937e5ff32e86177" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.17.16", + "serde", + "thiserror 2.0.11", + "windows-sys 0.60.2", +] + [[package]] name = "multimap" version = "0.10.1" @@ -2887,6 +5048,21 @@ dependencies = [ "serde", ] +[[package]] +name = "nalgebra" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb2d0de08694bed883320212c18ee3008576bfe8c306f4c3c4a58b4876998be" +dependencies = [ + "approx", + "matrixmultiply", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -2904,6 +5080,36 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.8.0", + "jni-sys", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.63", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + [[package]] name = "netlink-packet-core" version = "0.7.0" @@ -3005,6 +5211,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nibble_vec" version = "0.1.0" @@ -3051,12 +5263,31 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "no-std-net" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + [[package]] name = "nom" version = "7.1.3" @@ -3067,6 +5298,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "nonempty" version = "0.7.0" @@ -3093,10 +5333,90 @@ dependencies = [ ] [[package]] -name = "num-conv" -version = "0.1.0" +name = "num" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] [[package]] name = "num-traits" @@ -3105,6 +5425,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -3116,6 +5459,223 @@ dependencies = [ "libc", ] +[[package]] +name = "oauth2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" +dependencies = [ + "base64 0.22.1", + "chrono", + "getrandom 0.2.15", + "http", + "rand 0.8.5", + "reqwest 0.12.12", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror 1.0.63", + "url", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.8.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.8.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.8.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.8.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.8.0", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.8.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.8.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -3132,6 +5692,49 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "open" +version = "5.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" +dependencies = [ + "dunce", + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "openidconnect" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c6709ba2ea764bbed26bce1adf3c10517113ddea6f2d4196e4851757ef2b2" +dependencies = [ + "base64 0.21.7", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http", + "itertools 0.10.5", + "log", + "oauth2", + "p256", + "p384", + "rand 0.8.5", + "rsa", + "serde", + "serde-value", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror 1.0.63", + "url", +] + [[package]] name = "openssl" version = "0.10.66" @@ -3140,7 +5743,7 @@ checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ "bitflags 2.8.0", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -3192,6 +5795,34 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "ordered_hash_map" version = "0.5.0" @@ -3201,12 +5832,120 @@ dependencies = [ "hashbrown 0.15.3", ] +[[package]] +name = "os_info" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" +dependencies = [ + "android_system_properties", + "log", + "nix 0.30.1", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "ouroboros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.87", +] + [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owned_ttf_parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e6affeb1632d6ff6a23d2cd40ffed138e82f1532571a26f527c8a284bb2fbb" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "papergrid" version = "0.12.0" @@ -3218,6 +5957,12 @@ dependencies = [ "unicode-width 0.1.11", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -3241,12 +5986,41 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "password-auth" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2a4764cc1f8d961d802af27193c6f4f0124bd0e76e8393cf818e18880f0524" +dependencies = [ + "argon2", + "getrandom 0.2.15", + "password-hash", + "rand_core 0.6.4", +] + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + [[package]] name = "pbkdf2" version = "0.12.2" @@ -3267,6 +6041,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -3280,7 +6063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.7.1", + "indexmap 2.14.0", ] [[package]] @@ -3291,10 +6074,153 @@ checksum = "7a98c6720655620a521dcc722d0ad66cd8afd5d86e34a89ef691c50b7b24de06" dependencies = [ "fixedbitset 0.5.7", "hashbrown 0.15.3", - "indexmap 2.7.1", + "indexmap 2.14.0", "serde", ] +[[package]] +name = "pgvector" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc58e2d255979a31caa7cabfa7aac654af0354220719ab7a68520ae7a91e8c0b" +dependencies = [ + "serde", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_shared 0.8.0", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher 0.3.11", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -3327,6 +6253,38 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -3340,8 +6298,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", - "indexmap 2.7.1", - "quick-xml", + "indexmap 2.14.0", + "quick-xml 0.32.0", "serde", "time", ] @@ -3439,6 +6397,46 @@ dependencies = [ "pnet_sys", ] +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.8.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix 1.0.7", + "windows-sys 0.61.2", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -3480,7 +6478,39 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "predicates" +version = "3.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" + +[[package]] +name = "predicates-tree" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" +dependencies = [ + "predicates-core", + "termtree", ] [[package]] @@ -3504,6 +6534,34 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -3537,6 +6595,34 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + [[package]] name = "proc-macro2" version = "1.0.106" @@ -3546,6 +6632,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "version_check", + "yansi", +] + [[package]] name = "prost" version = "0.13.5" @@ -3677,6 +6776,38 @@ dependencies = [ "serde_json", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pxfm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.32.0" @@ -3686,6 +6817,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.8" @@ -3769,6 +6909,12 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "radix_trie" version = "0.2.1" @@ -3779,6 +6925,20 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + [[package]] name = "rand" version = "0.8.5" @@ -3800,6 +6960,16 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -3820,6 +6990,15 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -3838,6 +7017,65 @@ dependencies = [ "getrandom 0.3.2", ] +[[package]] +name = "rand_distr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2" +dependencies = [ + "rand 0.7.3", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "rcgen" version = "0.12.1" @@ -3881,6 +7119,26 @@ dependencies = [ "thiserror 2.0.11", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "regex" version = "1.12.3" @@ -3931,6 +7189,15 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.12.12" @@ -3959,7 +7226,10 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", @@ -3967,21 +7237,67 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tokio-rustls", "tower 0.5.2", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots 0.26.3", "windows-registry", ] +[[package]] +name = "reqwest" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower 0.5.2", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + [[package]] name = "resolv-conf" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7c8f7f733062b66dc1c63f9db168ac0b97a9210e247fa90fdc9ad08f51b302" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.14" @@ -4005,6 +7321,74 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rmp" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ + "rmp", + "serde", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rstest" version = "0.25.0" @@ -4025,7 +7409,7 @@ checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" dependencies = [ "cfg-if", "glob", - "proc-macro-crate", + "proc-macro-crate 3.2.0", "proc-macro2", "quote", "regex", @@ -4035,6 +7419,41 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "rust-embed" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.87", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +dependencies = [ + "globset", + "sha2", + "walkdir", +] + [[package]] name = "rust-i18n" version = "3.1.2" @@ -4084,11 +7503,28 @@ dependencies = [ "serde", "serde_json", "serde_yml", - "siphasher", + "siphasher 1.0.1", "toml 0.7.8", "triomphe", ] +[[package]] +name = "rust_decimal" +version = "1.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce901f9a19d251159075a4c37af514c3b8ef99c22e02dd8c19161cf397ee94a" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", + "wasm-bindgen", +] + [[package]] name = "rustc-hash" version = "2.1.0" @@ -4212,6 +7648,16 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rusttype" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff8374aa04134254b7995b63ad3dc41c7f7236f69528b28553da7d72efaa967" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -4260,6 +7706,57 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.87", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -4278,12 +7775,186 @@ version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0495e4577c672de8254beb68d01a9b62d0e8a13c099edecdbedccce3223cd29f" +[[package]] +name = "sea-bae" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f694a6ab48f14bc063cfadff30ab551d3c7e46d8f81836c51989d548f44a2a25" +dependencies = [ + "heck 0.4.1", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "sea-orm" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc312fedd460a47ea563911761d254a84e7b51d8cc73ec92c929e78f33fa957" +dependencies = [ + "async-stream", + "async-trait", + "bigdecimal", + "chrono", + "derive_more 2.1.1", + "futures-util", + "log", + "mac_address", + "ouroboros", + "pgvector", + "rust_decimal", + "sea-orm-macros", + "sea-query", + "sea-query-binder", + "serde", + "serde_json", + "sqlx", + "strum 0.26.3", + "thiserror 2.0.11", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sea-orm-cli" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da80ebcdb44571e86f03a2bdcb5532136a87397f366f38bbce64673fc5e6a450" +dependencies = [ + "chrono", + "clap", + "dotenvy", + "glob", + "regex", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "sea-orm-macros" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9a3f90e336ec74803e8eb98c61bc98754c1adfba3b4f84d946237b752b1c88" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "sea-bae", + "syn 2.0.87", + "unicode-ident", +] + +[[package]] +name = "sea-orm-migration" +version = "1.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c577f2959277e936c1d08109acd1e08fc36a95ef29ec028190ba82cad8f96e" +dependencies = [ + "async-trait", + "clap", + "dotenvy", + "sea-orm", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "sea-query" +version = "0.32.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5d1c518eaf5eda38e5773f902b26ab6d5e9e9e2bb2349ca6c64cf96f80448c" +dependencies = [ + "bigdecimal", + "chrono", + "inherent", + "ordered-float 4.6.0", + "rust_decimal", + "sea-query-derive", + "serde_json", + "time", + "uuid", +] + +[[package]] +name = "sea-query-binder" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608" +dependencies = [ + "bigdecimal", + "chrono", + "rust_decimal", + "sea-query", + "serde_json", + "sqlx", + "time", + "uuid", +] + +[[package]] +name = "sea-query-derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae0cbad6ab996955664982739354128c58d16e126114fe88c2a493642502aab" +dependencies = [ + "darling 0.20.10", + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.87", + "thiserror 2.0.11", +] + +[[package]] +name = "sea-schema" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2239ff574c04858ca77485f112afea1a15e53135d3097d0c86509cef1def1338" +dependencies = [ + "futures", + "sea-query", + "sea-schema-derive", +] + +[[package]] +name = "sea-schema-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "seahash" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -4320,11 +7991,32 @@ dependencies = [ "libc", ] +[[package]] +name = "selectors" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more 0.99.20", + "fxhash", + "log", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", +] + [[package]] name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -4336,6 +8028,28 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float 2.10.1", + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -4357,15 +8071,71 @@ dependencies = [ ] [[package]] -name = "serde_json" -version = "1.0.125" +name = "serde_derive_internals" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "serde_html_form" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" +dependencies = [ + "form_urlencoded", + "indexmap 2.14.0", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -4377,6 +8147,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4389,13 +8168,57 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.14.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serde_yml" version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e76bab63c3fd98d27c17f9cbce177f64a91f5e69ac04cafe04e1bb25d1dc3c" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.14.0", "itoa", "libyml", "log", @@ -4431,6 +8254,28 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "service-manager" version = "0.8.0" @@ -4446,6 +8291,16 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "servo_arc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.6" @@ -4477,6 +8332,17 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shared_child" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" +dependencies = [ + "libc", + "sigchld", + "windows-sys 0.60.2", +] + [[package]] name = "shellexpand" version = "3.1.1" @@ -4492,6 +8358,27 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "sigchld" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" +dependencies = [ + "libc", + "os_pipe", + "signal-hook", +] + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -4501,6 +8388,29 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3fd720c48c53cace224ae62bef1bbff363a70c68c4802a78b5cc6159618176" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -4513,6 +8423,24 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.11", + "time", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "siphasher" version = "1.0.1" @@ -4533,6 +8461,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "smoltcp" @@ -4584,6 +8515,54 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "spin" version = "0.9.8" @@ -4593,18 +8572,285 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "base64 0.22.1", + "bigdecimal", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.3", + "hashlink", + "indexmap 2.14.0", + "log", + "memchr", + "once_cell", + "percent-encoding", + "rust_decimal", + "rustls", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.11", + "time", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", + "webpki-roots 0.26.3", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.87", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.87", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64 0.22.1", + "bigdecimal", + "bitflags 2.8.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.5", + "rsa", + "rust_decimal", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64 0.22.1", + "bigdecimal", + "bitflags 2.8.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "num-bigint", + "once_cell", + "rand 0.8.5", + "rust_decimal", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "time", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "chrono", + "flume 0.11.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.11", + "time", + "tracing", + "url", + "uuid", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared 0.11.3", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", + "proc-macro2", + "quote", +] + +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" + [[package]] name = "strum" version = "0.27.2" @@ -4647,6 +8893,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "syn" version = "1.0.109" @@ -4729,6 +8986,19 @@ dependencies = [ "libc", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.19", + "version-compare", +] + [[package]] name = "tabled" version = "0.16.0" @@ -4758,6 +9028,403 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" +[[package]] +name = "tao" +version = "0.34.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9103edf55f2da3c82aea4c7fab7c4241032bfeea0e71fa557d98e00e7ce7cc20" +dependencies = [ + "bitflags 2.8.0", + "block2", + "core-foundation 0.10.0", + "core-graphics", + "crossbeam-channel", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", + "parking_lot", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da77cc00fb9028caf5b5d4650f75e31f1ef3693459dfca7f7e506d1ecef0ba2d" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs 6.0.0", + "dunce", + "embed_plist", + "getrandom 0.3.2", + "glob", + "gtk", + "heck 0.5.0", + "http", + "image 0.25.10", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest 0.13.2", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.11", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows 0.61.3", +] + +[[package]] +name = "tauri-build" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bbc990d1dbf57a8e1c7fa2327f2a614d8b757805603c1b9ba5c81bade09fd4d" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs 6.0.0", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "toml 0.9.12+spec-1.1.0", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a24476afd977c5d5d169f72425868613d82747916dd29e0a357c84c4bd6d29" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.87", + "tauri-utils", + "thiserror 2.0.11", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39b349a98dadaffebb73f0a40dcd1f23c999211e5a2e744403db384d0c33de7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.87", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddde7d51c907b940fb573006cdda9a642d6a7c8153657e88f8a5c3c9290cd4aa" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "toml 0.9.12+spec-1.1.0", + "walkdir", +] + +[[package]] +name = "tauri-plugin-clipboard-manager" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf" +dependencies = [ + "arboard", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", +] + +[[package]] +name = "tauri-plugin-os" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997" +dependencies = [ + "gethostname 1.1.0", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", +] + +[[package]] +name = "tauri-plugin-positioner" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02dcd56dd4797bd4d6c4c658daed40ce563176f92df90fbd2c904ce145de17ef" +dependencies = [ + "log", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", +] + +[[package]] +name = "tauri-plugin-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" +dependencies = [ + "tauri", + "tauri-plugin", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8457dbf9e2bab1edd8df22bb2c20857a59a9868e79cb3eac5ed639eec4d0c73b" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars 0.8.22", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror 2.0.11", + "tokio", +] + +[[package]] +name = "tauri-plugin-single-instance" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33a5b7d78f0dec4406b003ea87c40bf928d801b6fd9323a556172c91d8712c1" +dependencies = [ + "serde", + "serde_json", + "tauri", + "thiserror 2.0.11", + "tracing", + "windows-sys 0.60.2", + "zbus", +] + +[[package]] +name = "tauri-plugin-vpnservice" +version = "0.0.0" +dependencies = [ + "serde", + "tauri", + "tauri-plugin", + "thiserror 1.0.63", +] + +[[package]] +name = "tauri-runtime" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2826d79a3297ed08cd6ea7f412644ef58e32969504bc4fbd8d7dbeabc4445ea2" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.11", + "url", + "webkit2gtk", + "webview2-com", + "windows 0.61.3", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11ea2e6f801d275fdd890d6c9603736012742a1c33b96d0db788c9cdebf7f9e" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows 0.61.3", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219a1f983a2af3653f75b5747f76733b0da7ff03069c7a41901a5eb3ace4557d" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor 0.2.9", + "dunce", + "glob", + "html5ever", + "http", + "infer", + "json-patch", + "kuchikiki", + "log", + "memchr", + "phf 0.11.3", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.11", + "toml 0.9.12+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1087b111fe2b005e42dbdc1990fc18593234238d47453b0c99b7de1c9ab2c1e0" +dependencies = [ + "dunce", + "embed-resource", + "toml 0.9.12+spec-1.1.0", +] + [[package]] name = "tempfile" version = "3.22.0" @@ -4771,6 +9438,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "terminal_size" version = "0.4.1" @@ -4781,6 +9459,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + [[package]] name = "thiserror" version = "1.0.63" @@ -4836,6 +9520,20 @@ name = "thunk-rs" version = "0.3.5" source = "git+https://github.com/easytier/thunk.git#cbbeec75a66b7b3cf0824ae890d9d06bcfb9d1f3" +[[package]] +name = "tiff" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" +dependencies = [ + "fax", + "flate2", + "half", + "quick-error", + "weezl", + "zune-jpeg", +] + [[package]] name = "tikv-jemalloc-ctl" version = "0.6.0" @@ -4869,9 +9567,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", @@ -4879,22 +9577,22 @@ dependencies = [ "num-conv", "num_threads", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -5004,6 +9702,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-test" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6d24790a10a7af737693a3e8f1d03faef7e6ca0cc99aae5066f533766de545" +dependencies = [ + "futures-core", + "tokio", + "tokio-stream", +] + [[package]] name = "tokio-util" version = "0.7.13" @@ -5012,8 +9721,12 @@ checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", + "futures-util", + "hashbrown 0.14.5", "pin-project-lite", + "slab", "tokio", ] @@ -5046,8 +9759,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.7", + "toml_datetime 0.6.8", "toml_edit 0.19.15", ] @@ -5058,11 +9771,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.7", + "toml_datetime 0.6.8", "toml_edit 0.22.20", ] +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -5072,16 +9800,36 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.14.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.7", + "toml_datetime 0.6.8", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.8", "winnow 0.5.40", ] @@ -5091,13 +9839,28 @@ version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.7.1", + "indexmap 2.14.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.7", + "toml_datetime 0.6.8", "winnow 0.6.18", ] +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.1", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + [[package]] name = "tonic" version = "0.12.3" @@ -5106,7 +9869,7 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.7.7", "base64 0.22.1", "bytes", "h2", @@ -5174,6 +9937,46 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", +] + +[[package]] +name = "tower-cookies" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d" +dependencies = [ + "async-trait", + "axum-core 0.4.5", + "cookie", + "futures-util", + "http", + "parking_lot", + "pin-project-lite", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "async-compression", + "bitflags 2.8.0", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tokio", + "tokio-util", + "tower 0.5.2", + "tower-layer", + "tower-service", ] [[package]] @@ -5188,6 +9991,71 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "tower-sessions" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65856c81ee244e0f8a55ab0f7b769b72fbde387c235f0a73cd97c579818d05eb" +dependencies = [ + "async-trait", + "http", + "time", + "tokio", + "tower-cookies", + "tower-layer", + "tower-service", + "tower-sessions-core", + "tower-sessions-memory-store", + "tracing", +] + +[[package]] +name = "tower-sessions-core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb6abbfcaf6436ec5a772cd9f965401da12db793e404ae6134eac066fa5a04f3" +dependencies = [ + "async-trait", + "axum-core 0.4.5", + "base64 0.22.1", + "futures", + "http", + "parking_lot", + "rand 0.8.5", + "serde", + "serde_json", + "thiserror 1.0.63", + "time", + "tokio", + "tracing", +] + +[[package]] +name = "tower-sessions-memory-store" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fad75660c8afbe74f4e7cbbe8e9090171a056b57370ea4d7d5e9eb3e4af3092" +dependencies = [ + "async-trait", + "time", + "tokio", + "tower-sessions-core", +] + +[[package]] +name = "tower-sessions-sqlx-store" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd38eba51214e99accab78f6b7c8e273e90a9cb57575e86b592c60074e182d7" +dependencies = [ + "async-trait", + "rmp-serde", + "sqlx", + "thiserror 1.0.63", + "time", + "tower-sessions-core", +] + [[package]] name = "tracing" version = "0.1.41" @@ -5280,6 +10148,39 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "tray-icon" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" +dependencies = [ + "crossbeam-channel", + "dirs 6.0.0", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.17.16", + "serde", + "thiserror 2.0.11", + "windows-sys 0.60.2", +] + +[[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr", + "nom 8.0.0", + "petgraph 0.8.1", +] + [[package]] name = "triomphe" version = "0.1.13" @@ -5297,6 +10198,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + [[package]] name = "tun-easytier" version = "1.1.1" @@ -5307,7 +10214,7 @@ dependencies = [ "futures-core", "ipnet", "libc", - "libloading", + "libloading 0.8.5", "log", "nix 0.29.0", "thiserror 1.0.63", @@ -5353,18 +10260,91 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicase" version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -5399,6 +10379,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -5412,11 +10398,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna", + "idna 1.0.3", "percent-encoding", "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf16_iter" version = "1.0.5" @@ -5460,6 +10470,36 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "validator" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e" +dependencies = [ + "idna 0.5.0", + "once_cell", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0bcf92720c40105ac4b2dda2a4ea3aa717d4d6a862cc217da653a4bd5c6b10" +dependencies = [ + "darling 0.20.10", + "once_cell", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "valuable" version = "0.1.0" @@ -5484,6 +10524,26 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -5503,6 +10563,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5519,48 +10585,40 @@ dependencies = [ ] [[package]] -name = "wasm-bindgen" -version = "0.2.100" +name = "wasite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" dependencies = [ "cfg-if", "once_cell", "rustversion", + "serde", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.87", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" dependencies = [ - "cfg-if", "js-sys", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5568,31 +10626,114 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn 2.0.87", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" dependencies = [ "unicode-ident", ] [[package]] -name = "web-sys" -version = "0.3.70" +name = "wasm-streams" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wayland-backend" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.0.7", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +dependencies = [ + "bitflags 2.8.0", + "rustix 1.0.7", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +dependencies = [ + "bitflags 2.8.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags 2.8.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +dependencies = [ + "proc-macro2", + "quick-xml 0.39.2", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" dependencies = [ "js-sys", "wasm-bindgen", @@ -5608,6 +10749,50 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + [[package]] name = "webpki" version = "0.22.4" @@ -5654,6 +10839,48 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-implement 0.60.2", + "windows-interface 0.59.3", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.11", + "windows 0.61.3", + "windows-core 0.61.2", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + [[package]] name = "which" version = "4.4.2" @@ -5678,6 +10905,16 @@ dependencies = [ "winsafe", ] +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] + [[package]] name = "wide" version = "0.7.28" @@ -5752,6 +10989,21 @@ dependencies = [ "windows 0.48.0", ] +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + [[package]] name = "windows" version = "0.48.0" @@ -5781,6 +11033,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[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-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" @@ -5796,13 +11070,37 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "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]] name = "windows-implement" version = "0.58.0" @@ -5814,6 +11112,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -5825,20 +11134,47 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[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", - "windows-strings", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets 0.52.6", ] @@ -5851,6 +11187,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-service" version = "0.7.0" @@ -5868,10 +11213,19 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -5923,7 +11277,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -5988,6 +11342,24 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[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-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -6186,6 +11558,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" + [[package]] name = "winreg" version = "0.50.0" @@ -6206,6 +11593,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + [[package]] name = "winsafe" version = "0.0.19" @@ -6219,7 +11616,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b196f9328341b035820c54beebca487823e2e20a5977f284f2af2a0ee8f04400" dependencies = [ "c2rust-bitfields", - "libloading", + "libloading 0.8.5", "log", "thiserror 1.0.63", "windows-sys 0.52.0", @@ -6234,6 +11631,24 @@ dependencies = [ "bitflags 2.8.0", ] +[[package]] +name = "wl-clipboard-rs" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix 1.0.7", + "thiserror 2.0.11", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + [[package]] name = "write16" version = "1.0.0" @@ -6246,6 +11661,98 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "wry" +version = "0.54.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb26159b420aa77684589a744ae9a9461a95395b848764ad12290a14d960a11a" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs 6.0.0", + "dpi", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.11", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.61.3", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname 1.1.0", + "rustix 1.0.7", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + [[package]] name = "x25519-dalek" version = "2.0.1" @@ -6264,6 +11771,12 @@ version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yasna" version = "0.5.2" @@ -6297,6 +11810,66 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "nix 0.30.1", + "ordered-stream", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 0.7.15", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.87", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +dependencies = [ + "serde", + "winnow 0.7.15", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -6304,7 +11877,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive 0.8.48", ] [[package]] @@ -6318,6 +11900,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "zerofrom" version = "0.1.6" @@ -6396,7 +11989,7 @@ dependencies = [ "flate2", "getrandom 0.3.2", "hmac", - "indexmap 2.7.1", + "indexmap 2.14.0", "liblzma", "memchr", "pbkdf2", @@ -6413,6 +12006,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8" +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zopfli" version = "0.8.2" @@ -6452,3 +12051,58 @@ dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] + +[[package]] +name = "zvariant" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 0.7.15", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.87", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.87", + "winnow 0.7.15", +] diff --git a/easytier-web/src/client_manager/mod.rs b/easytier-web/src/client_manager/mod.rs index d18c2ab0..abc53ed4 100644 --- a/easytier-web/src/client_manager/mod.rs +++ b/easytier-web/src/client_manager/mod.rs @@ -379,19 +379,26 @@ mod tests { let req = tokio::time::timeout(Duration::from_secs(12), async { loop { - let session = mgr + let sessions = mgr .client_sessions .iter() - .next() - .map(|item| item.value().clone()); - let Some(session) = session else { + .map(|item| item.value().clone()) + .collect::>(); + if sessions.is_empty() { tokio::time::sleep(Duration::from_millis(100)).await; continue; - }; - let mut waiter = session.data().read().await.heartbeat_waiter(); - if let Ok(req) = waiter.recv().await { + } + let mut found_req = None; + for session in sessions { + if let Some(req) = session.data().read().await.req() { + found_req = Some(req); + break; + } + } + if let Some(req) = found_req { break req; } + tokio::time::sleep(Duration::from_millis(100)).await; } }) .await diff --git a/easytier-web/src/client_manager/session.rs b/easytier-web/src/client_manager/session.rs index ab0f4fb6..3867ca4d 100644 --- a/easytier-web/src/client_manager/session.rs +++ b/easytier-web/src/client_manager/session.rs @@ -386,7 +386,7 @@ impl WebServerService for SessionRpcService { _: easytier::proto::web::GetFeatureRequest, ) -> rpc_types::error::Result { Ok(easytier::proto::web::GetFeatureResponse { - support_encryption: true, + support_encryption: easytier::web_client::security::web_secure_tunnel_supported(), }) } } diff --git a/easytier/src/gateway/quic_proxy.rs b/easytier/src/gateway/quic_proxy.rs index dfd88d95..eb468037 100644 --- a/easytier/src/gateway/quic_proxy.rs +++ b/easytier/src/gateway/quic_proxy.rs @@ -611,6 +611,11 @@ impl QuicStreamReceiver { } _ = self.tasks.join_next(), if !self.tasks.is_empty() => {} + + else => { + info!("quic stream receiver endpoint closed, exiting"); + break; + } } } } diff --git a/easytier/src/instance/instance.rs b/easytier/src/instance/instance.rs index 7a5966ce..8d30f5fe 100644 --- a/easytier/src/instance/instance.rs +++ b/easytier/src/instance/instance.rs @@ -383,6 +383,10 @@ impl InstanceConfigPatcher { global_ctx .get_acl_filter() .reload_rules(AclRuleBuilder::build(&global_ctx)?.as_ref()); + weak_upgrade(&self.peer_manager)? + .get_route() + .refresh_acl_groups() + .await; Ok(()) } diff --git a/easytier/src/peers/mod.rs b/easytier/src/peers/mod.rs index 523d6718..24720e71 100644 --- a/easytier/src/peers/mod.rs +++ b/easytier/src/peers/mod.rs @@ -20,6 +20,7 @@ pub mod foreign_network_client; pub mod foreign_network_manager; pub mod encrypt; +pub(crate) mod secure_datagram; pub mod peer_task; diff --git a/easytier/src/peers/peer_ospf_route.rs b/easytier/src/peers/peer_ospf_route.rs index 0f3d4992..f260e601 100644 --- a/easytier/src/peers/peer_ospf_route.rs +++ b/easytier/src/peers/peer_ospf_route.rs @@ -418,6 +418,32 @@ impl Debug for SyncedRouteInfo { } impl SyncedRouteInfo { + fn set_peer_groups(&self, peer_id: PeerId, groups: HashMap>) { + if groups.is_empty() { + self.group_trust_map.remove(&peer_id); + self.group_trust_map_cache.remove(&peer_id); + return; + } + + let group_names = groups.keys().cloned().collect(); + self.group_trust_map.insert(peer_id, groups); + self.group_trust_map_cache + .insert(peer_id, Arc::new(group_names)); + } + + fn get_proof_groups(&self, peer_id: PeerId) -> HashMap> { + self.group_trust_map + .get(&peer_id) + .map(|groups| { + groups + .iter() + .filter(|(_, proof)| !proof.is_empty()) + .map(|(group, proof)| (group.clone(), proof.clone())) + .collect() + }) + .unwrap_or_default() + } + fn mark_credential_peer(info: &mut RoutePeerInfo, is_credential_peer: bool) { let mut feature_flag = info.feature_flag.unwrap_or_default(); feature_flag.is_credential_peer = is_credential_peer; @@ -439,13 +465,38 @@ impl SyncedRouteInfo { } fn remove_peer(&self, peer_id: PeerId) { - tracing::warn!(?peer_id, "remove_peer from synced_route_info"); - self.peer_infos.write().remove(&peer_id); - self.raw_peer_infos.remove(&peer_id); - self.conn_map.write().remove(&peer_id); - self.foreign_network.retain(|k, _| k.peer_id != peer_id); - self.group_trust_map.remove(&peer_id); - self.group_trust_map_cache.remove(&peer_id); + self.remove_peers([peer_id]); + } + + fn remove_peers(&self, peer_ids: I) + where + I: IntoIterator, + { + let peer_ids: HashSet<_> = peer_ids.into_iter().collect(); + if peer_ids.is_empty() { + return; + } + + for peer_id in &peer_ids { + tracing::warn!(?peer_id, "remove_peer from synced_route_info"); + } + + { + let mut peer_infos = self.peer_infos.write(); + let mut conn_map = self.conn_map.write(); + for peer_id in &peer_ids { + peer_infos.remove(peer_id); + conn_map.remove(peer_id); + } + } + + for peer_id in &peer_ids { + self.raw_peer_infos.remove(peer_id); + self.group_trust_map.remove(peer_id); + self.group_trust_map_cache.remove(peer_id); + } + self.foreign_network + .retain(|k, _| !peer_ids.contains(&k.peer_id)); shrink_dashmap(&self.raw_peer_infos, None); shrink_dashmap(&self.foreign_network, None); @@ -827,31 +878,20 @@ impl SyncedRouteInfo { &self, peer_infos: &[RoutePeerInfo], local_group_declarations: &[GroupIdentity], + trust_admin_groups_without_proof: bool, ) { let local_group_declarations = local_group_declarations .iter() .map(|g| (g.group_name.as_str(), g.group_secret.as_str())) .collect::>(); - let verify_groups = |old_trusted_groups: Option<&HashMap>>, - info: &RoutePeerInfo| - -> HashMap> { + let verify_groups = |info: &RoutePeerInfo| -> HashMap> { let mut trusted_groups_for_peer: HashMap> = HashMap::new(); for group_proof in &info.groups { let name = &group_proof.group_name; let proof_bytes = group_proof.group_proof.clone(); - // If we already trusted this group and the proof hasn't changed, reuse it. - if old_trusted_groups - .and_then(|g| g.get(name)) - .map(|old| old == &proof_bytes) - .unwrap_or(false) - { - trusted_groups_for_peer.insert(name.clone(), proof_bytes); - continue; - } - if let Some(&local_secret) = local_group_declarations.get(group_proof.group_name.as_str()) { @@ -867,34 +907,39 @@ impl SyncedRouteInfo { } } + if trust_admin_groups_without_proof && self.is_admin_peer(info) { + for group_proof in &info.groups { + trusted_groups_for_peer + .entry(group_proof.group_name.clone()) + .or_default(); + } + } + trusted_groups_for_peer }; for info in peer_infos { match self.group_trust_map.entry(info.peer_id) { dashmap::mapref::entry::Entry::Occupied(mut entry) => { - let old_trusted_groups = entry.get().clone(); - let trusted_groups_for_peer = verify_groups(Some(&old_trusted_groups), info); + let trusted_groups_for_peer = verify_groups(info); if trusted_groups_for_peer.is_empty() { entry.remove(); self.group_trust_map_cache.remove(&info.peer_id); } else { - self.group_trust_map_cache.insert( - info.peer_id, - Arc::new(trusted_groups_for_peer.keys().cloned().collect()), - ); + let group_names = trusted_groups_for_peer.keys().cloned().collect(); + self.group_trust_map_cache + .insert(info.peer_id, Arc::new(group_names)); *entry.get_mut() = trusted_groups_for_peer; } } dashmap::mapref::entry::Entry::Vacant(entry) => { - let trusted_groups_for_peer = verify_groups(None, info); + let trusted_groups_for_peer = verify_groups(info); if !trusted_groups_for_peer.is_empty() { - self.group_trust_map_cache.insert( - info.peer_id, - Arc::new(trusted_groups_for_peer.keys().cloned().collect()), - ); + let group_names = trusted_groups_for_peer.keys().cloned().collect(); + self.group_trust_map_cache + .insert(info.peer_id, Arc::new(group_names)); entry.insert(trusted_groups_for_peer); } } @@ -904,16 +949,12 @@ impl SyncedRouteInfo { fn update_my_group_trusts(&self, my_peer_id: PeerId, groups: &[PeerGroupInfo]) { let mut my_group_map = HashMap::new(); - let mut my_group_names = Vec::new(); for group in groups.iter() { my_group_map.insert(group.group_name.clone(), group.group_proof.clone()); - my_group_names.push(group.group_name.clone()); } - self.group_trust_map.insert(my_peer_id, my_group_map); - self.group_trust_map_cache - .insert(my_peer_id, Arc::new(my_group_names)); + self.set_peer_groups(my_peer_id, my_group_map); } /// Collect trusted credential pubkeys from admin nodes (network_secret holders) @@ -1004,18 +1045,13 @@ impl SyncedRouteInfo { continue; } if let Some(tc) = all_trusted.get(&info.noise_static_pubkey) { - // This peer is a credential peer, assign groups from credential declaration - if !tc.groups.is_empty() { - let mut group_map = HashMap::new(); - let mut group_names = Vec::new(); - for g in &tc.groups { - group_map.insert(g.clone(), Vec::new()); // no proof needed, admin-declared - group_names.push(g.clone()); - } - self.group_trust_map.insert(info.peer_id, group_map); - self.group_trust_map_cache - .insert(info.peer_id, Arc::new(group_names)); + // Start from proof-backed groups so credential-declared groups can coexist + // without leaving stale credential-only entries behind after refreshes. + let mut group_map = self.get_proof_groups(info.peer_id); + for g in &tc.groups { + group_map.entry(g.clone()).or_default(); } + self.set_peer_groups(info.peer_id, group_map); } } @@ -1039,19 +1075,10 @@ impl SyncedRouteInfo { // Remove untrusted peers from peer_infos so they won't appear in route graph if !untrusted_peers.is_empty() { drop(peer_infos); // release read lock before writing - let mut peer_infos_write = self.peer_infos.write(); for peer_id in &untrusted_peers { tracing::warn!(?peer_id, "removing untrusted peer from route info"); - peer_infos_write.remove(peer_id); - self.raw_peer_infos.remove(peer_id); } - drop(peer_infos_write); - // Also remove from conn_map - let mut conn_map = self.conn_map.write(); - for peer_id in &untrusted_peers { - conn_map.remove(peer_id); - } - self.version.inc(); + self.remove_peers(untrusted_peers.iter().copied()); } (untrusted_peers, global_trusted_keys) @@ -2444,14 +2471,53 @@ impl PeerRouteServiceImpl { my_peer_info_updated || my_conn_info_updated || my_foreign_network_updated } - async fn refresh_credential_trusts_and_disconnect(&self) -> bool { + async fn refresh_acl_groups(&self) -> bool { + let my_peer_info_updated = self.update_my_peer_info(); + let trust_admin_groups_without_proof = self + .global_ctx + .get_network_identity() + .network_secret + .is_none(); + + let peer_infos: Vec<_> = self + .synced_route_info + .peer_infos + .read() + .iter() + .map(|(_, info)| info.clone()) + .collect(); + self.synced_route_info.verify_and_update_group_trusts( + &peer_infos, + &self.global_ctx.get_acl_group_declarations(), + trust_admin_groups_without_proof, + ); + + let untrusted = self.refresh_credential_trusts(); + self.disconnect_untrusted_peers(&untrusted).await; + + if my_peer_info_updated || !untrusted.is_empty() { + self.update_route_table_and_cached_local_conn_bitmap(); + self.update_foreign_network_owner_map(); + } + if my_peer_info_updated { + self.update_peer_info_last_update(); + } + + my_peer_info_updated || !untrusted.is_empty() + } + + fn refresh_credential_trusts(&self) -> Vec { let network_identity = self.global_ctx.get_network_identity(); - let network_secret = network_identity.network_secret.as_deref(); let (untrusted, global_trusted_keys) = self .synced_route_info - .verify_and_update_credential_trusts(network_secret); + .verify_and_update_credential_trusts(network_identity.network_secret.as_deref()); self.global_ctx .update_trusted_keys(global_trusted_keys, &network_identity.network_name); + untrusted + } + + async fn refresh_credential_trusts_and_disconnect(&self) -> bool { + let untrusted = self.refresh_credential_trusts(); self.disconnect_untrusted_peers(&untrusted).await; !untrusted.is_empty() } @@ -2529,9 +2595,8 @@ impl PeerRouteServiceImpl { } } - for p in to_remove.iter() { - self.synced_route_info.remove_peer(*p); - } + self.synced_route_info + .remove_peers(to_remove.iter().copied()); // clear expired foreign network info let mut to_remove = Vec::new(); @@ -3197,6 +3262,11 @@ impl RouteSessionManager { (peer_infos, raw_peer_infos.as_ref().unwrap()) }; if !pi.is_empty() { + let trust_admin_groups_without_proof = service_impl + .global_ctx + .get_network_identity() + .network_secret + .is_none(); service_impl.synced_route_info.update_peer_infos( my_peer_id, service_impl.my_peer_route_id, @@ -3209,6 +3279,7 @@ impl RouteSessionManager { .verify_and_update_group_trusts( pi, &service_impl.global_ctx.get_acl_group_declarations(), + trust_admin_groups_without_proof, ); session.update_dst_saved_peer_info_version(pi, from_peer_id); need_update_route_table = true; @@ -3228,15 +3299,7 @@ impl RouteSessionManager { if need_update_route_table { // Run credential verification and update route table - let network_identity = service_impl.global_ctx.get_network_identity(); - let (untrusted, global_trusted_keys) = service_impl - .synced_route_info - .verify_and_update_credential_trusts(network_identity.network_secret.as_deref()); - untrusted_peers = untrusted; - // Sync trusted keys to GlobalCtx for handshake verification - service_impl - .global_ctx - .update_trusted_keys(global_trusted_keys, &network_identity.network_name); + untrusted_peers = service_impl.refresh_credential_trusts(); service_impl.update_route_table_and_cached_local_conn_bitmap(); } @@ -3644,6 +3707,12 @@ impl Route for PeerRoute { fn get_peer_groups(&self, peer_id: PeerId) -> Arc> { self.service_impl.get_peer_groups(peer_id) } + + async fn refresh_acl_groups(&self) { + if self.service_impl.refresh_acl_groups().await { + self.session_mgr.sync_now("refresh_acl_groups"); + } + } } impl PeerPacketFilter for Arc {} @@ -3665,11 +3734,14 @@ mod tests { time::{Duration, SystemTime}, }; - use super::{PeerRoute, REMOVE_DEAD_PEER_INFO_AFTER}; + use super::{PeerRoute, REMOVE_DEAD_PEER_INFO_AFTER, RouteConnInfo}; use crate::{ common::{ PeerId, - global_ctx::{GlobalCtxEvent, TrustedKeySource, tests::get_mock_global_ctx}, + global_ctx::{ + GlobalCtxEvent, TrustedKeySource, + tests::{get_mock_global_ctx, get_mock_global_ctx_with_network}, + }, }, connector::udp_hole_punch::tests::replace_stun_info_collector, peers::{ @@ -3680,8 +3752,10 @@ mod tests { tests::{connect_peer_manager, create_mock_peer_manager, wait_route_appear}, }, proto::{ + acl::{Acl, AclV1, GroupIdentity, GroupInfo}, common::{NatType, PeerFeatureFlag}, peer_rpc::{ + ForeignNetworkRouteInfoEntry, ForeignNetworkRouteInfoKey, PeerGroupInfo, PeerIdentityType, RoutePeerInfo, RoutePeerInfos, SyncRouteInfoRequest, TrustedCredentialPubkey, TrustedCredentialPubkeyProof, }, @@ -4040,6 +4114,219 @@ mod tests { ); } + #[tokio::test] + async fn credential_groups_merge_with_proof_groups_and_recompute_cleanly() { + let service_impl = PeerRouteServiceImpl::new(1, get_mock_global_ctx()); + let network_secret = "sec1"; + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + let credential_peer_id = 31; + let credential_pubkey = vec![7; 32]; + + let mut credential_info = RoutePeerInfo::new(); + credential_info.peer_id = credential_peer_id; + credential_info.version = 1; + credential_info.noise_static_pubkey = credential_pubkey.clone(); + credential_info.groups = vec![PeerGroupInfo::generate_with_proof( + "proof-group".to_string(), + "proof-secret".to_string(), + credential_peer_id, + )]; + + let mut admin_info = RoutePeerInfo::new(); + admin_info.peer_id = 32; + admin_info.version = 1; + admin_info.feature_flag = Some(PeerFeatureFlag { + is_credential_peer: false, + ..Default::default() + }); + admin_info.trusted_credential_pubkeys = vec![TrustedCredentialPubkeyProof::new_signed( + TrustedCredentialPubkey { + pubkey: credential_pubkey.clone(), + groups: vec!["cred-group".to_string()], + expiry_unix: now + 600, + ..Default::default() + }, + network_secret, + )]; + + { + let mut guard = service_impl.synced_route_info.peer_infos.write(); + guard.insert(admin_info.peer_id, admin_info.clone()); + guard.insert(credential_peer_id, credential_info.clone()); + } + + service_impl + .synced_route_info + .verify_and_update_group_trusts( + &[credential_info], + &[GroupIdentity { + group_name: "proof-group".to_string(), + group_secret: "proof-secret".to_string(), + }], + false, + ); + service_impl + .synced_route_info + .verify_and_update_credential_trusts(Some(network_secret)); + + let groups = service_impl.get_peer_groups(credential_peer_id); + assert!(groups.contains(&"proof-group".to_string())); + assert!(groups.contains(&"cred-group".to_string())); + + let guard = service_impl.synced_route_info.peer_infos.write(); + let admin_info = guard.get(&32).unwrap().clone(); + drop(guard); + + let mut updated_admin = admin_info; + updated_admin.trusted_credential_pubkeys = vec![TrustedCredentialPubkeyProof::new_signed( + TrustedCredentialPubkey { + pubkey: credential_pubkey.clone(), + groups: vec!["replacement-group".to_string()], + expiry_unix: now + 600, + ..Default::default() + }, + network_secret, + )]; + service_impl + .synced_route_info + .peer_infos + .write() + .insert(updated_admin.peer_id, updated_admin); + + service_impl + .synced_route_info + .verify_and_update_credential_trusts(Some(network_secret)); + + let groups = service_impl.get_peer_groups(credential_peer_id); + assert!(groups.contains(&"proof-group".to_string())); + assert!(groups.contains(&"replacement-group".to_string())); + assert!(!groups.contains(&"cred-group".to_string())); + } + + #[tokio::test] + async fn remove_peers_batches_cleanup_and_version_increment() { + let service_impl = PeerRouteServiceImpl::new(1, get_mock_global_ctx()); + let removed_peer_ids = [41, 42]; + let retained_peer_id = 43; + + { + let mut peer_infos = service_impl.synced_route_info.peer_infos.write(); + let mut conn_map = service_impl.synced_route_info.conn_map.write(); + for peer_id in removed_peer_ids { + let mut info = RoutePeerInfo::new(); + info.peer_id = peer_id; + info.version = 1; + peer_infos.insert(peer_id, info); + conn_map.insert(peer_id, RouteConnInfo::default()); + } + + let mut retained_info = RoutePeerInfo::new(); + retained_info.peer_id = retained_peer_id; + retained_info.version = 1; + peer_infos.insert(retained_peer_id, retained_info); + conn_map.insert(retained_peer_id, RouteConnInfo::default()); + } + + for peer_id in removed_peer_ids { + service_impl.synced_route_info.raw_peer_infos.insert( + peer_id, + DynamicMessage::new(RoutePeerInfo::default().descriptor()), + ); + service_impl.synced_route_info.group_trust_map.insert( + peer_id, + HashMap::from([("guest".to_string(), vec![1, 2, 3])]), + ); + service_impl + .synced_route_info + .group_trust_map_cache + .insert(peer_id, Arc::new(vec!["guest".to_string()])); + service_impl.synced_route_info.foreign_network.insert( + ForeignNetworkRouteInfoKey { + peer_id, + ..Default::default() + }, + ForeignNetworkRouteInfoEntry::default(), + ); + } + + service_impl.synced_route_info.foreign_network.insert( + ForeignNetworkRouteInfoKey { + peer_id: retained_peer_id, + ..Default::default() + }, + ForeignNetworkRouteInfoEntry::default(), + ); + + let initial_version = service_impl.synced_route_info.version.get(); + service_impl + .synced_route_info + .remove_peers(removed_peer_ids); + + assert_eq!( + service_impl.synced_route_info.version.get(), + initial_version + 1 + ); + for peer_id in removed_peer_ids { + assert!( + !service_impl + .synced_route_info + .peer_infos + .read() + .contains_key(&peer_id) + ); + assert!( + !service_impl + .synced_route_info + .conn_map + .read() + .contains_key(&peer_id) + ); + assert!( + !service_impl + .synced_route_info + .raw_peer_infos + .contains_key(&peer_id) + ); + assert!( + !service_impl + .synced_route_info + .group_trust_map + .contains_key(&peer_id) + ); + assert!( + !service_impl + .synced_route_info + .group_trust_map_cache + .contains_key(&peer_id) + ); + assert!( + !service_impl.synced_route_info.foreign_network.contains_key( + &ForeignNetworkRouteInfoKey { + peer_id, + ..Default::default() + } + ) + ); + } + + assert!( + service_impl + .synced_route_info + .peer_infos + .read() + .contains_key(&retained_peer_id) + ); + assert!(service_impl.synced_route_info.foreign_network.contains_key( + &ForeignNetworkRouteInfoKey { + peer_id: retained_peer_id, + ..Default::default() + } + )); + } + #[tokio::test] async fn sync_route_info_marks_credential_sender_and_filters_entries() { let peer_mgr = create_mock_pmgr().await; @@ -4208,6 +4495,7 @@ mod tests { admin_info.trusted_credential_pubkeys = vec![TrustedCredentialPubkeyProof { credential: Some(TrustedCredentialPubkey { pubkey: credential_pubkey.clone(), + groups: vec!["guest".to_string()], expiry_unix: i64::MAX, ..Default::default() }), @@ -4237,6 +4525,11 @@ mod tests { .trusted_credential_pubkeys .contains_key(&credential_pubkey) ); + assert!( + service_impl + .get_peer_groups(credential_peer_id) + .contains(&"guest".to_string()) + ); service_impl.clear_expired_peer().await; @@ -4260,6 +4553,299 @@ mod tests { .read() .contains_key(&credential_peer_id) ); + assert!( + !service_impl + .synced_route_info + .group_trust_map_cache + .contains_key(&credential_peer_id) + ); + } + + #[tokio::test] + async fn refresh_acl_groups_returns_true_when_untrusted_peers_are_disconnected() { + let service_impl = PeerRouteServiceImpl::new(1, get_mock_global_ctx()); + let credential_peer_id: PeerId = 10061; + let credential_pubkey = vec![8u8; 32]; + let closed_peers = Arc::new(Mutex::new(Vec::new())); + + *service_impl.interface.lock().await = Some(Box::new(TrackingInterface { + my_peer_id: service_impl.my_peer_id, + closed_peers: closed_peers.clone(), + })); + + let mut credential_info = RoutePeerInfo::new(); + credential_info.peer_id = credential_peer_id; + credential_info.version = 1; + credential_info.noise_static_pubkey = credential_pubkey.clone(); + credential_info.feature_flag = Some(PeerFeatureFlag { + is_credential_peer: true, + ..Default::default() + }); + let self_info = RoutePeerInfo::new_updated_self( + service_impl.my_peer_id, + service_impl.my_peer_route_id, + &service_impl.global_ctx, + ); + let mut self_info = self_info; + self_info.version = 1; + self_info.last_update = Some(SystemTime::now().into()); + { + let mut guard = service_impl.synced_route_info.peer_infos.write(); + guard.insert(service_impl.my_peer_id, self_info); + guard.insert(credential_peer_id, credential_info); + } + service_impl + .synced_route_info + .trusted_credential_pubkeys + .insert( + credential_pubkey.clone(), + TrustedCredentialPubkey { + pubkey: credential_pubkey.clone(), + expiry_unix: i64::MAX, + ..Default::default() + }, + ); + + assert!(service_impl.refresh_acl_groups().await); + assert!(closed_peers.lock().contains(&credential_peer_id)); + assert!( + !service_impl + .synced_route_info + .peer_infos + .read() + .contains_key(&credential_peer_id) + ); + assert!( + !service_impl + .synced_route_info + .trusted_credential_pubkeys + .contains_key(&credential_pubkey) + ); + } + + #[tokio::test] + async fn refresh_acl_groups_updates_local_membership_immediately() { + let peer_mgr = create_mock_pmgr().await; + let route = create_mock_route(peer_mgr.clone()).await; + let my_peer_id = peer_mgr.my_peer_id(); + + assert!(route.service_impl.get_peer_groups(my_peer_id).is_empty()); + + peer_mgr.get_global_ctx().config.set_acl(Some(Acl { + acl_v1: Some(AclV1 { + group: Some(GroupInfo { + declares: vec![GroupIdentity { + group_name: "admin".to_string(), + group_secret: "admin-secret".to_string(), + }], + members: vec!["admin".to_string()], + }), + ..Default::default() + }), + })); + + route.refresh_acl_groups().await; + + let groups = route.service_impl.get_peer_groups(my_peer_id); + assert!(groups.contains(&"admin".to_string())); + assert_eq!(groups.len(), 1); + } + + #[tokio::test] + async fn refresh_acl_groups_revalidates_cached_remote_groups() { + let peer_mgr = create_mock_pmgr().await; + let route = create_mock_route(peer_mgr.clone()).await; + let remote_peer_id = 200; + let remote_group = PeerGroupInfo::generate_with_proof( + "ops".to_string(), + "secret-v1".to_string(), + remote_peer_id, + ); + + peer_mgr.get_global_ctx().config.set_acl(Some(Acl { + acl_v1: Some(AclV1 { + group: Some(GroupInfo { + declares: vec![GroupIdentity { + group_name: "ops".to_string(), + group_secret: "secret-v1".to_string(), + }], + members: vec![], + }), + ..Default::default() + }), + })); + + let mut remote_info = RoutePeerInfo::new(); + remote_info.peer_id = remote_peer_id; + remote_info.version = 1; + remote_info.groups = vec![remote_group]; + route + .service_impl + .synced_route_info + .peer_infos + .write() + .insert(remote_peer_id, remote_info.clone()); + route + .service_impl + .synced_route_info + .verify_and_update_group_trusts( + &[remote_info], + &peer_mgr.get_global_ctx().get_acl_group_declarations(), + false, + ); + + assert!( + route + .service_impl + .get_peer_groups(remote_peer_id) + .contains(&"ops".to_string()) + ); + + peer_mgr.get_global_ctx().config.set_acl(Some(Acl { + acl_v1: Some(AclV1 { + group: Some(GroupInfo { + declares: vec![GroupIdentity { + group_name: "ops".to_string(), + group_secret: "secret-v2".to_string(), + }], + members: vec![], + }), + ..Default::default() + }), + })); + + route.refresh_acl_groups().await; + + assert!( + route + .service_impl + .get_peer_groups(remote_peer_id) + .is_empty() + ); + } + + #[tokio::test] + async fn credential_verifier_trusts_admin_self_groups_from_multiple_admins() { + let service_impl = PeerRouteServiceImpl::new( + 1, + get_mock_global_ctx_with_network(Some( + crate::common::config::NetworkIdentity::new_credential("net1".to_string()), + )), + ); + + let mut admin_a = RoutePeerInfo::new(); + admin_a.peer_id = 501; + admin_a.version = 1; + admin_a.groups = vec![ + PeerGroupInfo { + group_name: "ops".to_string(), + group_proof: vec![1; 32], + }, + PeerGroupInfo { + group_name: "core-admin".to_string(), + group_proof: vec![2; 32], + }, + ]; + + let mut admin_b = RoutePeerInfo::new(); + admin_b.peer_id = 502; + admin_b.version = 1; + admin_b.groups = vec![PeerGroupInfo { + group_name: "audit".to_string(), + group_proof: vec![3; 32], + }]; + + service_impl + .synced_route_info + .verify_and_update_group_trusts(&[admin_a.clone(), admin_b.clone()], &[], true); + + let admin_a_groups = service_impl.get_peer_groups(admin_a.peer_id); + assert!(admin_a_groups.contains(&"ops".to_string())); + assert!(admin_a_groups.contains(&"core-admin".to_string())); + + let admin_b_groups = service_impl.get_peer_groups(admin_b.peer_id); + assert!(admin_b_groups.contains(&"audit".to_string())); + } + + #[tokio::test] + async fn credential_verifier_still_checks_credential_self_declared_groups() { + let service_impl = PeerRouteServiceImpl::new( + 1, + get_mock_global_ctx_with_network(Some( + crate::common::config::NetworkIdentity::new_credential("net1".to_string()), + )), + ); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + let credential_peer_id = 601; + let credential_pubkey = vec![9; 32]; + + let mut admin_info = RoutePeerInfo::new(); + admin_info.peer_id = 600; + admin_info.version = 1; + admin_info.trusted_credential_pubkeys = vec![TrustedCredentialPubkeyProof { + credential: Some(TrustedCredentialPubkey { + pubkey: credential_pubkey.clone(), + groups: vec!["cred-acl".to_string()], + expiry_unix: now + 600, + ..Default::default() + }), + credential_hmac: vec![7; 32], + }]; + + let mut credential_info = RoutePeerInfo::new(); + credential_info.peer_id = credential_peer_id; + credential_info.version = 1; + credential_info.noise_static_pubkey = credential_pubkey.clone(); + credential_info.feature_flag = Some(PeerFeatureFlag { + is_credential_peer: true, + ..Default::default() + }); + credential_info.groups = vec![ + PeerGroupInfo::generate_with_proof( + "proof-group".to_string(), + "proof-secret".to_string(), + credential_peer_id, + ), + PeerGroupInfo::generate_with_proof( + "invalid-group".to_string(), + "wrong-secret".to_string(), + credential_peer_id, + ), + ]; + + { + let mut guard = service_impl.synced_route_info.peer_infos.write(); + guard.insert(admin_info.peer_id, admin_info.clone()); + guard.insert(credential_info.peer_id, credential_info.clone()); + } + + service_impl + .synced_route_info + .verify_and_update_group_trusts( + &[admin_info, credential_info], + &[ + GroupIdentity { + group_name: "proof-group".to_string(), + group_secret: "proof-secret".to_string(), + }, + GroupIdentity { + group_name: "invalid-group".to_string(), + group_secret: "actual-secret".to_string(), + }, + ], + true, + ); + service_impl + .synced_route_info + .verify_and_update_credential_trusts(None); + + let groups = service_impl.get_peer_groups(credential_peer_id); + assert!(groups.contains(&"proof-group".to_string())); + assert!(groups.contains(&"cred-acl".to_string())); + assert!(!groups.contains(&"invalid-group".to_string())); } #[rstest::rstest] diff --git a/easytier/src/peers/peer_session.rs b/easytier/src/peers/peer_session.rs index 1fe813f6..ee6a947a 100644 --- a/easytier/src/peers/peer_session.rs +++ b/easytier/src/peers/peer_session.rs @@ -1,26 +1,14 @@ -use std::{ - sync::{ - Arc, Mutex, RwLock, - atomic::{AtomicBool, AtomicU32, Ordering}, - }, - time::{SystemTime, UNIX_EPOCH}, +use std::sync::{ + Arc, RwLock, + atomic::{AtomicBool, Ordering}, }; -use atomic_shim::AtomicU64; - -use crate::{ - common::PeerId, - peers::encrypt::{Encryptor, create_encryptor}, - tunnel::packet_def::{StandardAeadTail, ZCPacket}, -}; use anyhow::anyhow; use dashmap::DashMap; -use hmac::{Hmac, Mac as _}; -use rand::RngCore as _; -use sha2::Sha256; -use zerocopy::FromBytes; -type HmacSha256 = Hmac; +use super::secure_datagram::{SecureDatagramDirection, SecureDatagramSession}; +use crate::{common::PeerId, tunnel::packet_def::ZCPacket}; + pub struct UpsertResponderSessionReturn { pub session: Arc, pub action: PeerSessionAction, @@ -87,9 +75,6 @@ impl PeerSessionStore { self.sessions.insert(key, session); } - /// Remove sessions that are no longer referenced by any PeerConn or RelayPeerMap. - /// A session with strong_count == 1 means only the store holds it — no active - /// connection is using it, so it can be safely cleaned up. pub fn evict_unused_sessions(&self) { self.sessions .retain(|_key, session| Arc::strong_count(session) > 1); @@ -188,7 +173,6 @@ impl PeerSessionStore { } PeerSessionAction::Sync | PeerSessionAction::Create => { let root_key = root_key_32.ok_or_else(|| anyhow!("missing root_key"))?; - // If the existing session is invalidated, remove it so we create a fresh one if let Some(existing) = self.sessions.get(key) && !existing.is_valid() { @@ -224,253 +208,25 @@ impl PeerSessionStore { } } -#[derive(Clone, Default)] -struct EpochKeySlot { - epoch: u32, - generation: u32, - valid: bool, - send_cipher: Option>, - recv_cipher: Option>, -} - -impl std::fmt::Debug for EpochKeySlot { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("EpochKeySlot") - .field("epoch", &self.epoch) - .field("generation", &self.generation) - .field("valid", &self.valid) - .finish() - } -} - -impl EpochKeySlot { - fn get_encryptor(&self, is_send: bool) -> Arc { - if is_send { - self.send_cipher.as_ref().unwrap().clone() - } else { - self.recv_cipher.as_ref().unwrap().clone() - } - } -} - -#[derive(Debug, Clone, Copy, Default)] -struct ReplayWindow256 { - max_seq: u64, - bitmap: [u8; 32], - valid: bool, -} - -impl ReplayWindow256 { - fn clear(&mut self) { - self.max_seq = 0; - self.bitmap.fill(0); - self.valid = false; - } - - fn test_bit(&self, idx: usize) -> bool { - let byte = idx / 8; - let bit = idx % 8; - (self.bitmap[byte] >> bit) & 1 == 1 - } - - fn set_bit(&mut self, idx: usize) { - let byte = idx / 8; - let bit = idx % 8; - self.bitmap[byte] |= 1u8 << bit; - } - - fn shift_right(&mut self, shift: usize) { - if shift == 0 { - return; - } - let total_bits = 256usize; - if shift >= total_bits { - self.bitmap.fill(0); - return; - } - - let byte_shift = shift / 8; - let bit_shift = shift % 8; - - if byte_shift > 0 { - for i in (0..self.bitmap.len()).rev() { - self.bitmap[i] = if i >= byte_shift { - self.bitmap[i - byte_shift] - } else { - 0 - }; - } - } - - if bit_shift > 0 { - let mut carry = 0u8; - for b in self.bitmap.iter_mut() { - let new_carry = *b >> (8 - bit_shift); - *b = (*b << bit_shift) | carry; - carry = new_carry; - } - } - } - - fn accept(&mut self, seq: u64) -> bool { - if !self.valid { - self.valid = true; - self.max_seq = seq; - self.set_bit(0); - return true; - } - - if seq > self.max_seq { - let shift = (seq - self.max_seq) as usize; - self.shift_right(shift); - self.max_seq = seq; - self.set_bit(0); - return true; - } - - let delta = (self.max_seq - seq) as usize; - if delta >= 256 { - return false; - } - if self.test_bit(delta) { - return false; - } - self.set_bit(delta); - true - } -} - -#[derive(Debug, Clone, Copy, Default)] -struct EpochRxSlot { - epoch: u32, - window: ReplayWindow256, - last_rx_ms: u64, - valid: bool, -} - -impl EpochRxSlot { - fn clear(&mut self) { - self.epoch = 0; - self.window.clear(); - self.last_rx_ms = 0; - self.valid = false; - } -} - -#[derive(Debug, Clone, Copy, Default)] -struct SyncRxGrace { - slots: [[EpochRxSlot; 2]; 2], - expires_at_ms: u64, - valid: bool, -} - -impl SyncRxGrace { - fn clear(&mut self) { - self.slots = [[EpochRxSlot::default(), EpochRxSlot::default()]; 2]; - self.expires_at_ms = 0; - self.valid = false; - } - - fn refresh(&mut self, slots: [[EpochRxSlot; 2]; 2], expires_at_ms: u64) { - self.slots = slots; - self.expires_at_ms = expires_at_ms; - self.valid = true; - } - - fn maybe_expire(&mut self, now_ms: u64) { - if self.valid && now_ms >= self.expires_at_ms { - self.clear(); - } - } -} - pub struct PeerSession { peer_id: PeerId, - root_key: RwLock<[u8; 32]>, - session_generation: AtomicU32, peer_static_pubkey: RwLock>, - - send_epoch: AtomicU32, - send_seq: [AtomicU64; 2], - send_epoch_started_ms: AtomicU64, - send_packets_since_epoch: AtomicU64, - - rx_slots: Mutex<[[EpochRxSlot; 2]; 2]>, - key_cache: Mutex<[[EpochKeySlot; 2]; 2]>, - sync_rx_grace: Mutex, - sync_rx_grace_expires_at_ms: AtomicU64, - - send_cipher_algorithm: String, - recv_cipher_algorithm: String, - - /// Set to true when the session is detected as corrupted (persistent decrypt failures). - /// Holders of Arc can check this to know the session should be discarded. + datagram: SecureDatagramSession, invalidated: AtomicBool, - /// Consecutive decrypt failure counter. Auto-invalidates when threshold is reached. - decrypt_fail_count: AtomicU32, } impl std::fmt::Debug for PeerSession { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("PeerSession") .field("peer_id", &self.peer_id) - .field("root_key", &self.root_key) - .field("session_generation", &self.session_generation) .field("peer_static_pubkey", &self.peer_static_pubkey) - .field("send_epoch", &self.send_epoch) - .field("send_seq", &self.send_seq) - .field("send_epoch_started_ms", &self.send_epoch_started_ms) - .field("send_packets_since_epoch", &self.send_packets_since_epoch) - .field("rx_slots", &self.rx_slots) - .field("key_cache", &self.key_cache) - .field("sync_rx_grace", &self.sync_rx_grace) - .field( - "sync_rx_grace_expires_at_ms", - &self.sync_rx_grace_expires_at_ms, - ) - .field("send_cipher_algorithm", &self.send_cipher_algorithm) - .field("recv_cipher_algorithm", &self.recv_cipher_algorithm) + .field("datagram", &self.datagram) .finish() } } impl PeerSession { - /// Idle-eviction timeout for receive slots, in milliseconds. - /// - /// If no packets are received for this period (~30 seconds), the - /// corresponding RX slot is considered idle and may be cleared/reused. - /// This helps reclaim state for dead peers or paths while still tolerating - /// short network stalls. Environments with very bursty or high-latency - /// traffic may want to increase this value; low-latency or tightly - /// resource-constrained deployments may lower it. - const EVICT_IDLE_AFTER_MS: u64 = 30_000; - /// Keep the pre-sync receive windows alive briefly so in-flight packets - /// from the previous epochs are still accepted after a shared session is - /// synced in place by another connection. - const SYNC_RX_GRACE_AFTER_MS: u64 = 5_000; - - /// Maximum number of packets to send in a single epoch before forcing - /// a key/epoch rotation. - /// - /// This bounds the amount of traffic protected under a single set of - /// derived keys, which is a common best practice for long-lived secure - /// channels. The current value (~1 million packets) is a conservative - /// default chosen to balance security (more frequent rotation) and - /// performance (avoiding excessive rekeying). Deployments with very high - /// or very low packet rates may tune this threshold accordingly. - const ROTATE_AFTER_PACKETS: u64 = 1_000_000; - - /// Maximum wall-clock lifetime of a send epoch, in milliseconds. - /// - /// Even if the packet-based limit is not reached, epochs are rotated - /// after this duration (~10 minutes) to avoid long-lived keys and keep - /// replay windows bounded in time. This also limits the impact of a - /// compromised key. Installations that prioritize lower overhead over - /// more aggressive key rotation may increase this value; those with - /// stricter security requirements may decrease it. - const ROTATE_AFTER_MS: u64 = 10 * 60 * 1000; - const MAX_ACCEPTED_RX_EPOCH_AHEAD: u32 = 3; - const DECRYPT_FAIL_THRESHOLD: u32 = 10; + const SYNC_RX_GRACE_AFTER_MS: u64 = SecureDatagramSession::SYNC_RX_GRACE_AFTER_MS; pub fn new( peer_id: PeerId, @@ -481,32 +237,17 @@ impl PeerSession { recv_cipher_algorithm: String, peer_static_pubkey: Option<[u8; 32]>, ) -> Self { - let rx_slots = [ - [EpochRxSlot::default(), EpochRxSlot::default()], - [EpochRxSlot::default(), EpochRxSlot::default()], - ]; - let key_cache = [ - [EpochKeySlot::default(), EpochKeySlot::default()], - [EpochKeySlot::default(), EpochKeySlot::default()], - ]; - let now_ms = now_ms(); Self { peer_id, - root_key: RwLock::new(root_key), - session_generation: AtomicU32::new(session_generation), peer_static_pubkey: RwLock::new(peer_static_pubkey), - send_epoch: AtomicU32::new(initial_epoch), - send_seq: [AtomicU64::new(0), AtomicU64::new(0)], - send_epoch_started_ms: AtomicU64::new(now_ms), - send_packets_since_epoch: AtomicU64::new(0), - rx_slots: Mutex::new(rx_slots), - key_cache: Mutex::new(key_cache), - sync_rx_grace: Mutex::new(SyncRxGrace::default()), - sync_rx_grace_expires_at_ms: AtomicU64::new(0), - send_cipher_algorithm, - recv_cipher_algorithm, + datagram: SecureDatagramSession::new( + root_key, + session_generation, + initial_epoch, + send_cipher_algorithm, + recv_cipher_algorithm, + ), invalidated: AtomicBool::new(false), - decrypt_fail_count: AtomicU32::new(0), } } @@ -514,44 +255,29 @@ impl PeerSession { self.peer_id } - /// Mark this session as invalid. All holders of Arc will see this. pub fn invalidate(&self) { self.invalidated.store(true, Ordering::Relaxed); + self.datagram.invalidate(); } pub fn is_valid(&self) -> bool { - !self.invalidated.load(Ordering::Relaxed) + !self.invalidated.load(Ordering::Relaxed) && self.datagram.is_valid() } pub fn session_generation(&self) -> u32 { - self.session_generation.load(Ordering::Relaxed) + self.datagram.session_generation() } pub fn root_key(&self) -> [u8; 32] { - *self.root_key.read().unwrap() + self.datagram.root_key() } pub fn new_root_key() -> [u8; 32] { - let mut out = [0u8; 32]; - rand::rngs::OsRng.fill_bytes(&mut out); - out + SecureDatagramSession::new_root_key() } pub fn next_sync_epoch(&self) -> u32 { - let send_epoch = self.send_epoch.load(Ordering::Relaxed); - let rx = self.rx_slots.lock().unwrap(); - let mut max_epoch = send_epoch; - for dir in 0..2 { - let cur = rx[dir][0]; - if cur.valid { - max_epoch = max_epoch.max(cur.epoch); - } - let prev = rx[dir][1]; - if prev.valid { - max_epoch = max_epoch.max(prev.epoch); - } - } - max_epoch.wrapping_add(1) + self.datagram.next_sync_epoch() } pub fn check_encrypt_algo_same( @@ -559,12 +285,8 @@ impl PeerSession { send_algorithm: &str, recv_algorithm: &str, ) -> Result<(), anyhow::Error> { - if self.send_cipher_algorithm != send_algorithm - || self.recv_cipher_algorithm != recv_algorithm - { - return Err(anyhow!("encrypt algorithm not same")); - } - Ok(()) + self.datagram + .check_encrypt_algo_same(send_algorithm, recv_algorithm) } pub fn check_or_set_peer_static_pubkey( @@ -592,277 +314,25 @@ impl PeerSession { initial_epoch: u32, preserve_rx_grace: bool, ) { - let old_root_key = self.root_key(); - let can_preserve_rx_grace = preserve_rx_grace && old_root_key == root_key; - { - let mut g = self.root_key.write().unwrap(); - *g = root_key; - } - self.session_generation - .store(session_generation, Ordering::Relaxed); - - self.send_epoch.store(initial_epoch, Ordering::Relaxed); - self.send_seq[0].store(0, Ordering::Relaxed); - self.send_seq[1].store(0, Ordering::Relaxed); - self.send_epoch_started_ms - .store(now_ms(), Ordering::Relaxed); - self.send_packets_since_epoch.store(0, Ordering::Relaxed); - - { - let mut rx = self.rx_slots.lock().unwrap(); - let mut sync_rx_grace = self.sync_rx_grace.lock().unwrap(); - if can_preserve_rx_grace { - let expires_at_ms = now_ms().saturating_add(Self::SYNC_RX_GRACE_AFTER_MS); - sync_rx_grace.refresh(*rx, expires_at_ms); - self.sync_rx_grace_expires_at_ms - .store(expires_at_ms, Ordering::Relaxed); - } else { - sync_rx_grace.clear(); - self.sync_rx_grace_expires_at_ms.store(0, Ordering::Relaxed); - } - for dir in 0..2 { - rx[dir][0].clear(); - rx[dir][1].clear(); - } - } - - self.key_cache - .lock() - .unwrap() - .fill([EpochKeySlot::default(), EpochKeySlot::default()]); + self.datagram.sync_root_key( + root_key, + session_generation, + initial_epoch, + preserve_rx_grace, + ); } - pub fn dir_for_sender(sender_peer_id: PeerId, receiver_peer_id: PeerId) -> usize { + pub fn dir_for_sender( + sender_peer_id: PeerId, + receiver_peer_id: PeerId, + ) -> SecureDatagramDirection { if sender_peer_id < receiver_peer_id { - 0 + SecureDatagramDirection::AToB } else { - 1 + SecureDatagramDirection::BToA } } - fn hkdf_traffic_key(&self, epoch: u32, dir: usize) -> [u8; 32] { - let root_key = self.root_key(); - let salt = [0u8; 32]; - let mut extract = HmacSha256::new_from_slice(&salt).unwrap(); - extract.update(&root_key); - let prk = extract.finalize().into_bytes(); - - let mut info = Vec::with_capacity(9 + 4 + 1); - info.extend_from_slice(b"et-traffic"); - info.extend_from_slice(&epoch.to_be_bytes()); - info.push(dir as u8); - - let mut expand = HmacSha256::new_from_slice(&prk).unwrap(); - expand.update(&info); - expand.update(&[1u8]); - let okm = expand.finalize().into_bytes(); - let mut key = [0u8; 32]; - key.copy_from_slice(&okm[..32]); - key - } - - fn get_or_create_encryptor( - &self, - epoch: u32, - dir: usize, - generation: u32, - is_send: bool, - ) -> Arc { - let mut guard = self.key_cache.lock().unwrap(); - for slot in guard[dir].iter_mut() { - if slot.valid && slot.epoch == epoch && slot.generation == generation { - return slot.get_encryptor(is_send); - } - } - - let key = self.hkdf_traffic_key(epoch, dir); - let mut key_128 = [0u8; 16]; - key_128.copy_from_slice(&key[..16]); - - let slot = EpochKeySlot { - epoch, - generation, - valid: true, - send_cipher: Some(create_encryptor(&self.send_cipher_algorithm, key_128, key)), - recv_cipher: Some(create_encryptor(&self.recv_cipher_algorithm, key_128, key)), - }; - let ret = slot.get_encryptor(is_send); - - if !guard[dir][0].valid || guard[dir][0].epoch == epoch { - guard[dir][0] = slot; - } else { - guard[dir][1] = slot; - } - - ret - } - - fn maybe_rotate_epoch(&self, now_ms: u64) { - let packets = self - .send_packets_since_epoch - .fetch_add(1, Ordering::Relaxed) - + 1; - let started = self.send_epoch_started_ms.load(Ordering::Relaxed); - if packets < Self::ROTATE_AFTER_PACKETS - && now_ms.saturating_sub(started) < Self::ROTATE_AFTER_MS - { - return; - } - - let cur = self.send_epoch.load(Ordering::Relaxed); - let next = cur.wrapping_add(1); - if self - .send_epoch - .compare_exchange(cur, next, Ordering::Relaxed, Ordering::Relaxed) - .is_ok() - { - self.send_epoch_started_ms.store(now_ms, Ordering::Relaxed); - self.send_packets_since_epoch.store(0, Ordering::Relaxed); - } - } - - fn next_nonce(&self, dir: usize) -> (u32, u64, [u8; 12]) { - let now_ms = now_ms(); - self.maybe_rotate_epoch(now_ms); - let epoch = self.send_epoch.load(Ordering::Relaxed); - let seq = self.send_seq[dir].fetch_add(1, Ordering::Relaxed); - let mut nonce = [0u8; 12]; - nonce[..4].copy_from_slice(&epoch.to_be_bytes()); - nonce[4..].copy_from_slice(&seq.to_be_bytes()); - (epoch, seq, nonce) - } - - fn parse_tail(payload: &[u8]) -> Option<[u8; 12]> { - let tail = StandardAeadTail::ref_from_suffix(payload)?; - Some(tail.nonce) - } - - fn evict_old_rx_slots(rx: &mut [[EpochRxSlot; 2]; 2], now_ms: u64) { - for dir_slots in rx.iter_mut() { - for slot in dir_slots.iter_mut() { - if !slot.valid { - continue; - } - let last = slot.last_rx_ms; - if last != 0 && now_ms.saturating_sub(last) > Self::EVICT_IDLE_AFTER_MS { - slot.clear(); - } - } - } - } - - fn epoch_in_slots(slots: &[EpochRxSlot; 2], epoch: u32) -> bool { - slots[0].valid && slots[0].epoch == epoch || slots[1].valid && slots[1].epoch == epoch - } - - fn sync_rx_grace_active(&self, now_ms: u64) -> bool { - let expires_at_ms = self.sync_rx_grace_expires_at_ms.load(Ordering::Relaxed); - if expires_at_ms == 0 { - return false; - } - if now_ms < expires_at_ms { - return true; - } - self.sync_rx_grace_expires_at_ms.store(0, Ordering::Relaxed); - false - } - - fn check_replay(&self, epoch: u32, seq: u64, dir: usize, now_ms: u64) -> bool { - let mut rx = self.rx_slots.lock().unwrap(); - Self::evict_old_rx_slots(&mut rx, now_ms); - let mut sync_rx_grace = if self.sync_rx_grace_active(now_ms) { - let mut sync_rx_grace = self.sync_rx_grace.lock().unwrap(); - sync_rx_grace.maybe_expire(now_ms); - if sync_rx_grace.valid { - Self::evict_old_rx_slots(&mut sync_rx_grace.slots, now_ms); - Some(sync_rx_grace) - } else { - self.sync_rx_grace_expires_at_ms.store(0, Ordering::Relaxed); - None - } - } else { - None - }; - let send_epoch = self.send_epoch.load(Ordering::Relaxed); - { - let mut key_cache = self.key_cache.lock().unwrap(); - for d in 0..2 { - for s in 0..2 { - if !key_cache[d][s].valid { - continue; - } - let e = key_cache[d][s].epoch; - let allowed = e == send_epoch - || rx[d][0].valid && rx[d][0].epoch == e - || rx[d][1].valid && rx[d][1].epoch == e - || sync_rx_grace - .as_ref() - .is_some_and(|g| Self::epoch_in_slots(&g.slots[d], e)); - if !allowed { - key_cache[d][s].valid = false; - } - } - } - } - - if sync_rx_grace - .as_ref() - .is_some_and(|g| Self::epoch_in_slots(&g.slots[dir], epoch)) - { - for slot in sync_rx_grace.as_mut().unwrap().slots[dir].iter_mut() { - if slot.valid && slot.epoch == epoch { - slot.last_rx_ms = now_ms; - return slot.window.accept(seq); - } - } - } - - if !rx[dir][0].valid { - rx[dir][0] = EpochRxSlot { - epoch, - window: ReplayWindow256::default(), - last_rx_ms: now_ms, - valid: true, - }; - } - - if rx[dir][0].valid && epoch == rx[dir][0].epoch { - rx[dir][0].last_rx_ms = now_ms; - return rx[dir][0].window.accept(seq); - } - - if rx[dir][1].valid && epoch == rx[dir][1].epoch { - rx[dir][1].last_rx_ms = now_ms; - return rx[dir][1].window.accept(seq); - } - - if rx[dir][0].valid && epoch > rx[dir][0].epoch { - let mut baseline_epoch = send_epoch; - if rx[dir][0].valid { - baseline_epoch = baseline_epoch.max(rx[dir][0].epoch); - } - if rx[dir][1].valid { - baseline_epoch = baseline_epoch.max(rx[dir][1].epoch); - } - let max_allowed_epoch = - baseline_epoch.saturating_add(Self::MAX_ACCEPTED_RX_EPOCH_AHEAD); - if epoch > max_allowed_epoch { - return false; - } - - rx[dir][1] = rx[dir][0]; - rx[dir][0] = EpochRxSlot { - epoch, - window: ReplayWindow256::default(), - last_rx_ms: now_ms, - valid: true, - }; - return rx[dir][0].window.accept(seq); - } - - false - } - pub fn encrypt_payload( &self, sender_peer_id: PeerId, @@ -872,19 +342,8 @@ impl PeerSession { if !self.is_valid() { return Err(anyhow!("session invalidated")); } - let dir = Self::dir_for_sender(sender_peer_id, receiver_peer_id); - let (epoch, _seq, nonce_bytes) = self.next_nonce(dir); - let encryptor = self.get_or_create_encryptor(epoch, dir, self.session_generation(), true); - if let Err(e) = encryptor.encrypt_with_nonce(pkt, Some(nonce_bytes.as_slice())) { - tracing::warn!( - peer_id = ?self.peer_id, - ?e, - "session encrypt failed, invalidating" - ); - self.invalidate(); - return Err(e.into()); - } - Ok(()) + self.datagram + .encrypt_payload(Self::dir_for_sender(sender_peer_id, receiver_peer_id), pkt) } pub fn decrypt_payload( @@ -896,47 +355,13 @@ impl PeerSession { if !self.is_valid() { return Err(anyhow!("session invalidated")); } - let dir = Self::dir_for_sender(sender_peer_id, receiver_peer_id); - let nonce_bytes = - Self::parse_tail(ciphertext_with_tail.payload()).ok_or_else(|| anyhow!("no tail"))?; - let epoch = u32::from_be_bytes(nonce_bytes[..4].try_into().unwrap()); - let seq = u64::from_be_bytes(nonce_bytes[4..].try_into().unwrap()); - - let now_ms = now_ms(); - if !self.check_replay(epoch, seq, dir, now_ms) { - return Err(anyhow!( - "replay rejected, sender_peer_id: {:?}, receiver_peer_id: {:?}", - sender_peer_id, - receiver_peer_id - )); - } - - let encryptor = self.get_or_create_encryptor(epoch, dir, self.session_generation(), false); - if let Err(e) = encryptor.decrypt(ciphertext_with_tail) { - let count = self.decrypt_fail_count.fetch_add(1, Ordering::Relaxed) + 1; - if count >= Self::DECRYPT_FAIL_THRESHOLD { - self.invalidate(); - tracing::warn!( - peer_id = ?self.peer_id, - count, - "session auto-invalidated after consecutive decrypt failures" - ); - } - return Err(e.into()); - } - self.decrypt_fail_count.store(0, Ordering::Relaxed); - - Ok(()) + self.datagram.decrypt_payload( + Self::dir_for_sender(sender_peer_id, receiver_peer_id), + ciphertext_with_tail, + ) } } -fn now_ms() -> u64 { - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_millis() as u64 -} - #[cfg(test)] mod tests { use super::*; @@ -984,177 +409,10 @@ mod tests { } #[test] - fn replay_rejects_far_future_epoch_without_poisoning_window() { - let peer_id: PeerId = 10; - let root_key = PeerSession::new_root_key(); - let generation = 1u32; - let initial_epoch = 0u32; - let s = PeerSession::new( - peer_id, - root_key, - generation, - initial_epoch, - "aes-256-gcm".to_string(), - "aes-256-gcm".to_string(), - None, - ); - - let now = now_ms(); - - assert!(s.check_replay(0, 1, 0, now)); - assert!(s.check_replay(0, 2, 0, now)); - - assert!(!s.check_replay(1000, 1, 0, now)); - - assert!(s.check_replay(1, 1, 0, now + 1)); - assert!(s.check_replay(1, 2, 0, now + 2)); - } - - #[test] - fn replay_window_shift_preserves_bits() { - let mut w = ReplayWindow256::default(); - // Accept seqs 0..10 - for i in 0..10u64 { - assert!(w.accept(i), "seq {i} should be accepted"); - } - assert_eq!(w.max_seq, 9); - - // All seqs 0..10 should be marked as seen (replay) - for i in 0..10u64 { - assert!(!w.accept(i), "seq {i} should be rejected as replay"); - } - - // Seq 10 should still be accepted - assert!(w.accept(10)); - } - - #[test] - fn replay_window_out_of_order_within_window() { - let mut w = ReplayWindow256::default(); - // Accept even seqs 0,2,4,...,20 - for i in (0..=20u64).step_by(2) { - assert!(w.accept(i), "seq {i} should be accepted"); - } - // Now accept odd seqs 1,3,5,...,19 (out of order, within window) - for i in (1..=19u64).step_by(2) { - assert!(w.accept(i), "seq {i} should be accepted (out of order)"); - } - // All seqs 0..=20 should now be marked as seen - for i in 0..=20u64 { - assert!(!w.accept(i), "seq {i} should be rejected as replay"); - } - } - - #[test] - fn sync_root_key_allows_any_epoch_from_remote() { - // After sync_root_key, the remote peer may still be sending at an - // old epoch. The receiver should accept those packets. - let peer_id: PeerId = 10; - let root_key = PeerSession::new_root_key(); - let s = PeerSession::new( - peer_id, - root_key, - 1, - 0, - "aes-256-gcm".to_string(), - "aes-256-gcm".to_string(), - None, - ); - - // Simulate receiving some packets at epoch 0 - let now = now_ms(); - assert!(s.check_replay(0, 0, 0, now)); - assert!(s.check_replay(0, 1, 0, now)); - - // Sync with initial_epoch=2 (simulating a Sync action) - s.sync_root_key(root_key, 2, 2, true); - - // Remote peer is still sending at epoch 0 — should be accepted - // (rx_slots were cleared, so the first packet establishes the epoch) - assert!( - s.check_replay(0, 10, 0, now + 1), - "packets at old epoch should be accepted after sync" - ); - } - - #[test] - fn sync_root_key_keeps_previous_epochs_during_grace_window() { - let peer_id: PeerId = 10; - let root_key = PeerSession::new_root_key(); - let s = PeerSession::new( - peer_id, - root_key, - 1, - 0, - "aes-256-gcm".to_string(), - "aes-256-gcm".to_string(), - None, - ); - - let now = now_ms(); - assert!(s.check_replay(0, 0, 0, now)); - assert!(s.check_replay(1, 0, 0, now + 1)); - - s.sync_root_key(root_key, 2, 2, true); - - // The first packet after sync may already use the new epoch. - assert!(s.check_replay(2, 0, 0, now + 2)); - // Older in-flight packets from pre-sync epochs should still be accepted - // during the grace period, regardless of arrival order. - assert!(s.check_replay(1, 1, 0, now + 3)); - assert!(s.check_replay(0, 1, 0, now + 4)); - } - - #[test] - fn sync_root_key_expires_previous_epochs_after_grace_window() { - let peer_id: PeerId = 10; - let root_key = PeerSession::new_root_key(); - let s = PeerSession::new( - peer_id, - root_key, - 1, - 0, - "aes-256-gcm".to_string(), - "aes-256-gcm".to_string(), - None, - ); - - let now = now_ms(); - assert!(s.check_replay(0, 0, 0, now)); - assert!(s.check_replay(1, 0, 0, now + 1)); - - s.sync_root_key(root_key, 2, 2, true); - assert!(s.check_replay(2, 0, 0, now + 2)); - - assert!( - !s.check_replay(0, 1, 0, now + PeerSession::SYNC_RX_GRACE_AFTER_MS + 3), - "old epochs should stop being accepted once the sync grace window expires" - ); - } - - #[test] - fn sync_root_key_does_not_preserve_previous_epochs_when_root_key_changes() { - let peer_id: PeerId = 10; - let root_key = PeerSession::new_root_key(); - let s = PeerSession::new( - peer_id, - root_key, - 1, - 0, - "aes-256-gcm".to_string(), - "aes-256-gcm".to_string(), - None, - ); - - let now = now_ms(); - assert!(s.check_replay(0, 0, 0, now)); - assert!(s.check_replay(1, 0, 0, now + 1)); - - s.sync_root_key(PeerSession::new_root_key(), 2, 2, true); - assert!(s.check_replay(2, 0, 0, now + 2)); - assert!( - !s.check_replay(1, 1, 0, now + 3), - "old epochs should not be preserved when sync replaces the root key" + fn sync_root_key_preserves_generic_grace_window_constant() { + assert_eq!( + PeerSession::SYNC_RX_GRACE_AFTER_MS, + SecureDatagramSession::SYNC_RX_GRACE_AFTER_MS ); } } diff --git a/easytier/src/peers/route_trait.rs b/easytier/src/peers/route_trait.rs index fdddc6bf..34080133 100644 --- a/easytier/src/peers/route_trait.rs +++ b/easytier/src/peers/route_trait.rs @@ -141,6 +141,8 @@ pub trait Route { fn get_peer_groups(&self, peer_id: PeerId) -> Arc>; + async fn refresh_acl_groups(&self) {} + async fn get_peer_groups_by_ip(&self, ip: &std::net::IpAddr) -> Arc> { match self.get_peer_id_by_ip(ip).await { Some(peer_id) => self.get_peer_groups(peer_id), diff --git a/easytier/src/peers/secure_datagram.rs b/easytier/src/peers/secure_datagram.rs new file mode 100644 index 00000000..f4722e44 --- /dev/null +++ b/easytier/src/peers/secure_datagram.rs @@ -0,0 +1,1020 @@ +use std::{ + sync::{ + Arc, Mutex, RwLock, + atomic::{AtomicBool, AtomicU32, Ordering}, + }, + time::{SystemTime, UNIX_EPOCH}, +}; + +use anyhow::anyhow; +use atomic_shim::AtomicU64; +use hmac::{Hmac, Mac as _}; +use rand::RngCore as _; +use sha2::Sha256; +use zerocopy::FromBytes; + +use crate::{ + peers::encrypt::{Encryptor, create_encryptor}, + tunnel::packet_def::{StandardAeadTail, ZCPacket}, +}; + +type HmacSha256 = Hmac; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SecureDatagramDirection { + AToB, + BToA, +} + +impl SecureDatagramDirection { + fn idx(self) -> usize { + match self { + Self::AToB => 0, + Self::BToA => 1, + } + } +} + +#[derive(Clone, Default)] +struct EpochKeySlot { + epoch: u32, + generation: u32, + valid: bool, + send_cipher: Option>, + recv_cipher: Option>, +} + +impl std::fmt::Debug for EpochKeySlot { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EpochKeySlot") + .field("epoch", &self.epoch) + .field("generation", &self.generation) + .field("valid", &self.valid) + .finish() + } +} + +impl EpochKeySlot { + fn get_encryptor(&self, is_send: bool) -> Arc { + if is_send { + self.send_cipher.as_ref().unwrap().clone() + } else { + self.recv_cipher.as_ref().unwrap().clone() + } + } +} + +#[derive(Debug, Clone, Copy, Default)] +struct ReplayWindow256 { + max_seq: u64, + bitmap: [u8; 32], + valid: bool, +} + +impl ReplayWindow256 { + fn clear(&mut self) { + self.max_seq = 0; + self.bitmap.fill(0); + self.valid = false; + } + + fn test_bit(&self, idx: usize) -> bool { + let byte = idx / 8; + let bit = idx % 8; + (self.bitmap[byte] >> bit) & 1 == 1 + } + + fn set_bit(&mut self, idx: usize) { + let byte = idx / 8; + let bit = idx % 8; + self.bitmap[byte] |= 1u8 << bit; + } + + fn shift_right(&mut self, shift: usize) { + if shift == 0 { + return; + } + let total_bits = 256usize; + if shift >= total_bits { + self.bitmap.fill(0); + return; + } + + let byte_shift = shift / 8; + let bit_shift = shift % 8; + + if byte_shift > 0 { + for i in (0..self.bitmap.len()).rev() { + self.bitmap[i] = if i >= byte_shift { + self.bitmap[i - byte_shift] + } else { + 0 + }; + } + } + + if bit_shift > 0 { + let mut carry = 0u8; + for b in self.bitmap.iter_mut() { + let new_carry = *b >> (8 - bit_shift); + *b = (*b << bit_shift) | carry; + carry = new_carry; + } + } + } + + fn accept(&mut self, seq: u64) -> bool { + if !self.valid { + self.valid = true; + self.max_seq = seq; + self.set_bit(0); + return true; + } + + if seq > self.max_seq { + let shift = (seq - self.max_seq) as usize; + self.shift_right(shift); + self.max_seq = seq; + self.set_bit(0); + return true; + } + + let delta = (self.max_seq - seq) as usize; + if delta >= 256 { + return false; + } + if self.test_bit(delta) { + return false; + } + self.set_bit(delta); + true + } + + fn can_accept(&self, seq: u64) -> bool { + if !self.valid || seq > self.max_seq { + return true; + } + + let delta = (self.max_seq - seq) as usize; + delta < 256 && !self.test_bit(delta) + } +} + +#[derive(Debug, Clone, Copy, Default)] +struct EpochRxSlot { + epoch: u32, + window: ReplayWindow256, + last_rx_ms: u64, + valid: bool, +} + +impl EpochRxSlot { + fn clear(&mut self) { + self.epoch = 0; + self.window.clear(); + self.last_rx_ms = 0; + self.valid = false; + } +} + +#[derive(Debug, Clone, Copy, Default)] +struct SyncRxGrace { + slots: [[EpochRxSlot; 2]; 2], + expires_at_ms: u64, + valid: bool, +} + +impl SyncRxGrace { + fn clear(&mut self) { + self.slots = [[EpochRxSlot::default(), EpochRxSlot::default()]; 2]; + self.expires_at_ms = 0; + self.valid = false; + } + + fn refresh(&mut self, slots: [[EpochRxSlot; 2]; 2], expires_at_ms: u64) { + self.slots = slots; + self.expires_at_ms = expires_at_ms; + self.valid = true; + } + + fn maybe_expire(&mut self, now_ms: u64) { + if self.valid && now_ms >= self.expires_at_ms { + self.clear(); + } + } +} + +pub struct SecureDatagramSession { + root_key: RwLock<[u8; 32]>, + session_generation: AtomicU32, + + send_epoch: AtomicU32, + send_seq: [AtomicU64; 2], + send_epoch_started_ms: AtomicU64, + send_packets_since_epoch: AtomicU64, + + rx_slots: Mutex<[[EpochRxSlot; 2]; 2]>, + key_cache: Mutex<[[EpochKeySlot; 2]; 2]>, + sync_rx_grace: Mutex, + sync_rx_grace_expires_at_ms: AtomicU64, + + send_cipher_algorithm: String, + recv_cipher_algorithm: String, + + invalidated: AtomicBool, + decrypt_fail_count: AtomicU32, +} + +impl std::fmt::Debug for SecureDatagramSession { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SecureDatagramSession") + .field("root_key", &self.root_key) + .field("session_generation", &self.session_generation) + .field("send_epoch", &self.send_epoch) + .field("send_seq", &self.send_seq) + .field("send_epoch_started_ms", &self.send_epoch_started_ms) + .field("send_packets_since_epoch", &self.send_packets_since_epoch) + .field("rx_slots", &self.rx_slots) + .field("key_cache", &self.key_cache) + .field("sync_rx_grace", &self.sync_rx_grace) + .field( + "sync_rx_grace_expires_at_ms", + &self.sync_rx_grace_expires_at_ms, + ) + .field("send_cipher_algorithm", &self.send_cipher_algorithm) + .field("recv_cipher_algorithm", &self.recv_cipher_algorithm) + .finish() + } +} + +impl SecureDatagramSession { + const EVICT_IDLE_AFTER_MS: u64 = 30_000; + pub(crate) const SYNC_RX_GRACE_AFTER_MS: u64 = 5_000; + const ROTATE_AFTER_PACKETS: u64 = 1_000_000; + const ROTATE_AFTER_MS: u64 = 10 * 60 * 1000; + const MAX_ACCEPTED_RX_EPOCH_AHEAD: u32 = 3; + const DECRYPT_FAIL_THRESHOLD: u32 = 10; + + pub fn new( + root_key: [u8; 32], + session_generation: u32, + initial_epoch: u32, + send_cipher_algorithm: String, + recv_cipher_algorithm: String, + ) -> Self { + let rx_slots = [ + [EpochRxSlot::default(), EpochRxSlot::default()], + [EpochRxSlot::default(), EpochRxSlot::default()], + ]; + let key_cache = [ + [EpochKeySlot::default(), EpochKeySlot::default()], + [EpochKeySlot::default(), EpochKeySlot::default()], + ]; + let now_ms = now_ms(); + Self { + root_key: RwLock::new(root_key), + session_generation: AtomicU32::new(session_generation), + send_epoch: AtomicU32::new(initial_epoch), + send_seq: [AtomicU64::new(0), AtomicU64::new(0)], + send_epoch_started_ms: AtomicU64::new(now_ms), + send_packets_since_epoch: AtomicU64::new(0), + rx_slots: Mutex::new(rx_slots), + key_cache: Mutex::new(key_cache), + sync_rx_grace: Mutex::new(SyncRxGrace::default()), + sync_rx_grace_expires_at_ms: AtomicU64::new(0), + send_cipher_algorithm, + recv_cipher_algorithm, + invalidated: AtomicBool::new(false), + decrypt_fail_count: AtomicU32::new(0), + } + } + + pub fn invalidate(&self) { + self.invalidated.store(true, Ordering::Relaxed); + } + + pub fn is_valid(&self) -> bool { + !self.invalidated.load(Ordering::Relaxed) + } + + pub fn session_generation(&self) -> u32 { + self.session_generation.load(Ordering::Relaxed) + } + + pub fn root_key(&self) -> [u8; 32] { + *self.root_key.read().unwrap() + } + + pub fn new_root_key() -> [u8; 32] { + let mut out = [0u8; 32]; + rand::rngs::OsRng.fill_bytes(&mut out); + out + } + + pub fn next_sync_epoch(&self) -> u32 { + let send_epoch = self.send_epoch.load(Ordering::Relaxed); + let rx = self.rx_slots.lock().unwrap(); + let mut max_epoch = send_epoch; + for dir in 0..2 { + let cur = rx[dir][0]; + if cur.valid { + max_epoch = max_epoch.max(cur.epoch); + } + let prev = rx[dir][1]; + if prev.valid { + max_epoch = max_epoch.max(prev.epoch); + } + } + max_epoch.wrapping_add(1) + } + + pub fn check_encrypt_algo_same( + &self, + send_algorithm: &str, + recv_algorithm: &str, + ) -> Result<(), anyhow::Error> { + if self.send_cipher_algorithm != send_algorithm + || self.recv_cipher_algorithm != recv_algorithm + { + return Err(anyhow!("encrypt algorithm not same")); + } + Ok(()) + } + + pub fn sync_root_key( + &self, + root_key: [u8; 32], + session_generation: u32, + initial_epoch: u32, + preserve_rx_grace: bool, + ) { + let old_root_key = self.root_key(); + let can_preserve_rx_grace = preserve_rx_grace && old_root_key == root_key; + { + let mut g = self.root_key.write().unwrap(); + *g = root_key; + } + self.session_generation + .store(session_generation, Ordering::Relaxed); + + self.send_epoch.store(initial_epoch, Ordering::Relaxed); + self.send_seq[0].store(0, Ordering::Relaxed); + self.send_seq[1].store(0, Ordering::Relaxed); + self.send_epoch_started_ms + .store(now_ms(), Ordering::Relaxed); + self.send_packets_since_epoch.store(0, Ordering::Relaxed); + + { + let mut rx = self.rx_slots.lock().unwrap(); + let mut sync_rx_grace = self.sync_rx_grace.lock().unwrap(); + if can_preserve_rx_grace { + let expires_at_ms = now_ms().saturating_add(Self::SYNC_RX_GRACE_AFTER_MS); + sync_rx_grace.refresh(*rx, expires_at_ms); + self.sync_rx_grace_expires_at_ms + .store(expires_at_ms, Ordering::Relaxed); + } else { + sync_rx_grace.clear(); + self.sync_rx_grace_expires_at_ms.store(0, Ordering::Relaxed); + } + for dir in 0..2 { + rx[dir][0].clear(); + rx[dir][1].clear(); + } + } + + self.key_cache + .lock() + .unwrap() + .fill([EpochKeySlot::default(), EpochKeySlot::default()]); + } + + fn hkdf_traffic_key(&self, epoch: u32, dir: SecureDatagramDirection) -> [u8; 32] { + let root_key = self.root_key(); + let salt = [0u8; 32]; + let mut extract = HmacSha256::new_from_slice(&salt).unwrap(); + extract.update(&root_key); + let prk = extract.finalize().into_bytes(); + + let mut info = Vec::with_capacity(9 + 4 + 1); + info.extend_from_slice(b"et-traffic"); + info.extend_from_slice(&epoch.to_be_bytes()); + info.push(dir.idx() as u8); + + let mut expand = HmacSha256::new_from_slice(&prk).unwrap(); + expand.update(&info); + expand.update(&[1u8]); + let okm = expand.finalize().into_bytes(); + let mut key = [0u8; 32]; + key.copy_from_slice(&okm[..32]); + key + } + + fn get_or_create_encryptor( + &self, + epoch: u32, + dir: SecureDatagramDirection, + generation: u32, + is_send: bool, + ) -> Arc { + let dir_idx = dir.idx(); + let mut guard = self.key_cache.lock().unwrap(); + for slot in guard[dir_idx].iter_mut() { + if slot.valid && slot.epoch == epoch && slot.generation == generation { + return slot.get_encryptor(is_send); + } + } + + let key = self.hkdf_traffic_key(epoch, dir); + let mut key_128 = [0u8; 16]; + key_128.copy_from_slice(&key[..16]); + + let slot = EpochKeySlot { + epoch, + generation, + valid: true, + send_cipher: Some(create_encryptor(&self.send_cipher_algorithm, key_128, key)), + recv_cipher: Some(create_encryptor(&self.recv_cipher_algorithm, key_128, key)), + }; + let ret = slot.get_encryptor(is_send); + + if !guard[dir_idx][0].valid || guard[dir_idx][0].epoch == epoch { + guard[dir_idx][0] = slot; + } else { + guard[dir_idx][1] = slot; + } + + ret + } + + fn maybe_rotate_epoch(&self, now_ms: u64) { + let packets = self + .send_packets_since_epoch + .fetch_add(1, Ordering::Relaxed) + + 1; + let started = self.send_epoch_started_ms.load(Ordering::Relaxed); + if packets < Self::ROTATE_AFTER_PACKETS + && now_ms.saturating_sub(started) < Self::ROTATE_AFTER_MS + { + return; + } + + let cur = self.send_epoch.load(Ordering::Relaxed); + let next = cur.wrapping_add(1); + if self + .send_epoch + .compare_exchange(cur, next, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() + { + self.send_epoch_started_ms.store(now_ms, Ordering::Relaxed); + self.send_packets_since_epoch.store(0, Ordering::Relaxed); + } + } + + fn next_nonce(&self, dir: SecureDatagramDirection) -> (u32, u64, [u8; 12]) { + let now_ms = now_ms(); + self.maybe_rotate_epoch(now_ms); + let epoch = self.send_epoch.load(Ordering::Relaxed); + let seq = self.send_seq[dir.idx()].fetch_add(1, Ordering::Relaxed); + let mut nonce = [0u8; 12]; + nonce[..4].copy_from_slice(&epoch.to_be_bytes()); + nonce[4..].copy_from_slice(&seq.to_be_bytes()); + (epoch, seq, nonce) + } + + fn parse_tail(payload: &[u8]) -> Option<[u8; 12]> { + let tail = StandardAeadTail::ref_from_suffix(payload)?; + Some(tail.nonce) + } + + fn evict_old_rx_slots(rx: &mut [[EpochRxSlot; 2]; 2], now_ms: u64) { + for dir_slots in rx.iter_mut() { + for slot in dir_slots.iter_mut() { + if !slot.valid { + continue; + } + let last = slot.last_rx_ms; + if last != 0 && now_ms.saturating_sub(last) > Self::EVICT_IDLE_AFTER_MS { + slot.clear(); + } + } + } + } + + fn epoch_in_slots(slots: &[EpochRxSlot; 2], epoch: u32) -> bool { + slots[0].valid && slots[0].epoch == epoch || slots[1].valid && slots[1].epoch == epoch + } + + fn sync_rx_grace_active(&self, now_ms: u64) -> bool { + let expires_at_ms = self.sync_rx_grace_expires_at_ms.load(Ordering::Relaxed); + if expires_at_ms == 0 { + return false; + } + if now_ms < expires_at_ms { + return true; + } + self.sync_rx_grace_expires_at_ms.store(0, Ordering::Relaxed); + false + } + + fn prune_key_cache(&self, rx: &[[EpochRxSlot; 2]; 2], sync_rx_grace: Option<&SyncRxGrace>) { + let send_epoch = self.send_epoch.load(Ordering::Relaxed); + let mut key_cache = self.key_cache.lock().unwrap(); + for d in 0..2 { + for s in 0..2 { + if !key_cache[d][s].valid { + continue; + } + let e = key_cache[d][s].epoch; + let allowed = e == send_epoch + || rx[d][0].valid && rx[d][0].epoch == e + || rx[d][1].valid && rx[d][1].epoch == e + || sync_rx_grace.is_some_and(|g| Self::epoch_in_slots(&g.slots[d], e)); + if !allowed { + key_cache[d][s].valid = false; + } + } + } + } + + fn precheck_replay( + &self, + epoch: u32, + seq: u64, + dir: SecureDatagramDirection, + now_ms: u64, + ) -> bool { + let dir_idx = dir.idx(); + let mut rx = self.rx_slots.lock().unwrap(); + Self::evict_old_rx_slots(&mut rx, now_ms); + let sync_rx_grace = if self.sync_rx_grace_active(now_ms) { + let mut sync_rx_grace = self.sync_rx_grace.lock().unwrap(); + sync_rx_grace.maybe_expire(now_ms); + if sync_rx_grace.valid { + Self::evict_old_rx_slots(&mut sync_rx_grace.slots, now_ms); + Some(sync_rx_grace) + } else { + self.sync_rx_grace_expires_at_ms.store(0, Ordering::Relaxed); + None + } + } else { + None + }; + + if sync_rx_grace + .as_ref() + .is_some_and(|g| Self::epoch_in_slots(&g.slots[dir_idx], epoch)) + { + for slot in sync_rx_grace.as_ref().unwrap().slots[dir_idx].iter() { + if slot.valid && slot.epoch == epoch { + return slot.window.can_accept(seq); + } + } + } + + if !rx[dir_idx][0].valid { + return true; + } + + if rx[dir_idx][0].valid && epoch == rx[dir_idx][0].epoch { + return rx[dir_idx][0].window.can_accept(seq); + } + + if rx[dir_idx][1].valid && epoch == rx[dir_idx][1].epoch { + return rx[dir_idx][1].window.can_accept(seq); + } + + if rx[dir_idx][0].valid && epoch > rx[dir_idx][0].epoch { + let mut baseline_epoch = self.send_epoch.load(Ordering::Relaxed); + if rx[dir_idx][0].valid { + baseline_epoch = baseline_epoch.max(rx[dir_idx][0].epoch); + } + if rx[dir_idx][1].valid { + baseline_epoch = baseline_epoch.max(rx[dir_idx][1].epoch); + } + let max_allowed_epoch = + baseline_epoch.saturating_add(Self::MAX_ACCEPTED_RX_EPOCH_AHEAD); + if epoch > max_allowed_epoch { + return false; + } + + return true; + } + + false + } + + fn commit_replay( + &self, + epoch: u32, + seq: u64, + dir: SecureDatagramDirection, + now_ms: u64, + ) -> bool { + let dir_idx = dir.idx(); + let mut rx = self.rx_slots.lock().unwrap(); + Self::evict_old_rx_slots(&mut rx, now_ms); + let mut sync_rx_grace = if self.sync_rx_grace_active(now_ms) { + let mut sync_rx_grace = self.sync_rx_grace.lock().unwrap(); + sync_rx_grace.maybe_expire(now_ms); + if sync_rx_grace.valid { + Self::evict_old_rx_slots(&mut sync_rx_grace.slots, now_ms); + Some(sync_rx_grace) + } else { + self.sync_rx_grace_expires_at_ms.store(0, Ordering::Relaxed); + None + } + } else { + None + }; + + let accepted = if sync_rx_grace + .as_ref() + .is_some_and(|g| Self::epoch_in_slots(&g.slots[dir_idx], epoch)) + { + let mut accepted = false; + for slot in sync_rx_grace.as_mut().unwrap().slots[dir_idx].iter_mut() { + if slot.valid && slot.epoch == epoch { + slot.last_rx_ms = now_ms; + accepted = slot.window.accept(seq); + break; + } + } + accepted + } else { + if !rx[dir_idx][0].valid { + rx[dir_idx][0] = EpochRxSlot { + epoch, + window: ReplayWindow256::default(), + last_rx_ms: now_ms, + valid: true, + }; + } + + if rx[dir_idx][0].valid && epoch == rx[dir_idx][0].epoch { + rx[dir_idx][0].last_rx_ms = now_ms; + rx[dir_idx][0].window.accept(seq) + } else if rx[dir_idx][1].valid && epoch == rx[dir_idx][1].epoch { + rx[dir_idx][1].last_rx_ms = now_ms; + rx[dir_idx][1].window.accept(seq) + } else if rx[dir_idx][0].valid && epoch > rx[dir_idx][0].epoch { + let mut baseline_epoch = self.send_epoch.load(Ordering::Relaxed); + if rx[dir_idx][0].valid { + baseline_epoch = baseline_epoch.max(rx[dir_idx][0].epoch); + } + if rx[dir_idx][1].valid { + baseline_epoch = baseline_epoch.max(rx[dir_idx][1].epoch); + } + let max_allowed_epoch = + baseline_epoch.saturating_add(Self::MAX_ACCEPTED_RX_EPOCH_AHEAD); + if epoch > max_allowed_epoch { + false + } else { + rx[dir_idx][1] = rx[dir_idx][0]; + rx[dir_idx][0] = EpochRxSlot { + epoch, + window: ReplayWindow256::default(), + last_rx_ms: now_ms, + valid: true, + }; + rx[dir_idx][0].window.accept(seq) + } + } else { + false + } + }; + + self.prune_key_cache(&rx, sync_rx_grace.as_deref()); + accepted + } + + fn check_replay( + &self, + epoch: u32, + seq: u64, + dir: SecureDatagramDirection, + now_ms: u64, + ) -> bool { + if self.precheck_replay(epoch, seq, dir, now_ms) { + return self.commit_replay(epoch, seq, dir, now_ms); + } + + false + } + + pub fn encrypt_payload( + &self, + dir: SecureDatagramDirection, + pkt: &mut ZCPacket, + ) -> Result<(), anyhow::Error> { + if !self.is_valid() { + return Err(anyhow!("session invalidated")); + } + let (epoch, _seq, nonce_bytes) = self.next_nonce(dir); + let encryptor = self.get_or_create_encryptor(epoch, dir, self.session_generation(), true); + if let Err(e) = encryptor.encrypt_with_nonce(pkt, Some(nonce_bytes.as_slice())) { + tracing::warn!(?e, "secure datagram session encrypt failed, invalidating"); + self.invalidate(); + return Err(e.into()); + } + Ok(()) + } + + pub fn decrypt_payload( + &self, + dir: SecureDatagramDirection, + ciphertext_with_tail: &mut ZCPacket, + ) -> Result<(), anyhow::Error> { + if !self.is_valid() { + return Err(anyhow!("session invalidated")); + } + let nonce_bytes = + Self::parse_tail(ciphertext_with_tail.payload()).ok_or_else(|| anyhow!("no tail"))?; + let epoch = u32::from_be_bytes(nonce_bytes[..4].try_into().unwrap()); + let seq = u64::from_be_bytes(nonce_bytes[4..].try_into().unwrap()); + + let now_ms = now_ms(); + if !self.precheck_replay(epoch, seq, dir, now_ms) { + return Err(anyhow!("replay rejected")); + } + + let encryptor = self.get_or_create_encryptor(epoch, dir, self.session_generation(), false); + if let Err(e) = encryptor.decrypt(ciphertext_with_tail) { + let count = self.decrypt_fail_count.fetch_add(1, Ordering::Relaxed) + 1; + if count >= Self::DECRYPT_FAIL_THRESHOLD { + self.invalidate(); + tracing::warn!( + count, + "secure datagram session auto-invalidated after consecutive decrypt failures" + ); + } + return Err(e.into()); + } + self.decrypt_fail_count.store(0, Ordering::Relaxed); + + if !self.commit_replay(epoch, seq, dir, now_ms) { + return Err(anyhow!("replay rejected")); + } + + Ok(()) + } + + #[cfg(test)] + fn check_replay_for_test( + &self, + epoch: u32, + seq: u64, + dir: SecureDatagramDirection, + now_ms: u64, + ) -> bool { + self.check_replay(epoch, seq, dir, now_ms) + } +} + +fn now_ms() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64 +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tunnel::packet_def::PacketType; + + #[test] + fn secure_datagram_supports_asymmetric_algorithms() { + let root_key = SecureDatagramSession::new_root_key(); + let generation = 1u32; + let initial_epoch = 0u32; + + let ab = SecureDatagramSession::new( + root_key, + generation, + initial_epoch, + "aes-256-gcm".to_string(), + "chacha20-poly1305".to_string(), + ); + let ba = SecureDatagramSession::new( + root_key, + generation, + initial_epoch, + "chacha20-poly1305".to_string(), + "aes-256-gcm".to_string(), + ); + + let plaintext1 = b"hello from a"; + let mut pkt1 = ZCPacket::new_with_payload(plaintext1); + pkt1.fill_peer_manager_hdr(10, 20, PacketType::Data as u8); + ab.encrypt_payload(SecureDatagramDirection::AToB, &mut pkt1) + .unwrap(); + ba.decrypt_payload(SecureDatagramDirection::AToB, &mut pkt1) + .unwrap(); + assert_eq!(pkt1.payload(), plaintext1); + + let plaintext2 = b"hello from b"; + let mut pkt2 = ZCPacket::new_with_payload(plaintext2); + pkt2.fill_peer_manager_hdr(20, 10, PacketType::Data as u8); + ba.encrypt_payload(SecureDatagramDirection::BToA, &mut pkt2) + .unwrap(); + ab.decrypt_payload(SecureDatagramDirection::BToA, &mut pkt2) + .unwrap(); + assert_eq!(pkt2.payload(), plaintext2); + } + + #[test] + fn replay_rejects_far_future_epoch_without_poisoning_window() { + let s = SecureDatagramSession::new( + SecureDatagramSession::new_root_key(), + 1, + 0, + "aes-256-gcm".to_string(), + "aes-256-gcm".to_string(), + ); + + let now = now_ms(); + + assert!(s.check_replay_for_test(0, 1, SecureDatagramDirection::AToB, now)); + assert!(s.check_replay_for_test(0, 2, SecureDatagramDirection::AToB, now)); + + assert!(!s.check_replay_for_test(1000, 1, SecureDatagramDirection::AToB, now)); + + assert!(s.check_replay_for_test(1, 1, SecureDatagramDirection::AToB, now + 1)); + assert!(s.check_replay_for_test(1, 2, SecureDatagramDirection::AToB, now + 2)); + } + + #[test] + fn failed_decrypt_does_not_poison_replay_window() { + let root_key = SecureDatagramSession::new_root_key(); + let sender = SecureDatagramSession::new( + root_key, + 1, + 0, + "aes-256-gcm".to_string(), + "aes-256-gcm".to_string(), + ); + let receiver = SecureDatagramSession::new( + root_key, + 1, + 0, + "aes-256-gcm".to_string(), + "aes-256-gcm".to_string(), + ); + + let mut pkt0 = ZCPacket::new_with_payload(b"pkt0"); + pkt0.fill_peer_manager_hdr(10, 20, PacketType::Data as u8); + sender + .encrypt_payload(SecureDatagramDirection::AToB, &mut pkt0) + .unwrap(); + receiver + .decrypt_payload(SecureDatagramDirection::AToB, &mut pkt0) + .unwrap(); + + let mut forged = ZCPacket::new_with_payload(b"forged"); + forged.fill_peer_manager_hdr(10, 20, PacketType::Data as u8); + sender + .encrypt_payload(SecureDatagramDirection::AToB, &mut forged) + .unwrap(); + + let mut poisoned_nonce = [0u8; StandardAeadTail::NONCE_SIZE]; + poisoned_nonce[..4].copy_from_slice(&0u32.to_be_bytes()); + poisoned_nonce[4..].copy_from_slice(&500u64.to_be_bytes()); + + let payload = forged.mut_payload(); + let nonce_offset = payload.len() - StandardAeadTail::NONCE_SIZE; + payload[nonce_offset..].copy_from_slice(&poisoned_nonce); + + assert!( + receiver + .decrypt_payload(SecureDatagramDirection::AToB, &mut forged) + .is_err() + ); + + let plaintext = b"pkt2"; + let mut pkt2 = ZCPacket::new_with_payload(plaintext); + pkt2.fill_peer_manager_hdr(10, 20, PacketType::Data as u8); + sender + .encrypt_payload(SecureDatagramDirection::AToB, &mut pkt2) + .unwrap(); + receiver + .decrypt_payload(SecureDatagramDirection::AToB, &mut pkt2) + .unwrap(); + assert_eq!(pkt2.payload(), plaintext); + } + + #[test] + fn replay_window_shift_preserves_bits() { + let mut w = ReplayWindow256::default(); + for i in 0..10u64 { + assert!(w.accept(i), "seq {i} should be accepted"); + } + assert_eq!(w.max_seq, 9); + + for i in 0..10u64 { + assert!(!w.accept(i), "seq {i} should be rejected as replay"); + } + + assert!(w.accept(10)); + } + + #[test] + fn replay_window_out_of_order_within_window() { + let mut w = ReplayWindow256::default(); + for i in (0..=20u64).step_by(2) { + assert!(w.accept(i), "seq {i} should be accepted"); + } + for i in (1..=19u64).step_by(2) { + assert!(w.accept(i), "seq {i} should be accepted (out of order)"); + } + for i in 0..=20u64 { + assert!(!w.accept(i), "seq {i} should be rejected as replay"); + } + } + + #[test] + fn sync_root_key_allows_any_epoch_from_remote() { + let s = SecureDatagramSession::new( + SecureDatagramSession::new_root_key(), + 1, + 0, + "aes-256-gcm".to_string(), + "aes-256-gcm".to_string(), + ); + + let root_key = s.root_key(); + let now = now_ms(); + assert!(s.check_replay_for_test(0, 0, SecureDatagramDirection::AToB, now)); + assert!(s.check_replay_for_test(0, 1, SecureDatagramDirection::AToB, now)); + + s.sync_root_key(root_key, 2, 2, true); + + assert!(s.check_replay_for_test(0, 10, SecureDatagramDirection::AToB, now + 1)); + } + + #[test] + fn sync_root_key_keeps_previous_epochs_during_grace_window() { + let s = SecureDatagramSession::new( + SecureDatagramSession::new_root_key(), + 1, + 0, + "aes-256-gcm".to_string(), + "aes-256-gcm".to_string(), + ); + + let root_key = s.root_key(); + let now = now_ms(); + assert!(s.check_replay_for_test(0, 0, SecureDatagramDirection::AToB, now)); + assert!(s.check_replay_for_test(1, 0, SecureDatagramDirection::AToB, now + 1)); + + s.sync_root_key(root_key, 2, 2, true); + + assert!(s.check_replay_for_test(2, 0, SecureDatagramDirection::AToB, now + 2)); + assert!(s.check_replay_for_test(1, 1, SecureDatagramDirection::AToB, now + 3)); + assert!(s.check_replay_for_test(0, 1, SecureDatagramDirection::AToB, now + 4)); + } + + #[test] + fn sync_root_key_expires_previous_epochs_after_grace_window() { + let s = SecureDatagramSession::new( + SecureDatagramSession::new_root_key(), + 1, + 0, + "aes-256-gcm".to_string(), + "aes-256-gcm".to_string(), + ); + + let root_key = s.root_key(); + let now = now_ms(); + assert!(s.check_replay_for_test(0, 0, SecureDatagramDirection::AToB, now)); + assert!(s.check_replay_for_test(1, 0, SecureDatagramDirection::AToB, now + 1)); + + s.sync_root_key(root_key, 2, 2, true); + assert!(s.check_replay_for_test(2, 0, SecureDatagramDirection::AToB, now + 2)); + + assert!(!s.check_replay_for_test( + 0, + 1, + SecureDatagramDirection::AToB, + now + SecureDatagramSession::SYNC_RX_GRACE_AFTER_MS + 3 + )); + } + + #[test] + fn sync_root_key_does_not_preserve_previous_epochs_when_root_key_changes() { + let s = SecureDatagramSession::new( + SecureDatagramSession::new_root_key(), + 1, + 0, + "aes-256-gcm".to_string(), + "aes-256-gcm".to_string(), + ); + + let now = now_ms(); + assert!(s.check_replay_for_test(0, 0, SecureDatagramDirection::AToB, now)); + assert!(s.check_replay_for_test(1, 0, SecureDatagramDirection::AToB, now + 1)); + + s.sync_root_key(SecureDatagramSession::new_root_key(), 2, 2, true); + assert!(s.check_replay_for_test(2, 0, SecureDatagramDirection::AToB, now + 2)); + assert!(!s.check_replay_for_test(1, 1, SecureDatagramDirection::AToB, now + 3)); + } +} diff --git a/easytier/src/peers/tests.rs b/easytier/src/peers/tests.rs index 647859f3..ed626316 100644 --- a/easytier/src/peers/tests.rs +++ b/easytier/src/peers/tests.rs @@ -8,7 +8,7 @@ use crate::{ PeerId, error::Error, global_ctx::{ - NetworkIdentity, + NetworkIdentity, TrustedKeySource, tests::{get_mock_global_ctx, get_mock_global_ctx_with_network}, }, stats_manager::{LabelSet, LabelType, MetricName}, @@ -1315,6 +1315,107 @@ async fn credential_node_group_assignment() { .await; } +#[tokio::test] +async fn credential_node_connected_via_admin_b_trusts_admin_a_groups() { + use crate::proto::acl::{Acl, AclV1, GroupIdentity, GroupInfo}; + + let admin_a = create_mock_peer_manager_secure("net1".to_string(), "secret".to_string()).await; + let admin_b = create_mock_peer_manager_secure("net1".to_string(), "secret".to_string()).await; + + let group_declares = vec![GroupIdentity { + group_name: "platform-admin".to_string(), + group_secret: "platform-admin-secret".to_string(), + }]; + admin_a.get_global_ctx().config.set_acl(Some(Acl { + acl_v1: Some(AclV1 { + group: Some(GroupInfo { + declares: group_declares.clone(), + members: vec!["platform-admin".to_string()], + }), + ..Default::default() + }), + })); + admin_b.get_global_ctx().config.set_acl(Some(Acl { + acl_v1: Some(AclV1 { + group: Some(GroupInfo { + declares: group_declares, + members: vec![], + }), + ..Default::default() + }), + })); + + connect_peer_manager(admin_a.clone(), admin_b.clone()).await; + wait_route_appear(admin_a.clone(), admin_b.clone()) + .await + .unwrap(); + + let (_cred_id, cred_secret) = admin_a + .get_global_ctx() + .get_credential_manager() + .generate_credential(vec![], false, vec![], std::time::Duration::from_secs(3600)); + admin_a + .get_global_ctx() + .issue_event(crate::common::global_ctx::GlobalCtxEvent::CredentialChanged); + + let privkey_bytes: [u8; 32] = base64::engine::general_purpose::STANDARD + .decode(&cred_secret) + .unwrap() + .try_into() + .unwrap(); + let private = x25519_dalek::StaticSecret::from(privkey_bytes); + let credential_pubkey = x25519_dalek::PublicKey::from(&private).as_bytes().to_vec(); + + wait_for_condition( + || { + let admin_b = admin_b.clone(); + let credential_pubkey = credential_pubkey.clone(); + async move { + admin_b.get_global_ctx().is_pubkey_trusted_with_source( + &credential_pubkey, + "net1", + TrustedKeySource::OspfCredential, + ) + } + }, + Duration::from_secs(10), + ) + .await; + + let cred_c = create_mock_peer_manager_credential("net1".to_string(), &private).await; + connect_peer_manager(cred_c.clone(), admin_b.clone()).await; + + let admin_a_id = admin_a.my_peer_id(); + wait_for_condition( + || { + let cred_c = cred_c.clone(); + async move { + cred_c + .list_routes() + .await + .iter() + .any(|r| r.peer_id == admin_a_id) + } + }, + Duration::from_secs(10), + ) + .await; + + wait_for_condition( + || { + let cred_c = cred_c.clone(); + async move { + cred_c + .get_route() + .get_peer_groups(admin_a_id) + .contains(&"platform-admin".to_string()) + } + }, + Duration::from_secs(10), + ) + .await; +} + /// Minimal test: two secure peers connect and discover each other's route. #[tokio::test] async fn two_secure_peers_route_appear() { diff --git a/easytier/src/tunnel/websocket.rs b/easytier/src/tunnel/websocket.rs index e561c834..a9f58bd4 100644 --- a/easytier/src/tunnel/websocket.rs +++ b/easytier/src/tunnel/websocket.rs @@ -220,6 +220,9 @@ impl WsTunnelConnector { let is_wss = is_wss(&addr)?; let socket_addr = SocketAddr::from_url(addr.clone(), ip_version).await?; let stream = tcp_socket.connect(socket_addr).await?; + if let Err(error) = stream.set_nodelay(true) { + tracing::warn!(?error, "set_nodelay fail in ws connect"); + } let info = TunnelInfo { tunnel_type: addr.scheme().to_owned(), diff --git a/easytier/src/web_client/mod.rs b/easytier/src/web_client/mod.rs index c487d13f..ee478e1c 100644 --- a/easytier/src/web_client/mod.rs +++ b/easytier/src/web_client/mod.rs @@ -126,7 +126,7 @@ impl WebClient { } }; - if support_encryption { + if support_encryption && security::web_secure_tunnel_supported() { log::info!("Server supports encryption, reconnecting with secure tunnel"); drop(session); @@ -159,6 +159,23 @@ impl WebClient { continue; } + if support_encryption { + if secure_mode { + connected.store(false, Ordering::Release); + let wait = 1; + log::warn!( + "secure-mode enabled but local build lacks aes-gcm support for web secure tunnel, retrying in {} seconds...", + wait + ); + tokio::time::sleep(std::time::Duration::from_secs(wait)).await; + continue; + } + + log::warn!( + "Server supports encryption but local build lacks aes-gcm support for web secure tunnel, falling back to legacy tunnel" + ); + } + if secure_mode { connected.store(false, Ordering::Release); let wait = 1; diff --git a/easytier/src/web_client/security.rs b/easytier/src/web_client/security.rs index 30020024..2916624a 100644 --- a/easytier/src/web_client/security.rs +++ b/easytier/src/web_client/security.rs @@ -1,11 +1,12 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; -use bytes::BytesMut; use futures::{SinkExt, StreamExt}; -use snow::{Builder, TransportState, params::NoiseParams}; +use snow::{Builder, params::NoiseParams}; use crate::{ + common::config::EncryptionAlgorithm, + peers::secure_datagram::{SecureDatagramDirection, SecureDatagramSession}, proto::common::TunnelInfo, tunnel::{ SplitTunnel, StreamItem, Tunnel, TunnelError, ZCPacketSink, ZCPacketStream, @@ -17,6 +18,11 @@ use crate::{ const NOISE_MAGIC: &[u8] = b"ET_WEB_NOISE_V1:"; const NOISE_PROLOGUE: &[u8] = b"easytier-webclient-noise-v1"; const NOISE_PATTERN: &str = "Noise_NN_25519_ChaChaPoly_SHA256"; +const WEB_SECURE_CIPHER_ALGORITHM: &str = "aes-gcm"; +const WEB_SESSION_GENERATION: u32 = 1; +const WEB_INITIAL_EPOCH: u32 = 0; +const WEB_SECURE_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(3); +const WEB_SECURE_ACCEPT_TIMEOUT: Duration = WEB_SECURE_HANDSHAKE_TIMEOUT; struct RawSplitTunnel { info: Option, @@ -50,24 +56,42 @@ impl Tunnel for RawSplitTunnel { } } -struct NoiseTunnelFilter { - transport: Arc>, +#[derive(Clone, Copy)] +enum SecureTunnelRole { + Initiator, + Responder, } -impl TunnelFilter for NoiseTunnelFilter { +impl SecureTunnelRole { + fn send_dir(self) -> SecureDatagramDirection { + match self { + Self::Initiator => SecureDatagramDirection::AToB, + Self::Responder => SecureDatagramDirection::BToA, + } + } + + fn recv_dir(self) -> SecureDatagramDirection { + match self { + Self::Initiator => SecureDatagramDirection::BToA, + Self::Responder => SecureDatagramDirection::AToB, + } + } +} + +struct SecureDatagramTunnelFilter { + session: Arc, + role: SecureTunnelRole, +} + +impl TunnelFilter for SecureDatagramTunnelFilter { type FilterOutput = (); fn before_send(&self, data: ZCPacket) -> Option { - let plain = data.tunnel_payload(); - let mut encrypted = vec![0u8; plain.len() + 64]; - let len = self - .transport - .lock() - .unwrap() - .write_message(plain, &mut encrypted) - .ok()?; - let mut packet = ZCPacket::new_with_payload(&encrypted[..len]); + let mut packet = ZCPacket::new_with_payload(data.tunnel_payload()); packet.fill_peer_manager_hdr(0, 0, PacketType::Data as u8); + self.session + .encrypt_payload(self.role.send_dir(), &mut packet) + .ok()?; Some(packet) } @@ -76,23 +100,24 @@ impl TunnelFilter for NoiseTunnelFilter { Ok(v) => v, Err(e) => return Some(Err(e)), }; - let cipher = packet.payload(); - let mut plain = vec![0u8; cipher.len() + 64]; - let len = match self - .transport - .lock() + + let mut cipher = ZCPacket::new_with_payload(packet.payload()); + cipher.fill_peer_manager_hdr(0, 0, PacketType::Data as u8); + cipher + .mut_peer_manager_header() .unwrap() - .read_message(cipher, &mut plain) + .set_encrypted(true); + if let Err(e) = self + .session + .decrypt_payload(self.role.recv_dir(), &mut cipher) { - Ok(v) => v, - Err(e) => { - return Some(Err(TunnelError::InvalidPacket(format!( - "noise decrypt failed: {e}" - )))); - } - }; + return Some(Err(TunnelError::InvalidPacket(format!( + "secure datagram decrypt failed: {e}" + )))); + } + Some(Ok(ZCPacket::new_from_buf( - BytesMut::from(&plain[..len]), + cipher.payload_bytes(), ZCPacketType::DummyTunnel, ))) } @@ -117,24 +142,50 @@ fn decode_noise_payload(payload: &[u8]) -> Option<&[u8]> { payload.strip_prefix(NOISE_MAGIC) } +pub fn web_secure_tunnel_supported() -> bool { + WEB_SECURE_CIPHER_ALGORITHM + .parse::() + .is_ok() +} + +fn web_secure_cipher_algorithm() -> Result<&'static str, TunnelError> { + if !web_secure_tunnel_supported() { + return Err(TunnelError::InternalError(format!( + "web secure tunnel requires {WEB_SECURE_CIPHER_ALGORITHM} support" + ))); + } + Ok(WEB_SECURE_CIPHER_ALGORITHM) +} + +fn new_web_secure_session(root_key: [u8; 32], algorithm: &str) -> Arc { + let algo = algorithm.to_string(); + Arc::new(SecureDatagramSession::new( + root_key, + WEB_SESSION_GENERATION, + WEB_INITIAL_EPOCH, + algo.clone(), + algo, + )) +} + fn wrap_secure_tunnel( info: Option, stream: std::pin::Pin>, sink: std::pin::Pin>, - transport: TransportState, + session: Arc, + role: SecureTunnelRole, ) -> Box { let raw = RawSplitTunnel::new(info, stream, sink); Box::new(TunnelWithFilter::new( raw, - NoiseTunnelFilter { - transport: Arc::new(Mutex::new(transport)), - }, + SecureDatagramTunnelFilter { session, role }, )) } pub async fn upgrade_client_tunnel( tunnel: Box, ) -> Result, TunnelError> { + let web_cipher_algorithm = web_secure_cipher_algorithm()?; let info = tunnel.info(); let (mut stream, mut sink) = tunnel.split(); @@ -156,19 +207,32 @@ pub async fn upgrade_client_tunnel( ))) .await?; - let msg2_packet = stream.next().await.ok_or(TunnelError::Shutdown)??; + let msg2_packet = match tokio::time::timeout(WEB_SECURE_HANDSHAKE_TIMEOUT, stream.next()).await + { + Ok(Some(Ok(packet))) => packet, + Ok(Some(Err(error))) => return Err(error), + Ok(None) => return Err(TunnelError::Shutdown), + Err(error) => return Err(error.into()), + }; let msg2_cipher = decode_noise_payload(msg2_packet.payload()) .ok_or_else(|| TunnelError::InvalidPacket("invalid noise msg2 magic".to_string()))?; - let mut msg2 = vec![0u8; 1024]; - state - .read_message(msg2_cipher, &mut msg2) + let mut root_key_buf = [0u8; 32]; + let root_key_len = state + .read_message(msg2_cipher, &mut root_key_buf) .map_err(|e| TunnelError::InvalidPacket(format!("read noise msg2 failed: {e}")))?; + if root_key_len != root_key_buf.len() { + return Err(TunnelError::InvalidPacket(format!( + "invalid web secure root key len: {root_key_len}" + ))); + } - let transport = state - .into_transport_mode() - .map_err(|e| TunnelError::InternalError(format!("switch transport mode failed: {e}")))?; - - Ok(wrap_secure_tunnel(info, stream, sink, transport)) + Ok(wrap_secure_tunnel( + info, + stream, + sink, + new_web_secure_session(root_key_buf, web_cipher_algorithm), + SecureTunnelRole::Initiator, + )) } pub async fn accept_or_upgrade_server_tunnel( @@ -179,7 +243,7 @@ pub async fn accept_or_upgrade_server_tunnel( let mut stream = stream; let mut sink = sink; - let first_packet = match tokio::time::timeout(Duration::from_secs(1), stream.next()).await { + let first_packet = match tokio::time::timeout(WEB_SECURE_ACCEPT_TIMEOUT, stream.next()).await { Ok(Some(Ok(packet))) => packet, Ok(Some(Err(error))) => return Err(error), Ok(None) => return Err(TunnelError::Shutdown), @@ -197,6 +261,7 @@ pub async fn accept_or_upgrade_server_tunnel( false, )); }; + let web_cipher_algorithm = web_secure_cipher_algorithm()?; let params: NoiseParams = NOISE_PATTERN .parse() @@ -212,18 +277,81 @@ pub async fn accept_or_upgrade_server_tunnel( .read_message(msg1_cipher, &mut msg1) .map_err(|e| TunnelError::InvalidPacket(format!("read noise msg1 failed: {e}")))?; + let root_key = SecureDatagramSession::new_root_key(); let mut msg2 = vec![0u8; 1024]; let msg2_len = state - .write_message(&[], &mut msg2) + .write_message(&root_key, &mut msg2) .map_err(|e| TunnelError::InternalError(format!("write noise msg2 failed: {e}")))?; sink.send(pack_control_packet(&encode_noise_payload( &msg2[..msg2_len], ))) .await?; - let transport = state - .into_transport_mode() - .map_err(|e| TunnelError::InternalError(format!("switch transport mode failed: {e}")))?; - - Ok((wrap_secure_tunnel(info, stream, sink, transport), true)) + Ok(( + wrap_secure_tunnel( + info, + stream, + sink, + new_web_secure_session(root_key, web_cipher_algorithm), + SecureTunnelRole::Responder, + ), + true, + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::tunnel::ring::create_ring_tunnel_pair; + + #[test] + fn web_secure_cipher_algorithm_matches_support_flag() { + let result = web_secure_cipher_algorithm(); + if web_secure_tunnel_supported() { + assert_eq!(result.unwrap(), WEB_SECURE_CIPHER_ALGORITHM); + } else { + assert!(matches!(result, Err(TunnelError::InternalError(_)))); + } + } + + #[test] + fn web_secure_session_uses_pinned_cipher_algorithm() { + if !web_secure_tunnel_supported() { + return; + } + + let session = new_web_secure_session( + SecureDatagramSession::new_root_key(), + web_secure_cipher_algorithm().unwrap(), + ); + session + .check_encrypt_algo_same(WEB_SECURE_CIPHER_ALGORITHM, WEB_SECURE_CIPHER_ALGORITHM) + .unwrap(); + } + + #[tokio::test] + async fn upgrade_client_tunnel_times_out_when_server_never_replies() { + let (server_tunnel, client_tunnel) = create_ring_tunnel_pair(); + let _server_tunnel = server_tunnel; + + let err = upgrade_client_tunnel(client_tunnel).await.unwrap_err(); + assert!(matches!(err, TunnelError::Timeout(_))); + } + + #[tokio::test] + async fn accept_secure_tunnel_after_short_client_delay() { + let (server_tunnel, client_tunnel) = create_ring_tunnel_pair(); + + let server_task = + tokio::spawn(async move { accept_or_upgrade_server_tunnel(server_tunnel).await }); + + tokio::time::sleep(Duration::from_millis(1500)).await; + + let client_task = tokio::spawn(async move { upgrade_client_tunnel(client_tunnel).await }); + + let (server_res, client_res) = tokio::join!(server_task, client_task); + let (_, secure) = server_res.unwrap().unwrap(); + assert!(secure); + assert!(client_res.unwrap().is_ok()); + } }