diff --git a/Cargo.lock b/Cargo.lock index 3cc1211..a848a6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "aead" version = "0.5.2" @@ -70,6 +85,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.101" @@ -249,6 +273,30 @@ dependencies = [ "tracing", ] +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + [[package]] name = "base64" version = "0.22.1" @@ -354,6 +402,16 @@ dependencies = [ "syn 2.0.116", ] +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -468,6 +526,39 @@ dependencies = [ "rand_core 0.10.0", ] +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "num-traits", + "windows-link 0.2.1", +] + +[[package]] +name = "chrono-tz" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + [[package]] name = "cipher" version = "0.4.4" @@ -507,6 +598,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -556,6 +656,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + [[package]] name = "crossbeam-epoch" version = "0.9.18" @@ -681,6 +791,12 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "deunicode" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + [[package]] name = "digest" version = "0.10.7" @@ -746,6 +862,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "downcast-rs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -977,7 +1099,7 @@ dependencies = [ "log", "rustversion", "windows-link 0.2.1", - "windows-result", + "windows-result 0.3.4", ] [[package]] @@ -1041,6 +1163,36 @@ dependencies = [ "polyval", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "globwalk" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" +dependencies = [ + "bitflags 2.9.0", + "ignore", + "walkdir", +] + [[package]] name = "h2" version = "0.4.8" @@ -1260,6 +1412,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "hyper" version = "1.6.0" @@ -1324,6 +1485,30 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -1469,6 +1654,22 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.8.0" @@ -1552,6 +1753,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + [[package]] name = "itoa" version = "1.0.15" @@ -1769,6 +1976,12 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "litemap" version = "0.7.5" @@ -1855,6 +2068,36 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "backtrace", + "backtrace-ext", + "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "unicode-width 0.1.14", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.116", +] + [[package]] name = "mime" version = "0.3.17" @@ -1867,6 +2110,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "1.2.0" @@ -1928,7 +2180,10 @@ dependencies = [ "rcgen", "redis", "reed-solomon-erasure", + "regex", "reqwest", + "rsbinder", + "rsbinder-aidl", "rtnetlink", "rustls", "serde", @@ -2218,6 +2473,15 @@ dependencies = [ "objc2-security", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "oid-registry" version = "0.8.1" @@ -2291,6 +2555,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "owo-colors" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" + [[package]] name = "parking" version = "2.2.1" @@ -2345,6 +2615,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "parse-zoneinfo" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" +dependencies = [ + "regex", +] + [[package]] name = "pastey" version = "0.1.1" @@ -2367,6 +2646,87 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.116", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -2456,6 +2816,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "pretty-hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a65843dfefbafd3c879c683306959a6de478443ffe9c9adf02f5976432402d7" + [[package]] name = "prettyplease" version = "0.2.37" @@ -2477,7 +2843,7 @@ dependencies = [ "is-terminal", "lazy_static", "term", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -2507,7 +2873,7 @@ dependencies = [ "bitflags 2.9.0", "hex", "procfs-core", - "rustix", + "rustix 0.38.44", ] [[package]] @@ -2818,6 +3184,18 @@ dependencies = [ "syn 2.0.116", ] +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + [[package]] name = "regex-automata" version = "0.4.9" @@ -2940,6 +3318,51 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" +[[package]] +name = "rsbinder" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8083f00eae89dabdac285d622f9844f0da16eca07e15b779e6b66aaedec3e9" +dependencies = [ + "async-trait", + "downcast-rs", + "log", + "pretty-hex", + "rsbinder-aidl", + "rsproperties", + "rustix 1.1.4", + "tokio", +] + +[[package]] +name = "rsbinder-aidl" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625505d53b7e80fedf1e07a4a4483aff82b430c07e6813dad7e9c1c70d0c0315" +dependencies = [ + "convert_case", + "miette", + "pest", + "pest_derive", + "serde", + "tera", + "thiserror 2.0.12", +] + +[[package]] +name = "rsproperties" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98d55abde44982a00d67a84809166ea8f5c143abc86818f555485f19766cfe4" +dependencies = [ + "log", + "pretty-hex", + "rustix 1.1.4", + "thiserror 2.0.12", + "zerocopy 0.8.23", + "zerocopy-derive 0.8.23", +] + [[package]] name = "rtnetlink" version = "0.21.0" @@ -2974,6 +3397,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rustc-demangle" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -3007,10 +3436,23 @@ dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", "windows-sys 0.59.0", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + [[package]] name = "rustls" version = "0.23.38" @@ -3185,6 +3627,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -3200,6 +3653,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + [[package]] name = "simd_cesu8" version = "1.1.1" @@ -3216,6 +3679,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + [[package]] name = "slab" version = "0.4.9" @@ -3225,6 +3694,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slug" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "smallvec" version = "1.14.0" @@ -3285,6 +3764,27 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "supports-color" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + [[package]] name = "syn" version = "1.0.109" @@ -3371,6 +3871,28 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tera" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8004bca281f2d32df3bacd59bc67b312cb4c70cea46cbd79dbe8ac5ed206722" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand 0.8.5", + "regex", + "serde", + "serde_json", + "slug", + "unicode-segmentation", +] + [[package]] name = "term" version = "0.7.0" @@ -3382,6 +3904,26 @@ dependencies = [ "winapi", ] +[[package]] +name = "terminal_size" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" +dependencies = [ + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "unicode-linebreak", + "unicode-width 0.2.2", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -3497,7 +4039,9 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot 0.12.3", "pin-project-lite", + "signal-hook-registry", "socket2 0.6.3", "tokio-macros", "windows-sys 0.61.2", @@ -3741,18 +4285,42 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + [[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -4089,7 +4657,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ - "windows-core", + "windows-core 0.51.1", "windows-targets 0.48.5", ] @@ -4102,6 +4670,41 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[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.116", +] + +[[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.116", +] + [[package]] name = "windows-link" version = "0.1.3" @@ -4121,8 +4724,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ "windows-link 0.1.3", - "windows-result", - "windows-strings", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] @@ -4134,6 +4737,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-strings" version = "0.4.2" @@ -4143,6 +4755,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/mycelium-android/Cargo.lock b/mycelium-android/Cargo.lock index 357f45e..86f03b0 100644 --- a/mycelium-android/Cargo.lock +++ b/mycelium-android/Cargo.lock @@ -1731,7 +1731,10 @@ dependencies = [ "rcgen", "redis", "reed-solomon-erasure", + "regex", "reqwest", + "rsbinder", + "rsbinder-aidl", "rtnetlink", "rustls", "serde", @@ -1751,12 +1754,8 @@ dependencies = [ name = "mycelium-android" version = "0.1.0" dependencies = [ - "libc", "mycelium", - "regex", "rsbinder", - "rsbinder-aidl", - "tokio", "tracing", "tracing-android", "tracing-subscriber", diff --git a/mycelium-android/Cargo.toml b/mycelium-android/Cargo.toml index 1d47d01..ac67640 100644 --- a/mycelium-android/Cargo.toml +++ b/mycelium-android/Cargo.toml @@ -8,19 +8,10 @@ name = "mycelium-android" path = "src/main.rs" [dependencies] -mycelium = { path = "../mycelium" } -tokio = { version = "1.50", features = ["rt-multi-thread"] } -rsbinder = { version = "0.6", features = ["android_14_plus"] } +mycelium = { path = "../mycelium", features = ["aidl"] } +rsbinder = { version = "0.6", features = ["android_14"] } tracing = { version = "0.1", features = ["release_max_level_debug"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } [target.'cfg(target_os = "android")'.dependencies] tracing-android = "0.2" -libc = "0.2" - -[features] -mactunfd = [] - -[build-dependencies] -rsbinder-aidl = "0.6" -regex = "1" diff --git a/mycelium-android/src/main.rs b/mycelium-android/src/main.rs index e31c2cd..c6408bf 100644 --- a/mycelium-android/src/main.rs +++ b/mycelium-android/src/main.rs @@ -1,11 +1,4 @@ -// Pull in the AIDL-generated Rust bindings (tech::threefold::mycelium module). -include!(concat!(env!("OUT_DIR"), "/mycelium_aidl.rs")); - -mod node; -mod service; -mod service_manager; - -use service::MyceliumService; +use mycelium::aidl::{BnMyceliumService, IMyceliumService, MyceliumService}; fn main() { setup_logging(); @@ -34,14 +27,13 @@ fn main() { if let Some(path) = &key_file { let key_data = std::fs::read(path).expect("failed to read key file"); - use tech::threefold::mycelium::IMyceliumService::IMyceliumService; svc.start(&peers, &key_data, false) .expect("failed to start mycelium node"); } - let binder = tech::threefold::mycelium::IMyceliumService::BnMyceliumService::new_binder(svc); + let binder = BnMyceliumService::new_binder(svc); - service_manager::add_service( + mycelium::aidl::add_service( "tech.threefold.mycelium.IMyceliumService", binder.as_binder(), ) @@ -52,10 +44,6 @@ fn main() { let _ = rsbinder::ProcessState::join_thread_pool(); } -// ── Logging setup ───────────────────────────────────────────────────────────── -// NOTE: This is copied from mobile. -// TODO: Is this needed, i.e. do we care about capturing logs? - #[cfg(target_os = "android")] fn setup_logging() { use tracing::level_filters::LevelFilter; @@ -73,7 +61,6 @@ fn setup_logging() { .init(); } -// Convenience when building on linux #[cfg(not(target_os = "android"))] fn setup_logging() { use tracing_subscriber::EnvFilter; diff --git a/mycelium/Cargo.toml b/mycelium/Cargo.toml index 01f8ff8..e22dea8 100644 --- a/mycelium/Cargo.toml +++ b/mycelium/Cargo.toml @@ -12,6 +12,7 @@ vendored-openssl = ["openssl/vendored"] mactunfd = [ "tun/appstore", ] #mactunfd is a flag to specify that macos should provide tun FD instead of tun name +aidl = ["dep:rsbinder", "dep:rsbinder-aidl", "dep:regex"] [dependencies] cdn-meta = { git = "https://github.com/threefoldtech/mycelium-cdn-registry", package = "cdn-meta" } @@ -67,6 +68,7 @@ reed-solomon-erasure = "6.0.0" hickory-server = "0.26.0" async-trait = "0.1.89" hickory-resolver = "0.26.0" +rsbinder = { version = "0.6", features = ["android_14"], optional = true } [target.'cfg(target_os = "linux")'.dependencies] rtnetlink = "0.21.0" nix = { version = "0.31.2", features = ["socket"] } @@ -83,6 +85,11 @@ wintun = "0.5.1" [target.'cfg(target_os = "android")'.dependencies] tun = { git = "https://github.com/LeeSmet/rust-tun", features = ["async"] } +libc = "0.2.180" [target.'cfg(target_os = "ios")'.dependencies] tun = { git = "https://github.com/LeeSmet/rust-tun", features = ["async"] } + +[build-dependencies] +rsbinder-aidl = { version = "0.6", optional = true } +regex = { version = "1", optional = true } diff --git a/mycelium-android/aidl/android/os/IServiceManager.aidl b/mycelium/aidl/android/os/IServiceManager.aidl similarity index 100% rename from mycelium-android/aidl/android/os/IServiceManager.aidl rename to mycelium/aidl/android/os/IServiceManager.aidl diff --git a/mycelium-android/aidl/tech/threefold/mycelium/IMyceliumService.aidl b/mycelium/aidl/tech/threefold/mycelium/IMyceliumService.aidl similarity index 100% rename from mycelium-android/aidl/tech/threefold/mycelium/IMyceliumService.aidl rename to mycelium/aidl/tech/threefold/mycelium/IMyceliumService.aidl diff --git a/mycelium-android/aidl/tech/threefold/mycelium/NodeInfo.aidl b/mycelium/aidl/tech/threefold/mycelium/NodeInfo.aidl similarity index 100% rename from mycelium-android/aidl/tech/threefold/mycelium/NodeInfo.aidl rename to mycelium/aidl/tech/threefold/mycelium/NodeInfo.aidl diff --git a/mycelium-android/aidl/tech/threefold/mycelium/PacketStatEntry.aidl b/mycelium/aidl/tech/threefold/mycelium/PacketStatEntry.aidl similarity index 100% rename from mycelium-android/aidl/tech/threefold/mycelium/PacketStatEntry.aidl rename to mycelium/aidl/tech/threefold/mycelium/PacketStatEntry.aidl diff --git a/mycelium-android/aidl/tech/threefold/mycelium/PacketStats.aidl b/mycelium/aidl/tech/threefold/mycelium/PacketStats.aidl similarity index 100% rename from mycelium-android/aidl/tech/threefold/mycelium/PacketStats.aidl rename to mycelium/aidl/tech/threefold/mycelium/PacketStats.aidl diff --git a/mycelium-android/aidl/tech/threefold/mycelium/PeerInfo.aidl b/mycelium/aidl/tech/threefold/mycelium/PeerInfo.aidl similarity index 100% rename from mycelium-android/aidl/tech/threefold/mycelium/PeerInfo.aidl rename to mycelium/aidl/tech/threefold/mycelium/PeerInfo.aidl diff --git a/mycelium-android/aidl/tech/threefold/mycelium/QueriedSubnet.aidl b/mycelium/aidl/tech/threefold/mycelium/QueriedSubnet.aidl similarity index 100% rename from mycelium-android/aidl/tech/threefold/mycelium/QueriedSubnet.aidl rename to mycelium/aidl/tech/threefold/mycelium/QueriedSubnet.aidl diff --git a/mycelium-android/aidl/tech/threefold/mycelium/Route.aidl b/mycelium/aidl/tech/threefold/mycelium/Route.aidl similarity index 100% rename from mycelium-android/aidl/tech/threefold/mycelium/Route.aidl rename to mycelium/aidl/tech/threefold/mycelium/Route.aidl diff --git a/mycelium-android/build.rs b/mycelium/build.rs similarity index 59% rename from mycelium-android/build.rs rename to mycelium/build.rs index a69a2e0..4aade66 100644 --- a/mycelium-android/build.rs +++ b/mycelium/build.rs @@ -1,7 +1,13 @@ fn main() { - let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); - let output = out_dir.join("mycelium_aidl.rs"); + #[cfg(feature = "aidl")] + aidl_codegen(); +} +#[cfg(feature = "aidl")] +fn aidl_codegen() { + let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); + + let output = out_dir.join("mycelium_aidl.rs"); rsbinder_aidl::Builder::new() .source("aidl/tech/threefold/mycelium/PeerInfo.aidl") .source("aidl/tech/threefold/mycelium/NodeInfo.aidl") @@ -13,6 +19,7 @@ fn main() { .output(&output) .generate() .unwrap(); + patch_async_compat(&output); let sm_output = out_dir.join("service_manager_a15.rs"); rsbinder_aidl::Builder::new() @@ -22,31 +29,20 @@ fn main() { .unwrap(); patch_async_compat(&sm_output); - // rsbinder-aidl 0.6.0 generates async infrastructure that is incompatible with - // async-trait ≥ 0.1.80 on Rust ≥ 1.75 (native AFIT makes async traits non-dyn-compatible). - // We can't pin async-trait < 0.1.80 because mycelium requires ^0.1.89. - patch_async_compat(&output); - println!("cargo:rerun-if-changed=aidl/"); } -/// Fix the generated code so it compiles with modern async-trait. +/// Fix rsbinder-aidl 0.6 generated code for async-trait >= 0.1.80 on Rust >= 1.75. /// -/// `IXxxAsyncService` is annotated `#[async_trait]` with `async fn` methods. -/// With async-trait ≥ 0.1.80 on Rust ≥ 1.75 the macro uses native AFIT, making the -/// trait non-dyn-compatible. The adapter generated by `declare_binder_interface!` -/// requires `&dyn IXxxAsyncService`, so we rewrite every `async fn` to return a -/// `BoxFuture` instead — which is dyn-compatible. +/// The generated `IXxxAsyncService` traits use `async fn` methods annotated with +/// `#[async_trait]`. With modern async-trait the macro uses native AFIT, making +/// the trait non-dyn-compatible. We rewrite `async fn` to return `BoxFuture`. +#[cfg(feature = "aidl")] fn patch_async_compat(path: &std::path::Path) { let src = std::fs::read_to_string(path).expect("read generated AIDL file"); - // Step 1 — Strip #[async_trait] from the AsyncService trait. let patched = src.replace("#[::async_trait::async_trait]\n", ""); - // Step 2 — Rewrite every `async fn r#name(params) -> RetType;` found in the file - // (only IXxxAsyncService uses async fn in rsbinder-aidl generated code). - // Each `&` in the parameter list gets a `'a` lifetime; the return type is wrapped - // in BoxFuture<'a, _>. Method names keep the `r#` raw-identifier prefix. let async_fn_re = regex::Regex::new(r"(?m)^(\s+)async fn (r#\w+)\(([^)]+)\)\s*->\s*([^;]+);$").unwrap(); @@ -54,8 +50,6 @@ fn patch_async_compat(path: &std::path::Path) { .replace_all(&patched, |caps: ®ex::Captures| { let indent = &caps[1]; let name = &caps[2]; - // Add 'a lifetime to every reference parameter, but leave &self untouched. - // Workaround: replace &self with a unique marker before adding 'a, then restore it. let params = caps[3] .replace("&self", "\x00SELF\x00") .replace('&', "&'a ") diff --git a/mycelium/src/aidl/conversions.rs b/mycelium/src/aidl/conversions.rs new file mode 100644 index 0000000..f6ba3bc --- /dev/null +++ b/mycelium/src/aidl/conversions.rs @@ -0,0 +1,48 @@ +//! Conversions between mycelium core types and AIDL-generated parcelables. + +use crate::peer_manager::PeerStats; +use crate::routing_table::RouteEntry; + +use super::tech::threefold::mycelium::PacketStatEntry::PacketStatEntry; +use super::tech::threefold::mycelium::PeerInfo::PeerInfo; +use super::tech::threefold::mycelium::Route::Route; + +impl From for PeerInfo { + fn from(p: PeerStats) -> Self { + PeerInfo { + protocol: p.endpoint.proto().to_string(), + address: p.endpoint.address().to_string(), + peerType: p.pt.to_string(), + connectionState: p.connection_state.to_string(), + rxBytes: p.rx_bytes as i64, + txBytes: p.tx_bytes as i64, + discoveredSeconds: p.discovered as i64, + lastConnectedSeconds: p.last_connected.map(|v| v as i64).unwrap_or(-1), + } + } +} + +impl From for Route { + fn from(r: RouteEntry) -> Self { + Route { + subnet: r.source().subnet().to_string(), + nextHop: r.neighbour().connection_identifier().clone(), + metric: if r.metric().is_infinite() { + -1 + } else { + u16::from(r.metric()) as i64 + }, + seqno: u16::from(r.seqno()) as i32, + } + } +} + +impl From for PacketStatEntry { + fn from(e: crate::router::PacketStatEntry) -> Self { + PacketStatEntry { + ip: e.ip.to_string(), + packetCount: e.packet_count as i64, + byteCount: e.byte_count as i64, + } + } +} diff --git a/mycelium/src/aidl/mod.rs b/mycelium/src/aidl/mod.rs new file mode 100644 index 0000000..35779a8 --- /dev/null +++ b/mycelium/src/aidl/mod.rs @@ -0,0 +1,19 @@ +//! Android AIDL (Binder IPC) interface for the mycelium node. +//! +//! This module is only compiled when the `aidl` feature is enabled. It provides +//! [`MyceliumService`] which implements the `IMyceliumService` AIDL interface, +//! and [`add_service`] to register the service with Android's ServiceManager. + +// Pull in the AIDL-generated Rust bindings (tech::threefold::mycelium module). +include!(concat!(env!("OUT_DIR"), "/mycelium_aidl.rs")); + +mod conversions; +mod service; +mod service_manager; + +pub use service::MyceliumService; +pub use service_manager::add_service; + +// Re-export the generated BnMyceliumService for the binary to use. +pub use tech::threefold::mycelium::IMyceliumService::BnMyceliumService; +pub use tech::threefold::mycelium::IMyceliumService::IMyceliumService; diff --git a/mycelium-android/src/service.rs b/mycelium/src/aidl/service.rs similarity index 76% rename from mycelium-android/src/service.rs rename to mycelium/src/aidl/service.rs index d18a65c..89814a6 100644 --- a/mycelium-android/src/service.rs +++ b/mycelium/src/aidl/service.rs @@ -1,19 +1,23 @@ +//! Implementation of the `IMyceliumService` AIDL interface. + use std::net::{IpAddr, SocketAddr}; use std::sync::Mutex; use rsbinder::StatusCode; use tracing::{error, info, warn}; -use crate::node::{parse_proxy_remote, peer_stats_to_aidl, NodeHandle, DEFAULT_SOCKS_PORT}; -use crate::tech::threefold::mycelium::IMyceliumService::IMyceliumService; -use crate::tech::threefold::mycelium::NodeInfo::NodeInfo; -use crate::tech::threefold::mycelium::PacketStatEntry::PacketStatEntry; -use crate::tech::threefold::mycelium::PacketStats::PacketStats; -use crate::tech::threefold::mycelium::PeerInfo::PeerInfo; -use crate::tech::threefold::mycelium::QueriedSubnet::QueriedSubnet; -use crate::tech::threefold::mycelium::Route::Route; +use crate::metrics::NoMetrics; +use crate::node_handle::{parse_proxy_remote, NodeHandle, DEFAULT_SOCKS_PORT}; +use crate::peer_manager::PeerDiscoveryMode; +use crate::{crypto, Config}; -// ── Service struct ───────────────────────────────────────────────────────────── +use super::tech::threefold::mycelium::IMyceliumService::IMyceliumService; +use super::tech::threefold::mycelium::NodeInfo::NodeInfo; +use super::tech::threefold::mycelium::PacketStatEntry::PacketStatEntry; +use super::tech::threefold::mycelium::PacketStats::PacketStats; +use super::tech::threefold::mycelium::PeerInfo::PeerInfo; +use super::tech::threefold::mycelium::QueriedSubnet::QueriedSubnet; +use super::tech::threefold::mycelium::Route::Route; /// Implements the `IMyceliumService` AIDL interface. /// @@ -21,15 +25,20 @@ use crate::tech::threefold::mycelium::Route::Route; /// answers immediately (pure computation) or uses `rt_handle.block_on` to /// drive an async operation against the running mycelium node. pub struct MyceliumService { - /// Running node handle, present once `start()` has been called. handle: Mutex>, } +impl Default for MyceliumService { + fn default() -> Self { + MyceliumService { + handle: Mutex::new(None), + } + } +} + impl MyceliumService { pub fn new() -> Self { - MyceliumService { - handle: Mutex::new(None), - } + Self::default() } } @@ -49,7 +58,43 @@ impl IMyceliumService for MyceliumService { info!("starting mycelium node, {} peers", peers.len()); - match NodeHandle::start(peers.to_vec(), key, enable_dns) { + let endpoints = peers.iter().filter_map(|p| p.parse().ok()).collect(); + + #[cfg(target_os = "android")] + let tun_fd = crate::node_handle::create_tun_fd("tun0").map_err(|e| { + error!("Failed to create TUN fd: {e}"); + rsbinder::Status::from(StatusCode::Unknown) + })?; + + let config = Config { + node_key: crypto::SecretKey::from(key), + peers: endpoints, + no_tun: false, + #[cfg(any( + target_os = "linux", + all(target_os = "macos", not(feature = "mactunfd")), + target_os = "windows" + ))] + tun_name: "tun0".to_string(), + #[cfg(any( + target_os = "android", + target_os = "ios", + all(target_os = "macos", feature = "mactunfd"), + ))] + tun_fd: Some(tun_fd), + tcp_listen_port: 9651, + quic_listen_port: Some(9651), + peer_discovery_port: 9650, + peer_discovery_mode: PeerDiscoveryMode::Disabled, + metrics: NoMetrics, + private_network_config: None, + firewall_mark: None, + update_workers: 1, + cdn_cache: None, + enable_dns, + }; + + match NodeHandle::start(config) { Ok(h) => { *self.handle.lock().unwrap() = Some(h); Ok(true) @@ -125,7 +170,7 @@ impl IMyceliumService for MyceliumService { .await .peer_info() .into_iter() - .map(peer_stats_to_aidl) + .map(PeerInfo::from) .collect()) }) } @@ -136,7 +181,7 @@ impl IMyceliumService for MyceliumService { .as_ref() .ok_or_else(|| rsbinder::Status::from(StatusCode::InvalidOperation))?; let endpoint = endpoint - .parse::() + .parse::() .map_err(|_| rsbinder::Status::from(StatusCode::BadValue))?; h.rt() .block_on(async { Ok(h.node().lock().await.add_peer(endpoint).is_ok()) }) @@ -148,7 +193,7 @@ impl IMyceliumService for MyceliumService { .as_ref() .ok_or_else(|| rsbinder::Status::from(StatusCode::InvalidOperation))?; let endpoint = endpoint - .parse::() + .parse::() .map_err(|_| rsbinder::Status::from(StatusCode::BadValue))?; h.rt() .block_on(async { Ok(h.node().lock().await.remove_peer(endpoint).is_ok()) }) @@ -165,16 +210,7 @@ impl IMyceliumService for MyceliumService { .await .selected_routes() .into_iter() - .map(|r| Route { - subnet: r.source().subnet().to_string(), - nextHop: r.neighbour().connection_identifier().clone(), - metric: if r.metric().is_infinite() { - -1 - } else { - u16::from(r.metric()) as i64 - }, - seqno: u16::from(r.seqno()) as i32, - }) + .map(Route::from) .collect()) }) } @@ -190,16 +226,7 @@ impl IMyceliumService for MyceliumService { .await .fallback_routes() .into_iter() - .map(|r| Route { - subnet: r.source().subnet().to_string(), - nextHop: r.neighbour().connection_identifier().clone(), - metric: if r.metric().is_infinite() { - -1 - } else { - u16::from(r.metric()) as i64 - }, - seqno: u16::from(r.seqno()) as i32, - }) + .map(Route::from) .collect()) }) } @@ -233,40 +260,22 @@ impl IMyceliumService for MyceliumService { h.rt().block_on(async { let stats = h.node().lock().await.packet_statistics(); Ok(PacketStats { - bySource: stats - .by_source - .into_iter() - .map(|e| PacketStatEntry { - ip: e.ip.to_string(), - packetCount: e.packet_count as i64, - byteCount: e.byte_count as i64, - }) - .collect(), - byDestination: stats - .by_destination - .into_iter() - .map(|e| PacketStatEntry { - ip: e.ip.to_string(), - packetCount: e.packet_count as i64, - byteCount: e.byte_count as i64, - }) - .collect(), + bySource: stats.by_source.into_iter().map(PacketStatEntry::from).collect(), + byDestination: stats.by_destination.into_iter().map(PacketStatEntry::from).collect(), }) }) } fn generateSecretKey(&self) -> rsbinder::status::Result> { - Ok(mycelium::crypto::SecretKey::new().as_bytes().to_vec()) + Ok(crypto::SecretKey::new().as_bytes().to_vec()) } fn addressFromSecretKey(&self, key: &[u8]) -> rsbinder::status::Result { let arr: [u8; 32] = key .try_into() .map_err(|_| rsbinder::Status::from(StatusCode::BadValue))?; - let secret = mycelium::crypto::SecretKey::from(arr); - Ok(mycelium::crypto::PublicKey::from(&secret) - .address() - .to_string()) + let secret = crypto::SecretKey::from(arr); + Ok(crypto::PublicKey::from(&secret).address().to_string()) } fn startProxyProbe(&self) -> rsbinder::status::Result { @@ -320,7 +329,6 @@ impl IMyceliumService for MyceliumService { h.rt().block_on(async { let future = h.node().lock().await.connect_proxy(remote_addr); - // MutexGuard released before await match future.await { Ok(addr) => Ok(addr.to_string()), Err(e) => { diff --git a/mycelium-android/src/service_manager.rs b/mycelium/src/aidl/service_manager.rs similarity index 62% rename from mycelium-android/src/service_manager.rs rename to mycelium/src/aidl/service_manager.rs index c4c0475..917ac68 100644 --- a/mycelium-android/src/service_manager.rs +++ b/mycelium/src/aidl/service_manager.rs @@ -1,3 +1,10 @@ +//! Android 15 ServiceManager registration workaround. +//! +//! rsbinder 0.6 doesn't have a correct IServiceManager AIDL for Android 15 +//! (API 35), which inserted `getService2` between `getService` and +//! `checkService`, shifting transaction codes. We generate our own bindings +//! from a corrected AIDL file. + include!(concat!(env!("OUT_DIR"), "/service_manager_a15.rs")); use android::os::IServiceManager::{BpServiceManager, IServiceManager}; @@ -5,6 +12,7 @@ use rsbinder::{ProcessState, Proxy, SIBinder}; const DUMP_FLAG_PRIORITY_DEFAULT: i32 = 1 << 3; +/// Register a Binder service with Android's ServiceManager. pub fn add_service(name: &str, binder: SIBinder) -> Result<(), rsbinder::status::Status> { let context = ProcessState::as_self() .context_object() diff --git a/mycelium/src/lib.rs b/mycelium/src/lib.rs index 2101c6a..0284af5 100644 --- a/mycelium/src/lib.rs +++ b/mycelium/src/lib.rs @@ -49,6 +49,9 @@ mod rr_cache; mod seqno_cache; mod sequence_number; mod source_table; +#[cfg(feature = "aidl")] +pub mod aidl; +pub mod node_handle; pub mod subnet; pub mod task; mod tun; diff --git a/mycelium/src/metrics.rs b/mycelium/src/metrics.rs index f72bf9b..eb25cb9 100644 --- a/mycelium/src/metrics.rs +++ b/mycelium/src/metrics.rs @@ -233,3 +233,8 @@ pub trait Metrics { #[inline] fn router_incoming_packet_queue_full_subnet(&self) {} } + +/// A no-op [`Metrics`] implementation that discards all metrics. +#[derive(Clone)] +pub struct NoMetrics; +impl Metrics for NoMetrics {} diff --git a/mycelium-android/src/node.rs b/mycelium/src/node_handle.rs similarity index 57% rename from mycelium-android/src/node.rs rename to mycelium/src/node_handle.rs index 63ae99a..2d34343 100644 --- a/mycelium-android/src/node.rs +++ b/mycelium/src/node_handle.rs @@ -1,16 +1,27 @@ +//! Synchronous wrapper around a [`Node`] running on a background Tokio runtime. +//! +//! This module provides [`NodeHandle`], which spawns a mycelium node in a +//! dedicated thread with its own Tokio runtime. All access goes through the +//! handle, which exposes blocking methods suitable for use from non-async +//! contexts (AIDL Binder threads, C FFI, etc.). + use std::net::SocketAddr; use std::sync::Arc; -use mycelium::peer_manager::{PeerDiscoveryMode, PeerStats}; -use mycelium::{crypto, metrics::Metrics, Config, Node}; use tokio::sync::Mutex; use tracing::{error, info}; -// ── Error type ──────────────────────────────────────────────────────────────── +use crate::metrics::NoMetrics; +use crate::{Config, Node}; +// ── Error type ────────────────────────────────────────────────────────────── + +/// Errors that can occur when starting a [`NodeHandle`]. #[derive(Debug)] pub enum NodeError { + /// The background thread panicked or failed to start the Tokio runtime. ThreadPanic, + /// The mycelium [`Node`] could not be created. NodeCreate(String), } @@ -25,22 +36,13 @@ impl std::fmt::Display for NodeError { impl std::error::Error for NodeError {} -// ── NoMetrics ───────────────────────────────────────────────────────────────── - -#[derive(Clone)] -pub struct NoMetrics; -impl Metrics for NoMetrics {} - -// ── TUN setup (Android only) ────────────────────────────────────────────────── +// ── TUN setup (Android only) ──────────────────────────────────────────────── /// On Android the `tun` crate expects an already-opened file descriptor. /// This function opens `/dev/tun`, configures the interface with TUNSETIFF, /// and returns the raw fd. Requires `CAP_NET_ADMIN`. -/// -/// NOTE: If we continue with this approach, it's better to change the TUN code inside mycelium to do this, similar to linux. #[cfg(target_os = "android")] -fn create_tun_fd(tun_name: &str) -> Result { - // TUNSETIFF = _IOW('T', 202, int) = 0x400454ca +pub fn create_tun_fd(tun_name: &str) -> Result { const TUNSETIFF: libc::c_ulong = 0x400454ca; const IFF_TUN: libc::c_short = 0x0001; const IFF_NO_PI: libc::c_short = 0x1000; @@ -50,10 +52,9 @@ fn create_tun_fd(tun_name: &str) -> Result { return Err(std::io::Error::last_os_error()); } - // struct ifreq: char[16] name, then union starting with short flags let mut ifr = [0u8; 40]; let name_bytes = tun_name.as_bytes(); - let len = name_bytes.len().min(15); // IFNAMSIZ - 1, leave room for null + let len = name_bytes.len().min(15); ifr[..len].copy_from_slice(&name_bytes[..len]); let flags: i16 = IFF_TUN | IFF_NO_PI; ifr[16] = (flags & 0xff) as u8; @@ -69,10 +70,11 @@ fn create_tun_fd(tun_name: &str) -> Result { Ok(fd) } -// ── NodeHandle ───────────────────────────────────────────────────────────────── +// ── NodeHandle ────────────────────────────────────────────────────────────── /// Handle to a running mycelium node. Holds a reference to the node for direct -/// method calls and the Tokio runtime handle to drive them from Binder threads. +/// method calls and the Tokio runtime handle to drive them from non-async +/// threads (e.g. Binder threads, C FFI callbacks). pub struct NodeHandle { node: Arc>>, rt_handle: tokio::runtime::Handle, @@ -81,12 +83,10 @@ pub struct NodeHandle { impl NodeHandle { /// Spawn the Tokio runtime and mycelium node in a background thread. + /// /// Blocks the calling thread until the node is ready (or fails to start). - pub fn start( - peers: Vec, - priv_key: [u8; 32], - enable_dns: bool, - ) -> Result { + /// The caller provides a fully constructed [`Config`]. + pub fn start(config: Config) -> Result { let (result_tx, result_rx) = std::sync::mpsc::sync_channel(1); let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>(); @@ -99,8 +99,6 @@ impl NodeHandle { Ok(rt) => rt, Err(e) => { error!("Failed to build Tokio runtime: {e}"); - // Can't send NodeCreate error here since we don't have the box yet, - // but ThreadPanic is close enough for a runtime build failure. let _ = result_tx.send(Err(NodeError::ThreadPanic)); return; } @@ -109,49 +107,6 @@ impl NodeHandle { let handle = rt.handle().clone(); rt.block_on(async move { - let endpoints = peers - .iter() - .filter_map(|p| p.parse().ok()) - .collect::>(); - - #[cfg(target_os = "android")] - let tun_fd = match create_tun_fd("tun0") { - Ok(fd) => fd, - Err(e) => { - error!("Failed to create TUN fd: {e}"); - let _ = result_tx.send(Err(NodeError::NodeCreate(e.to_string()))); - return; - } - }; - - let config = Config { - node_key: crypto::SecretKey::from(priv_key), - peers: endpoints, - no_tun: false, - #[cfg(any( - target_os = "linux", - all(target_os = "macos", not(feature = "mactunfd")), - target_os = "windows" - ))] - tun_name: "tun0".to_string(), - #[cfg(any( - target_os = "android", - target_os = "ios", - all(target_os = "macos", feature = "mactunfd"), - ))] - tun_fd: Some(tun_fd), - tcp_listen_port: 9651, - quic_listen_port: Some(9651), - peer_discovery_port: 9650, - peer_discovery_mode: PeerDiscoveryMode::Disabled, - metrics: NoMetrics, - private_network_config: None, - firewall_mark: None, - update_workers: 1, - cdn_cache: None, - enable_dns, - }; - match Node::new(config).await { Err(e) => { error!("Failed to create node: {e}"); @@ -161,7 +116,6 @@ impl NodeHandle { info!("mycelium node started"); let node = Arc::new(Mutex::new(node)); let _ = result_tx.send(Ok((Arc::clone(&node), handle))); - // Keep the runtime alive until stop() is called. let _ = shutdown_rx.await; info!("mycelium node stopped"); } @@ -186,38 +140,27 @@ impl NodeHandle { } } + /// Returns `true` if the node has not been stopped. pub fn is_running(&self) -> bool { self.shutdown_tx.is_some() } + /// Reference to the underlying [`Node`], wrapped in an async [`Mutex`]. pub fn node(&self) -> &Arc>> { &self.node } + /// Handle to the background Tokio runtime. Use this with + /// [`block_on`](tokio::runtime::Handle::block_on) to drive async node + /// methods from synchronous code. pub fn rt(&self) -> &tokio::runtime::Handle { &self.rt_handle } } -// ── Helpers ─────────────────────────────────────────────────────────────────── +// ── Helpers ───────────────────────────────────────────────────────────────── -/// Convert a [`PeerStats`] to the AIDL-generated `PeerInfo` parcelable. -// rsbinder-aidl generates a sub-module with the same name as the parcelable, -// so the type is PeerInfo::PeerInfo. -pub fn peer_stats_to_aidl(p: PeerStats) -> crate::tech::threefold::mycelium::PeerInfo::PeerInfo { - crate::tech::threefold::mycelium::PeerInfo::PeerInfo { - protocol: p.endpoint.proto().to_string(), - address: p.endpoint.address().to_string(), - peerType: p.pt.to_string(), - connectionState: p.connection_state.to_string(), - rxBytes: p.rx_bytes as i64, - txBytes: p.tx_bytes as i64, - discoveredSeconds: p.discovered as i64, - lastConnectedSeconds: p.last_connected.map(|v| v as i64).unwrap_or(-1), - } -} - -/// Parse a proxy address string ("ip:port") into a [`SocketAddr`]. +/// Parse a proxy address string (`"ip:port"`) into a [`SocketAddr`]. /// Returns `None` for an empty string (auto-select). pub fn parse_proxy_remote(remote: &str) -> Result, String> { if remote.is_empty() {