diff --git a/.github/workflows/_rust.yml b/.github/workflows/_rust.yml index 49c6a889c..9c726113d 100644 --- a/.github/workflows/_rust.yml +++ b/.github/workflows/_rust.yml @@ -78,8 +78,7 @@ jobs: fail-fast: false matrix: include: - # Awaiting another PR to generalize the smoke test for Ubuntu - #- runs-on: ubuntu-20.04 + - runs-on: ubuntu-20.04 # Broken on 22.04 #- runs-on: ubuntu-22.04 - runs-on: windows-2019 diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f4f6b76e9..169baa8d2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -197,13 +197,13 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 4.0.3", - "event-listener-strategy", + "event-listener 5.1.0", + "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", ] @@ -279,7 +279,7 @@ dependencies = [ "futures-io", "futures-lite 2.2.0", "parking", - "polling 3.3.2", + "polling 3.5.0", "rustix 0.38.31", "slab", "tracing", @@ -302,7 +302,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", - "event-listener-strategy", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -331,7 +331,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -366,7 +366,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -446,7 +446,7 @@ dependencies = [ "http 1.0.0", "http-body 1.0.0", "http-body-util", - "hyper 1.1.0", + "hyper 1.2.0", "hyper-util", "itoa 1.0.10", "matchit", @@ -698,9 +698,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytecodec" @@ -724,9 +724,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.14.1" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2490600f404f2b94c167e31d3ed1d5f3c225a0f3b80230053b3e0b7b962bd9" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" [[package]] name = "byteorder" @@ -796,12 +796,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" [[package]] name = "cesu8" @@ -840,9 +837,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.6" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" dependencies = [ "smallvec 1.13.1", "target-lexicon", @@ -880,15 +877,15 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -924,9 +921,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.18" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -934,33 +931,33 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clipboard-win" @@ -1269,9 +1266,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -1345,17 +1342,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "ctor" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" +checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1370,9 +1367,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.1" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" dependencies = [ "cfg-if", "cpufeatures", @@ -1392,14 +1389,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "darling" -version = "0.20.5" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ "darling_core", "darling_macro", @@ -1407,27 +1404,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.5" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim", - "syn 2.0.48", + "strsim 0.10.0", + "syn 2.0.51", ] [[package]] name = "darling_macro" -version = "0.20.5" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1496,6 +1493,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -1506,6 +1512,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1571,9 +1589,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "embed-resource" @@ -1584,7 +1602,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.9", + "toml 0.8.10", "vswhom", "winreg 0.51.0", ] @@ -1613,14 +1631,14 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "enumflags2" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" +checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" dependencies = [ "enumflags2_derive", "serde", @@ -1628,13 +1646,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" +checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -1668,9 +1686,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc" +checksum = "26a147e1a6641a55d994b3e4e9fa4d9b180c8d652c09b363af8c9bf1b8e04139" [[package]] name = "event-listener" @@ -1700,6 +1718,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ad6fd685ce13acd6d9541a30f6db6567a7a24c9ffd4ba2955d29e3f22c8b27" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" version = "0.4.0" @@ -1710,6 +1739,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.1.0", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -1736,9 +1775,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" +checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" [[package]] name = "field-offset" @@ -1819,7 +1858,9 @@ dependencies = [ "connlib-client-shared", "connlib-shared", "crash-handler", + "dirs", "firezone-cli-utils", + "futures", "git-version", "hex", "hostname", @@ -2005,7 +2046,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2133,7 +2174,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2150,9 +2191,9 @@ checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" @@ -2375,7 +2416,7 @@ checksum = "53010ccb100b96a67bc32c0175f0ed1426b31b655d562898e57325f81c023ac0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -2455,13 +2496,13 @@ dependencies = [ [[package]] name = "goblin" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134" +checksum = "bb07a4ffed2093b118a525b1d8f5204ae274faed5604537caf7135d0f18d9887" dependencies = [ "log", "plain", - "scroll 0.11.0", + "scroll", ] [[package]] @@ -2531,7 +2572,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.11", - "indexmap 2.2.2", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -2550,7 +2591,7 @@ dependencies = [ "futures-sink", "futures-util", "http 1.0.0", - "indexmap 2.2.2", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -2586,9 +2627,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" [[package]] name = "hex" @@ -2794,7 +2835,7 @@ dependencies = [ "httpdate", "itoa 1.0.10", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.6", "tokio", "tower-service", "tracing", @@ -2803,9 +2844,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" dependencies = [ "bytes", "futures-channel", @@ -2817,6 +2858,7 @@ dependencies = [ "httpdate", "itoa 1.0.10", "pin-project-lite", + "smallvec 1.13.1", "tokio", ] @@ -2856,7 +2898,7 @@ dependencies = [ "futures-util", "http 1.0.0", "http-body 1.0.0", - "hyper 1.1.0", + "hyper 1.2.0", "pin-project-lite", "socket2 0.5.6", "tokio", @@ -2864,9 +2906,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2929,9 +2971,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", @@ -2952,9 +2994,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -3044,12 +3086,12 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", - "rustix 0.38.31", + "libc", "windows-sys 0.52.0", ] @@ -3165,9 +3207,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -3461,9 +3503,9 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" -version = "0.8.0" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] @@ -3494,24 +3536,24 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minidump-common" -version = "0.19.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dbc11dfb55b3b7b5684fb16d98e0fc9d1e93a64d6b00bf383eabfc4541aaac2" +checksum = "74b1a2187f9aea1134ed3cf96b3827283aabc67dc95ed502e7072b515a1c770c" dependencies = [ "bitflags 2.4.2", "debugid", "num-derive", "num-traits", "range-map", - "scroll 0.11.0", + "scroll", "smart-default", ] [[package]] name = "minidump-writer" -version = "0.8.3" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "017101620fe5d413ac2d98224ab8b1fff0d4bacaf2803c130ad4a6db3e5d3e70" +checksum = "1b1b7d3eaaf4a4bf24af8f59d0f3ad31a3cdd82a53f275c328101688334438c2" dependencies = [ "bitflags 2.4.2", "byteorder", @@ -3525,7 +3567,7 @@ dependencies = [ "minidump-common", "nix 0.27.1", "procfs-core", - "scroll 0.11.0", + "scroll", "tempfile", "thiserror", ] @@ -3542,8 +3584,8 @@ dependencies = [ "log", "minidump-writer", "parking_lot", - "polling 3.3.2", - "scroll 0.12.0", + "polling 3.5.0", + "scroll", "thiserror", "uds", ] @@ -3556,9 +3598,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -3791,9 +3833,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] @@ -3806,30 +3848,29 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -3850,9 +3891,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -4058,6 +4099,12 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "3.9.2" @@ -4284,7 +4331,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -4350,7 +4397,7 @@ checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -4378,9 +4425,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plain" @@ -4401,7 +4448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ "base64 0.21.7", - "indexmap 2.2.2", + "indexmap 2.2.3", "line-wrap", "quick-xml 0.31.0", "serde", @@ -4426,7 +4473,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -4452,9 +4499,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.11" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -4481,9 +4528,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.2" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" +checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" dependencies = [ "cfg-if", "concurrent-queue", @@ -4935,7 +4982,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.25.3", + "webpki-roots 0.25.4", "winreg 0.50.0", ] @@ -5075,7 +5122,7 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.1", + "rustls-webpki 0.102.2", "subtle", "zeroize", ] @@ -5091,9 +5138,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" +checksum = "5ede67b28608b4c60685c7d54122d4400d90f62b40caee7700e700380a390fa8" [[package]] name = "rustls-webpki" @@ -5107,9 +5154,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.1" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", "rustls-pki-types", @@ -5136,9 +5183,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "sadness-generator" @@ -5176,33 +5223,13 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scroll" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" -dependencies = [ - "scroll_derive 0.11.1", -] - [[package]] name = "scroll" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" dependencies = [ - "scroll_derive 0.12.0", -] - -[[package]] -name = "scroll_derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", + "scroll_derive", ] [[package]] @@ -5213,7 +5240,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -5331,9 +5358,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -5351,21 +5378,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ + "indexmap 2.2.3", "itoa 1.0.10", "ryu", "serde", @@ -5379,7 +5407,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -5405,16 +5433,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b0ed1662c5a68664f45b76d18deb0e234aff37207086803165c961eb695e981" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" dependencies = [ "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.2", + "indexmap 2.2.3", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -5422,14 +5451,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568577ff0ef47b879f736cd66740e022f3672788cdf002a05a4e609ea5a6fb15" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -5581,7 +5610,7 @@ checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -5752,6 +5781,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "structmeta" version = "0.2.0" @@ -5761,7 +5796,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -5772,7 +5807,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -5854,9 +5889,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" dependencies = [ "proc-macro2", "quote", @@ -5909,10 +5944,10 @@ version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" dependencies = [ - "cfg-expr 0.15.6", + "cfg-expr 0.15.7", "heck 0.4.1", "pkg-config", - "toml 0.8.9", + "toml 0.8.10", "version-compare 0.1.1", ] @@ -5999,19 +6034,20 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.13" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tauri" -version = "1.5.4" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd27c04b9543776a972c86ccf70660b517ecabbeced9fb58d8b961a13ad129af" +checksum = "0da520ff07c0745199204f7a7a62a8c6ee1666313b792b051ca170eca04649aa" dependencies = [ "anyhow", "cocoa 0.24.1", "dirs-next", + "dunce", "embed_plist", "encoding_rs", "flate2", @@ -6133,9 +6169,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cae61fbc731f690a4899681c9052dde6d05b159b44563ace8186fc1bfb7d158" +checksum = "a561ae38c6b27510c77a3ab4cf65bebe18fba51ca4569e023fb9e194ff4995fb" dependencies = [ "cocoa 0.24.1", "gtk", @@ -6242,7 +6278,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -6268,14 +6304,14 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -6364,7 +6400,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -6412,7 +6448,7 @@ dependencies = [ "tokio", "tokio-rustls 0.25.0", "tungstenite", - "webpki-roots 0.26.0", + "webpki-roots 0.26.1", ] [[package]] @@ -6452,14 +6488,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.21.1", + "toml_edit 0.22.6", ] [[package]] @@ -6477,24 +6513,24 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.2", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.21.1" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" dependencies = [ - "indexmap 2.2.2", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.2", ] [[package]] @@ -6600,7 +6636,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -6770,9 +6806,9 @@ dependencies = [ [[package]] name = "treediff" -version = "4.0.2" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" +checksum = "4d127780145176e2b5d16611cc25a900150e86e9fd79d3bde6ff3a37359c9cb5" dependencies = [ "serde_json", ] @@ -6850,18 +6886,18 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "universal-hash" @@ -7021,9 +7057,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -7031,24 +7067,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -7058,9 +7094,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7068,22 +7104,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "wasm-streams" @@ -7100,9 +7136,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -7157,15 +7193,15 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "webpki-roots" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de2cfda980f21be5a7ed2eadb3e6fe074d56022bea2cdeb1a62eb220fc04188" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" dependencies = [ "rustls-pki-types", ] @@ -7322,7 +7358,7 @@ dependencies = [ "windows-core 0.52.0", "windows-implement 0.52.0", "windows-interface", - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -7350,7 +7386,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -7371,7 +7407,7 @@ checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -7382,7 +7418,7 @@ checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -7443,7 +7479,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -7478,17 +7514,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -7503,7 +7539,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -7520,9 +7556,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -7556,9 +7592,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -7592,9 +7628,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -7628,9 +7664,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -7664,9 +7700,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -7682,9 +7718,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -7718,15 +7754,24 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "winnow" -version = "0.5.36" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818ce546a11a9986bc24f93d0cdf38a8a1a400f1473ea8c82e59f6e0ffab9249" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" dependencies = [ "memchr", ] @@ -7852,9 +7897,9 @@ checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" [[package]] name = "x25519-dalek" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", "rand_core 0.6.4", @@ -7875,19 +7920,19 @@ dependencies = [ [[package]] name = "xdg-home" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" +checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" dependencies = [ - "nix 0.26.4", + "libc", "winapi", ] [[package]] name = "zbus" -version = "3.14.1" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" +checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" dependencies = [ "async-broadcast", "async-executor", @@ -7926,9 +7971,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.14.1" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" +checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7966,7 +8011,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.51", ] [[package]] @@ -7984,9 +8029,9 @@ dependencies = [ [[package]] name = "zvariant" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" +checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" dependencies = [ "byteorder", "enumflags2", @@ -7998,9 +8043,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "3.15.0" +version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" +checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/rust/gui-client/src-tauri/Cargo.toml b/rust/gui-client/src-tauri/Cargo.toml index beffaf0ab..9cb361c72 100644 --- a/rust/gui-client/src-tauri/Cargo.toml +++ b/rust/gui-client/src-tauri/Cargo.toml @@ -46,21 +46,24 @@ thiserror = { version = "1.0", default-features = false } tokio = { version = "1.36.0", features = ["time"] } tracing = { workspace = true } tracing-log = "0.2" +tracing-panic = "0.1.1" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } url = { version = "2.5.0", features = ["serde"] } uuid = { version = "1.7.0", features = ["v4"] } -tracing-panic = "0.1.1" zip = { version = "0.6.6", features = ["deflate", "time"], default-features = false } -# These dependencies are locked behind `cfg(windows)` because they either can't compile at all on Linux, or they need native dependencies like glib that are difficult to get. Try not to add more here. +[target.'cfg(target_os = "linux")'.dependencies] +dirs = "5.0.1" +# Used for infinite `pending` on not-yet-implemented functions +futures = "0.3.30" -[target.'cfg(windows)'.dependencies] +[target.'cfg(target_os = "windows")'.dependencies] tauri-winrt-notification = "0.1.3" windows-implement = "0.52.0" winreg = "0.52.0" wintun = "0.4.0" -[target.'cfg(windows)'.dependencies.windows] +[target.'cfg(target_os = "windows")'.dependencies.windows] version = "0.52.0" features = [ # For implementing COM interfaces diff --git a/rust/gui-client/src-tauri/src/client.rs b/rust/gui-client/src-tauri/src/client.rs index d695658f3..76fd1aa18 100644 --- a/rust/gui-client/src-tauri/src/client.rs +++ b/rust/gui-client/src-tauri/src/client.rs @@ -1,6 +1,6 @@ use anyhow::Result; use clap::{Args, Parser}; -use std::{os::windows::process::CommandExt, path::PathBuf, process::Command}; +use std::path::PathBuf; mod about; mod auth; @@ -16,6 +16,8 @@ mod network_changes; mod resolvers; mod settings; mod updates; + +#[cfg(target_os = "windows")] mod wintun_install; /// Output of `git describe` at compile time @@ -35,10 +37,6 @@ pub(crate) enum Error { Gui(#[from] gui::Error), } -// Hides Powershell's console on Windows -// -const CREATE_NO_WINDOW: u32 = 0x08000000; - /// The program's entry point, equivalent to `main` /// /// When a user runs the Windows client normally without admin permissions, this will happen: @@ -69,22 +67,8 @@ pub(crate) fn run() -> Result<()> { // We're already elevated, just run the GUI run_gui(cli) } else { - // We're not elevated, ask Powershell to re-launch us, then exit - let current_exe = tauri_utils::platform::current_exe()?; - if current_exe.display().to_string().contains('\"') { - anyhow::bail!("The exe path must not contain double quotes, it makes it hard to elevate with Powershell"); - } - Command::new("powershell") - .creation_flags(CREATE_NO_WINDOW) - .arg("-Command") - .arg("Start-Process") - .arg("-FilePath") - .arg(format!(r#""{}""#, current_exe.display())) - .arg("-Verb") - .arg("RunAs") - .arg("-ArgumentList") - .arg("elevated") - .spawn()?; + // We're not elevated, ask Powershell / sudo to re-launch us, then exit + elevation::elevate()?; Ok(()) } } diff --git a/rust/gui-client/src-tauri/src/client/crash_handling.rs b/rust/gui-client/src-tauri/src/client/crash_handling.rs index 8de13bfe3..18134fe64 100755 --- a/rust/gui-client/src-tauri/src/client/crash_handling.rs +++ b/rust/gui-client/src-tauri/src/client/crash_handling.rs @@ -69,6 +69,12 @@ fn start_server_and_connect() -> Result<(minidumper::Client, std::process::Child let socket_path = known_dirs::runtime() .context("`known_dirs::runtime` failed")? .join("crash_handler_pipe"); + std::fs::create_dir_all( + socket_path + .parent() + .context("`known_dirs::runtime` should have a parent")?, + ) + .context("Failed to create dir for crash_handler_pipe")?; let mut server = None; @@ -118,11 +124,13 @@ impl minidumper::ServerHandler for Handler { .expect("Should be able to find logs dir to put crash dump in") .join("last_crash.dmp"); - if let Some(dir) = dump_path.parent() { - if !dir.try_exists()? { - std::fs::create_dir_all(dir)?; - } - } + // `tracing` is unlikely to work inside the crash handler subprocess, so + // just print to stderr and it may show up on the terminal. This helps in CI / local dev. + eprintln!("Creating minidump at {}", dump_path.display()); + let Some(dir) = dump_path.parent() else { + return Err(std::io::ErrorKind::NotFound.into()); + }; + std::fs::create_dir_all(dir)?; let file = File::create(&dump_path)?; Ok((file, dump_path)) } diff --git a/rust/gui-client/src-tauri/src/client/debug_commands.rs b/rust/gui-client/src-tauri/src/client/debug_commands.rs index 75a1c4b60..93a6f7aca 100644 --- a/rust/gui-client/src-tauri/src/client/debug_commands.rs +++ b/rust/gui-client/src-tauri/src/client/debug_commands.rs @@ -10,7 +10,6 @@ pub enum Cmd { Crash, Hostname, NetworkChanges, - Wintun, } pub fn run(cmd: Cmd) -> Result<()> { @@ -19,7 +18,6 @@ pub fn run(cmd: Cmd) -> Result<()> { Cmd::Crash => crash(), Cmd::Hostname => hostname(), Cmd::NetworkChanges => client::network_changes::run_debug(), - Cmd::Wintun => wintun(), } } @@ -48,15 +46,3 @@ fn hostname() -> Result<()> { ); Ok(()) } - -/// Try to load wintun.dll and throw an error if it's not in the right place -fn wintun() -> Result<()> { - tracing_subscriber::fmt::init(); - let path = connlib_shared::windows::wintun_dll_path()?; - // SAFETY: Loading a DLL from disk is unsafe. We're responsible for making sure - // we're using the DLL's API correctly. - unsafe { wintun::load_from_path(&path) }?; - tracing::info!(?path, "Loaded wintun.dll"); - - Ok(()) -} diff --git a/rust/gui-client/src-tauri/src/client/deep_link.rs b/rust/gui-client/src-tauri/src/client/deep_link.rs index 02adbb433..3d110b3ce 100644 --- a/rust/gui-client/src-tauri/src/client/deep_link.rs +++ b/rust/gui-client/src-tauri/src/client/deep_link.rs @@ -1,15 +1,22 @@ //! A module for registering, catching, and parsing deep links that are sent over to the app's already-running instance -//! Based on reading some of the Windows code from , which is licensed "MIT OR Apache-2.0" use crate::client::auth::Response as AuthResponse; -use connlib_shared::{control::SecureUrl, BUNDLE_ID}; +use connlib_shared::control::SecureUrl; use secrecy::{ExposeSecret, Secret, SecretString}; -use std::{ffi::c_void, io, path::Path, str::FromStr}; -use tokio::{io::AsyncReadExt, io::AsyncWriteExt, net::windows::named_pipe}; -use windows::Win32::Security as WinSec; +use std::io; pub(crate) const FZ_SCHEME: &str = "firezone-fd0020211111"; +#[cfg(target_os = "linux")] +#[path = "deep_link/linux.rs"] +mod imp; + +#[cfg(target_os = "windows")] +#[path = "deep_link/windows.rs"] +mod imp; + +// TODO: Replace this all for `anyhow`. +#[cfg_attr(target_os = "linux", allow(dead_code))] #[derive(thiserror::Error, Debug)] pub enum Error { #[error("named pipe server couldn't start listening, we are probably the second instance")] @@ -26,6 +33,7 @@ pub enum Error { /// We got some data but it's not UTF-8 #[error(transparent)] LinkNotUtf8(std::str::Utf8Error), + #[cfg(target_os = "windows")] #[error("Couldn't set up security descriptor for deep link server")] SecurityDescriptor, /// Error from server's POV @@ -34,10 +42,13 @@ pub enum Error { #[error(transparent)] UrlParse(#[from] url::ParseError), /// Something went wrong setting up the registry + #[cfg(target_os = "windows")] #[error(transparent)] WindowsRegistry(io::Error), } +pub(crate) use imp::{open, register, Server}; + pub(crate) fn parse_auth_callback(url: &Secret) -> Option { let url = &url.expose_secret().inner; match url.host() { @@ -86,163 +97,6 @@ pub(crate) fn parse_auth_callback(url: &Secret) -> Option Result { - // This isn't air-tight - We recreate the whole server on each loop, - // rather than binding 1 socket and accepting many streams like a normal socket API. - // I can only assume Tokio is following Windows' underlying API. - - // We could instead pick an ephemeral TCP port and write that to a file, - // akin to how Unix processes will write their PID to a file to manage long-running instances - // But this doesn't require us to listen on TCP. - - let mut server_options = named_pipe::ServerOptions::new(); - server_options.first_pipe_instance(true); - - // This will allow non-admin clients to connect to us even if we're running as admin - let mut sd = WinSec::SECURITY_DESCRIPTOR::default(); - let psd = WinSec::PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut c_void); - // SAFETY: Unsafe needed to call Win32 API. There shouldn't be any threading - // or lifetime problems because we only pass pointers to our local vars to - // Win32, and Win32 shouldn't save them anywhere. - unsafe { - // ChatGPT pointed me to these functions, it's better than the official MS docs - WinSec::InitializeSecurityDescriptor( - psd, - windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION, - ) - .map_err(|_| Error::SecurityDescriptor)?; - WinSec::SetSecurityDescriptorDacl(psd, true, None, false) - .map_err(|_| Error::SecurityDescriptor)?; - } - - let mut sa = WinSec::SECURITY_ATTRIBUTES { - // TODO: Try `size_of_val` here instead - nLength: std::mem::size_of::() - .try_into() - .unwrap(), - lpSecurityDescriptor: psd.0, - bInheritHandle: false.into(), - }; - - // TODO: On the IPC branch I found that this will cause prefix issues - // with other named pipes. Change it. - let path = named_pipe_path(BUNDLE_ID); - let sa_ptr = &mut sa as *mut _ as *mut c_void; - // SAFETY: Unsafe needed to call Win32 API. There shouldn't be any threading - // or lifetime problems because we only pass pointers to our local vars to - // Win32, and Win32 shouldn't save them anywhere. - let server = unsafe { server_options.create_with_security_attributes_raw(path, sa_ptr) } - .map_err(|_| Error::CantListen)?; - - tracing::debug!("server is bound"); - Ok(Server { inner: server }) - } - - /// Await one incoming deep link from a named pipe client - /// Tokio's API is strange, so this consumes the server. - /// I assume this is based on the underlying Windows API. - /// I tried re-using the server and it acted strange. The official Tokio - /// examples are not clear on this. - pub async fn accept(mut self) -> Result, Error> { - self.inner - .connect() - .await - .map_err(Error::ServerCommunications)?; - tracing::debug!("server got connection"); - - // TODO: Limit the read size here. Our typical callback is 350 bytes, so 4,096 bytes should be more than enough. - // Also, I think `read_to_end` can do partial reads because this is a named pipe, - // not a file. We might need a length-prefixed or newline-terminated format for IPC. - let mut bytes = vec![]; - self.inner - .read_to_end(&mut bytes) - .await - .map_err(Error::ServerCommunications)?; - let bytes = Secret::new(bytes); - - self.inner.disconnect().ok(); - - tracing::debug!("Server read"); - let s = SecretString::from_str( - std::str::from_utf8(bytes.expose_secret()).map_err(Error::LinkNotUtf8)?, - ) - .expect("Infallible"); - let url = Secret::new(SecureUrl::from_url(url::Url::parse(s.expose_secret())?)); - - Ok(url) - } -} - -/// Open a deep link by sending it to the already-running instance of the app -pub async fn open(url: &url::Url) -> Result<(), Error> { - let path = named_pipe_path(BUNDLE_ID); - let mut client = named_pipe::ClientOptions::new() - .open(path) - .map_err(Error::Connect)?; - client - .write_all(url.as_str().as_bytes()) - .await - .map_err(Error::ClientCommunications)?; - Ok(()) -} - -/// Registers the current exe as the handler for our deep link scheme. -/// -/// This is copied almost verbatim from tauri-plugin-deep-link's `register` fn, with an improvement -/// that we send the deep link to a subcommand so the URL won't confuse `clap` -/// -/// * `id` A unique ID for the app, e.g. "com.contoso.todo-list" or "dev.firezone.client" -pub fn register() -> Result<(), Error> { - let exe = tauri_utils::platform::current_exe() - .map_err(Error::CurrentExe)? - .display() - .to_string() - .replace("\\\\?\\", ""); - - set_registry_values(BUNDLE_ID, &exe).map_err(Error::WindowsRegistry)?; - - Ok(()) -} - -/// Set up the Windows registry to call the given exe when our deep link scheme is used -/// -/// All errors from this function are registry-related -fn set_registry_values(id: &str, exe: &str) -> Result<(), io::Error> { - let hkcu = winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER); - let base = Path::new("Software").join("Classes").join(FZ_SCHEME); - - let (key, _) = hkcu.create_subkey(&base)?; - key.set_value("", &format!("URL:{}", id))?; - key.set_value("URL Protocol", &"")?; - - let (icon, _) = hkcu.create_subkey(base.join("DefaultIcon"))?; - icon.set_value("", &format!("{},0", &exe))?; - - let (cmd, _) = hkcu.create_subkey(base.join("shell").join("open").join("command"))?; - cmd.set_value("", &format!("{} open-deep-link \"%1\"", &exe))?; - - Ok(()) -} - -/// Returns a valid name for a Windows named pipe -/// -/// # Arguments -/// -/// * `id` - BUNDLE_ID, e.g. `dev.firezone.client` -fn named_pipe_path(id: &str) -> String { - format!(r"\\.\pipe\{}", id) -} - #[cfg(test)] mod tests { use anyhow::Result; diff --git a/rust/gui-client/src-tauri/src/client/deep_link/linux.rs b/rust/gui-client/src-tauri/src/client/deep_link/linux.rs new file mode 100644 index 000000000..eab1e2bdc --- /dev/null +++ b/rust/gui-client/src-tauri/src/client/deep_link/linux.rs @@ -0,0 +1,30 @@ +//! TODO: Not implemented for Linux yet + +use super::Error; +use connlib_shared::control::SecureUrl; +use secrecy::Secret; + +pub(crate) struct Server {} + +impl Server { + pub(crate) fn new() -> Result { + tracing::warn!("Not implemented yet"); + tracing::trace!(scheme = super::FZ_SCHEME, "prevents dead code warning"); + Ok(Self {}) + } + + pub(crate) async fn accept(self) -> Result, Error> { + tracing::warn!("Deep links not implemented yet on Linux"); + futures::future::pending().await + } +} + +pub(crate) async fn open(_url: &url::Url) -> Result<(), Error> { + tracing::warn!("Not implemented yet"); + Ok(()) +} + +pub(crate) fn register() -> Result<(), Error> { + tracing::warn!("Not implemented yet"); + Ok(()) +} diff --git a/rust/gui-client/src-tauri/src/client/deep_link/windows.rs b/rust/gui-client/src-tauri/src/client/deep_link/windows.rs new file mode 100644 index 000000000..8683a511a --- /dev/null +++ b/rust/gui-client/src-tauri/src/client/deep_link/windows.rs @@ -0,0 +1,164 @@ +//! A module for registering, catching, and parsing deep links that are sent over to the app's already-running instance +//! Based on reading some of the Windows code from , which is licensed "MIT OR Apache-2.0" + +use super::{Error, FZ_SCHEME}; +use connlib_shared::{control::SecureUrl, BUNDLE_ID}; +use secrecy::{ExposeSecret, Secret, SecretString}; +use std::{ffi::c_void, io, path::Path, str::FromStr}; +use tokio::{io::AsyncReadExt, io::AsyncWriteExt, net::windows::named_pipe}; +use windows::Win32::Security as WinSec; + +/// A server for a named pipe, so we can receive deep links from other instances +/// of the client launched by web browsers +pub(crate) struct Server { + inner: named_pipe::NamedPipeServer, +} + +impl Server { + /// Construct a server, but don't await client connections yet + /// + /// Panics if there is no Tokio runtime + pub(crate) fn new() -> Result { + // This isn't air-tight - We recreate the whole server on each loop, + // rather than binding 1 socket and accepting many streams like a normal socket API. + // I can only assume Tokio is following Windows' underlying API. + + // We could instead pick an ephemeral TCP port and write that to a file, + // akin to how Unix processes will write their PID to a file to manage long-running instances + // But this doesn't require us to listen on TCP. + + let mut server_options = named_pipe::ServerOptions::new(); + server_options.first_pipe_instance(true); + + // This will allow non-admin clients to connect to us even if we're running as admin + let mut sd = WinSec::SECURITY_DESCRIPTOR::default(); + let psd = WinSec::PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut c_void); + // SAFETY: Unsafe needed to call Win32 API. There shouldn't be any threading + // or lifetime problems because we only pass pointers to our local vars to + // Win32, and Win32 shouldn't save them anywhere. + unsafe { + // ChatGPT pointed me to these functions, it's better than the official MS docs + WinSec::InitializeSecurityDescriptor( + psd, + windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION, + ) + .map_err(|_| Error::SecurityDescriptor)?; + WinSec::SetSecurityDescriptorDacl(psd, true, None, false) + .map_err(|_| Error::SecurityDescriptor)?; + } + + let mut sa = WinSec::SECURITY_ATTRIBUTES { + // TODO: Try `size_of_val` here instead + nLength: std::mem::size_of::() + .try_into() + .unwrap(), + lpSecurityDescriptor: psd.0, + bInheritHandle: false.into(), + }; + + // TODO: On the IPC branch I found that this will cause prefix issues + // with other named pipes. Change it. + let path = named_pipe_path(BUNDLE_ID); + let sa_ptr = &mut sa as *mut _ as *mut c_void; + // SAFETY: Unsafe needed to call Win32 API. There shouldn't be any threading + // or lifetime problems because we only pass pointers to our local vars to + // Win32, and Win32 shouldn't save them anywhere. + let server = unsafe { server_options.create_with_security_attributes_raw(path, sa_ptr) } + .map_err(|_| Error::CantListen)?; + + tracing::debug!("server is bound"); + Ok(Server { inner: server }) + } + + /// Await one incoming deep link from a named pipe client + /// Tokio's API is strange, so this consumes the server. + /// I assume this is based on the underlying Windows API. + /// I tried re-using the server and it acted strange. The official Tokio + /// examples are not clear on this. + pub(crate) async fn accept(mut self) -> Result, Error> { + self.inner + .connect() + .await + .map_err(Error::ServerCommunications)?; + tracing::debug!("server got connection"); + + // TODO: Limit the read size here. Our typical callback is 350 bytes, so 4,096 bytes should be more than enough. + // Also, I think `read_to_end` can do partial reads because this is a named pipe, + // not a file. We might need a length-prefixed or newline-terminated format for IPC. + let mut bytes = vec![]; + self.inner + .read_to_end(&mut bytes) + .await + .map_err(Error::ServerCommunications)?; + let bytes = Secret::new(bytes); + + self.inner.disconnect().ok(); + + tracing::debug!("Server read"); + let s = SecretString::from_str( + std::str::from_utf8(bytes.expose_secret()).map_err(Error::LinkNotUtf8)?, + ) + .expect("Infallible"); + let url = Secret::new(SecureUrl::from_url(url::Url::parse(s.expose_secret())?)); + + Ok(url) + } +} + +/// Open a deep link by sending it to the already-running instance of the app +pub async fn open(url: &url::Url) -> Result<(), Error> { + let path = named_pipe_path(BUNDLE_ID); + let mut client = named_pipe::ClientOptions::new() + .open(path) + .map_err(Error::Connect)?; + client + .write_all(url.as_str().as_bytes()) + .await + .map_err(Error::ClientCommunications)?; + Ok(()) +} + +/// Registers the current exe as the handler for our deep link scheme. +/// +/// This is copied almost verbatim from tauri-plugin-deep-link's `register` fn, with an improvement +/// that we send the deep link to a subcommand so the URL won't confuse `clap` +pub fn register() -> Result<(), Error> { + let exe = tauri_utils::platform::current_exe() + .map_err(Error::CurrentExe)? + .display() + .to_string() + .replace("\\\\?\\", ""); + + set_registry_values(BUNDLE_ID, &exe).map_err(Error::WindowsRegistry)?; + + Ok(()) +} + +/// Set up the Windows registry to call the given exe when our deep link scheme is used +/// +/// All errors from this function are registry-related +fn set_registry_values(id: &str, exe: &str) -> Result<(), io::Error> { + let hkcu = winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER); + let base = Path::new("Software").join("Classes").join(FZ_SCHEME); + + let (key, _) = hkcu.create_subkey(&base)?; + key.set_value("", &format!("URL:{}", id))?; + key.set_value("URL Protocol", &"")?; + + let (icon, _) = hkcu.create_subkey(base.join("DefaultIcon"))?; + icon.set_value("", &format!("{},0", &exe))?; + + let (cmd, _) = hkcu.create_subkey(base.join("shell").join("open").join("command"))?; + cmd.set_value("", &format!("{} open-deep-link \"%1\"", &exe))?; + + Ok(()) +} + +/// Returns a valid name for a Windows named pipe +/// +/// # Arguments +/// +/// * `id` - BUNDLE_ID, e.g. `dev.firezone.client` +fn named_pipe_path(id: &str) -> String { + format!(r"\\.\pipe\{}", id) +} diff --git a/rust/gui-client/src-tauri/src/client/elevation.rs b/rust/gui-client/src-tauri/src/client/elevation.rs index 7034214f9..cd7739514 100644 --- a/rust/gui-client/src-tauri/src/client/elevation.rs +++ b/rust/gui-client/src-tauri/src/client/elevation.rs @@ -1,39 +1,75 @@ -use crate::client::wintun_install; -use std::str::FromStr; +pub(crate) use imp::{check, elevate}; -#[derive(thiserror::Error, Debug)] -pub(crate) enum Error { - #[error("couldn't install wintun.dll")] - DllInstall(#[from] wintun_install::Error), - #[error("couldn't load wintun.dll")] - DllLoad, - #[error("UUID parse error - This should be impossible since the UUID is hard-coded")] - Uuid, -} +#[cfg(target_os = "linux")] +mod imp { + use anyhow::Result; -/// Creates a bogus wintun tunnel to check whether we have permissions to create wintun tunnels. -/// Extracts wintun.dll if needed. -/// -/// Returns true if already elevated, false if not elevated, error if we can't be sure -pub(crate) fn check() -> Result { - // Almost the same as the code in tun_windows.rs in connlib - const TUNNEL_UUID: &str = "72228ef4-cb84-4ca5-a4e6-3f8636e75757"; - const TUNNEL_NAME: &str = "Firezone Elevation Check"; - - let path = match wintun_install::ensure_dll() { - Ok(x) => x, - Err(wintun_install::Error::PermissionDenied) => return Ok(false), - Err(e) => return Err(Error::DllInstall(e)), - }; - - // SAFETY: Unsafe needed because we're loading a DLL from disk and it has arbitrary C code in it. - // `wintun_install::ensure_dll` checks the hash before we get here. This protects against accidental corruption, but not against attacks. (Because of TOCTOU) - let wintun = unsafe { wintun::load_from_path(path) }.map_err(|_| Error::DllLoad)?; - let uuid = uuid::Uuid::from_str(TUNNEL_UUID).map_err(|_| Error::Uuid)?; - - // Wintun hides the exact Windows error, so let's assume the only way Adapter::create can fail is if we're not elevated. - if wintun::Adapter::create(&wintun, "Firezone", TUNNEL_NAME, Some(uuid.as_u128())).is_err() { - return Ok(false); + pub(crate) fn check() -> Result { + // TODO + Ok(true) + } + + pub(crate) fn elevate() -> Result<()> { + todo!() + } +} + +#[cfg(target_os = "windows")] +mod imp { + use crate::client::wintun_install; + use anyhow::{Context, Result}; + use std::{os::windows::process::CommandExt, str::FromStr}; + + /// Check if we have elevated privileges, extract wintun.dll if needed. + /// + /// Returns true if already elevated, false if not elevated, error if we can't be sure + pub(crate) fn check() -> Result { + // Almost the same as the code in tun_windows.rs in connlib + const TUNNEL_UUID: &str = "72228ef4-cb84-4ca5-a4e6-3f8636e75757"; + const TUNNEL_NAME: &str = "Firezone Elevation Check"; + + let path = match wintun_install::ensure_dll() { + Ok(x) => x, + Err(wintun_install::Error::PermissionDenied) => return Ok(false), + Err(e) => return Err(e).context("Failed to ensure wintun.dll is installed"), + }; + + // SAFETY: Unsafe needed because we're loading a DLL from disk and it has arbitrary C code in it. + // `wintun_install::ensure_dll` checks the hash before we get here. This protects against accidental corruption, but not against attacks. (Because of TOCTOU) + let wintun = + unsafe { wintun::load_from_path(path) }.context("Failed to load wintun.dll")?; + let uuid = + uuid::Uuid::from_str(TUNNEL_UUID).context("Impossible: Hard-coded UUID is invalid")?; + + // Wintun hides the exact Windows error, so let's assume the only way Adapter::create can fail is if we're not elevated. + if wintun::Adapter::create(&wintun, "Firezone", TUNNEL_NAME, Some(uuid.as_u128())).is_err() + { + return Ok(false); + } + Ok(true) + } + + pub(crate) fn elevate() -> Result<()> { + // Hides Powershell's console on Windows + // + const CREATE_NO_WINDOW: u32 = 0x08000000; + + let current_exe = tauri_utils::platform::current_exe()?; + if current_exe.display().to_string().contains('\"') { + anyhow::bail!("The exe path must not contain double quotes, it makes it hard to elevate with Powershell"); + } + std::process::Command::new("powershell") + .creation_flags(CREATE_NO_WINDOW) + .arg("-Command") + .arg("Start-Process") + .arg("-FilePath") + .arg(format!(r#""{}""#, current_exe.display())) + .arg("-Verb") + .arg("RunAs") + .arg("-ArgumentList") + .arg("elevated") + .spawn() + .context("Failed to elevate ourselves with `RunAs`")?; + Ok(()) } - Ok(true) } diff --git a/rust/gui-client/src-tauri/src/client/gui.rs b/rust/gui-client/src-tauri/src/client/gui.rs index 4bcb371d9..442b2146a 100644 --- a/rust/gui-client/src-tauri/src/client/gui.rs +++ b/rust/gui-client/src-tauri/src/client/gui.rs @@ -4,7 +4,7 @@ // TODO: `git grep` for unwraps before 1.0, especially this gui module use crate::client::{ - self, about, deep_link, known_dirs, logging, network_changes, + self, about, deep_link, logging, network_changes, settings::{self, AdvancedSettings}, Failure, }; @@ -21,6 +21,14 @@ use ControllerRequest as Req; mod system_tray_menu; +#[cfg(target_os = "linux")] +#[path = "gui/os_linux.rs"] +mod os; + +#[cfg(target_os = "windows")] +#[path = "gui/os_windows.rs"] +mod os; + /// The Windows client doesn't use platform APIs to detect network connectivity changes, /// so we rely on connlib to do so. We have valid use cases for headless Windows clients /// (IoT devices, point-of-sale devices, etc), so try to reconnect for 30 days if there's @@ -53,6 +61,7 @@ impl Managed { } // TODO: Replace with `anyhow` gradually per +#[cfg_attr(target_os = "linux", allow(dead_code))] #[derive(Debug, thiserror::Error)] pub(crate) enum Error { #[error(r#"Couldn't show clickable notification titled "{0}""#)] @@ -303,9 +312,8 @@ async fn smoke_test(ctlr_tx: CtlrTx) -> Result<()> { settings::apply_advanced_settings_inner(&settings::AdvancedSettings::default()).await?; // Test log exporting - let path = known_dirs::session() - .context("`known_dirs::session` failed during smoke test")? - .join("smoke_test_log_export.zip"); + let path = PathBuf::from("smoke_test_log_export.zip"); + let stem = "connlib-smoke-test".into(); match tokio::fs::remove_file(&path).await { Ok(()) => {} @@ -585,7 +593,7 @@ impl Controller { Req::DisconnectedTokenExpired => { tracing::info!("Token expired"); self.sign_out()?; - show_notification( + os::show_notification( "Firezone disconnected", "To access resources, sign in again.", )?; @@ -644,7 +652,7 @@ impl Controller { self.tunnel_ready = true; self.refresh_system_tray_menu()?; - show_notification( + os::show_notification( "Firezone connected", "You are now signed in and able to access resources.", )?; @@ -655,7 +663,7 @@ impl Controller { // We don't need to route through the controller here either, we could // use the `open` crate directly instead of Tauri's wrapper // `tauri::api::shell::open` - show_clickable_notification( + os::show_clickable_notification( &title, "Click here to download the new version.", self.ctlr_tx.clone(), @@ -831,62 +839,3 @@ async fn run_controller( Ok(()) } - -/// Show a notification in the bottom right of the screen -/// -/// May say "Windows Powershell" and have the wrong icon in dev mode -/// See -fn show_notification(title: &str, body: &str) -> Result<(), Error> { - tauri_winrt_notification::Toast::new(BUNDLE_ID) - .title(title) - .text1(body) - .show() - .map_err(|_| Error::Notification(title.to_string()))?; - - Ok(()) -} - -/// Show a notification that signals `Controller` when clicked -/// -/// May say "Windows Powershell" and have the wrong icon in dev mode -/// See -/// -/// Known issue: If the notification times out and goes into the notification center -/// (the little thing that pops up when you click the bell icon), then we may not get the -/// click signal. -/// -/// I've seen this reported by people using Powershell, C#, etc., so I think it might -/// be a Windows bug? -/// - -/// - -/// - -/// -/// Firefox doesn't have this problem. Maybe they're using a different API. -fn show_clickable_notification( - title: &str, - body: &str, - tx: CtlrTx, - req: ControllerRequest, -) -> Result<(), Error> { - // For some reason `on_activated` is FnMut - let mut req = Some(req); - - tauri_winrt_notification::Toast::new(BUNDLE_ID) - .title(title) - .text1(body) - .scenario(tauri_winrt_notification::Scenario::Reminder) - .on_activated(move || { - if let Some(req) = req.take() { - if let Err(error) = tx.blocking_send(req) { - tracing::error!( - ?error, - "User clicked on notification, but we couldn't tell `Controller`" - ); - } - } - Ok(()) - }) - .show() - .map_err(|_| Error::ClickableNotification(title.to_string()))?; - Ok(()) -} diff --git a/rust/gui-client/src-tauri/src/client/gui/os_linux.rs b/rust/gui-client/src-tauri/src/client/gui/os_linux.rs new file mode 100644 index 000000000..e56a4187b --- /dev/null +++ b/rust/gui-client/src-tauri/src/client/gui/os_linux.rs @@ -0,0 +1,18 @@ +use super::{ControllerRequest, CtlrTx, Error}; + +/// Show a notification in the bottom right of the screen +pub(crate) fn show_notification(_title: &str, _body: &str) -> Result<(), Error> { + // TODO + Ok(()) +} + +/// Show a notification that signals `Controller` when clicked +pub(crate) fn show_clickable_notification( + _title: &str, + _body: &str, + _tx: CtlrTx, + _req: ControllerRequest, +) -> Result<(), Error> { + // TODO + Ok(()) +} diff --git a/rust/gui-client/src-tauri/src/client/gui/os_windows.rs b/rust/gui-client/src-tauri/src/client/gui/os_windows.rs new file mode 100644 index 000000000..55798750e --- /dev/null +++ b/rust/gui-client/src-tauri/src/client/gui/os_windows.rs @@ -0,0 +1,61 @@ +use super::{ControllerRequest, CtlrTx, Error}; +use connlib_shared::BUNDLE_ID; + +/// Show a notification in the bottom right of the screen +/// +/// May say "Windows Powershell" and have the wrong icon in dev mode +/// See +pub(crate) fn show_notification(title: &str, body: &str) -> Result<(), Error> { + tauri_winrt_notification::Toast::new(BUNDLE_ID) + .title(title) + .text1(body) + .show() + .map_err(|_| Error::Notification(title.to_string()))?; + + Ok(()) +} + +/// Show a notification that signals `Controller` when clicked +/// +/// May say "Windows Powershell" and have the wrong icon in dev mode +/// See +/// +/// Known issue: If the notification times out and goes into the notification center +/// (the little thing that pops up when you click the bell icon), then we may not get the +/// click signal. +/// +/// I've seen this reported by people using Powershell, C#, etc., so I think it might +/// be a Windows bug? +/// - +/// - +/// - +/// +/// Firefox doesn't have this problem. Maybe they're using a different API. +pub(crate) fn show_clickable_notification( + title: &str, + body: &str, + tx: CtlrTx, + req: ControllerRequest, +) -> Result<(), Error> { + // For some reason `on_activated` is FnMut + let mut req = Some(req); + + tauri_winrt_notification::Toast::new(BUNDLE_ID) + .title(title) + .text1(body) + .scenario(tauri_winrt_notification::Scenario::Reminder) + .on_activated(move || { + if let Some(req) = req.take() { + if let Err(error) = tx.blocking_send(req) { + tracing::error!( + ?error, + "User clicked on notification, but we couldn't tell `Controller`" + ); + } + } + Ok(()) + }) + .show() + .map_err(|_| Error::ClickableNotification(title.to_string()))?; + Ok(()) +} diff --git a/rust/gui-client/src-tauri/src/client/network_changes.rs b/rust/gui-client/src-tauri/src/client/network_changes.rs index 67fcba7b6..1d59fecd7 100644 --- a/rust/gui-client/src-tauri/src/client/network_changes.rs +++ b/rust/gui-client/src-tauri/src/client/network_changes.rs @@ -1,355 +1,9 @@ -//! A module for getting callbacks from Windows when we gain / lose Internet connectivity -//! -//! # Latency -//! -//! 2 or 3 seconds for the user clicking "Connect" or "Disconnect" on Wi-Fi, -//! or for plugging or unplugging an Ethernet cable. -//! -//! Plugging in Ethernet may take longer since it waits on DHCP. -//! Connecting to Wi-Fi usually notifies while Windows is showing the progress bar -//! in the Wi-Fi menu. -//! -//! DNS server changes are (TODO ) -//! -//! # Worker thread -//! -//! `Listener` must live in a worker thread if used from Tauri. -//! ` -//! This is because both `Listener` and some feature in Tauri (maybe drag-and-drop) depend on COM. -//! `Listener` works fine if we initialize COM with COINIT_MULTITHREADED, but -//! Tauri initializes COM some other way. -//! -//! In the debug command we don't need a worker thread because we're the only code -//! in the process using COM. -//! -//! I tried disabling file drag-and-drop in tauri.conf.json, that didn't work: -//! - -//! - -//! - -//! -//! There is some explanation of the COM threading stuff in MSDN here: -//! - -//! - -//! -//! Raymond Chen also explains it on his blog: +#[cfg(target_os = "linux")] +#[path = "network_changes/linux.rs"] +mod imp; -use anyhow::Result; -use std::sync::Arc; -use tokio::{runtime::Runtime, sync::Notify}; -use windows::{ - core::{ComInterface, Result as WinResult, GUID}, - Win32::{ - Networking::NetworkListManager::{ - INetworkEvents, INetworkEvents_Impl, INetworkListManager, NetworkListManager, - NLM_CONNECTIVITY, NLM_NETWORK_PROPERTY_CHANGE, - }, - System::Com, - }, -}; +#[cfg(target_os = "windows")] +#[path = "network_changes/windows.rs"] +mod imp; -#[derive(thiserror::Error, Debug)] -pub(crate) enum Error { - #[error("Couldn't initialize COM: {0}")] - ComInitialize(windows::core::Error), - #[error("Couldn't stop worker thread")] - CouldntStopWorkerThread, - #[error("Couldn't creat NetworkListManager")] - CreateNetworkListManager(windows::core::Error), - #[error("Couldn't start listening to network events: {0}")] - Listening(windows::core::Error), - #[error("Couldn't stop listening to network events: {0}")] - Unadvise(windows::core::Error), -} - -/// Debug subcommand to test network connectivity events -pub(crate) fn run_debug() -> Result<()> { - tracing_subscriber::fmt::init(); - - // Returns Err before COM is initialized - assert!(get_apartment_type().is_err()); - - let com_worker = Worker::new()?; - - // We have to initialize COM again for the main thread. This doesn't - // seem to be a problem in the main app since Tauri initializes COM for itself. - let _guard = ComGuard::new(); - - assert_eq!( - get_apartment_type(), - Ok((Com::APTTYPE_MTA, Com::APTTYPEQUALIFIER_NONE)) - ); - - let rt = Runtime::new().unwrap(); - - tracing::info!("Listening for network events..."); - - rt.block_on(async move { - loop { - com_worker.notified().await; - // Make sure whatever Tokio thread we're on is associated with COM - // somehow. - assert_eq!( - get_apartment_type()?, - (Com::APTTYPE_MTA, Com::APTTYPEQUALIFIER_NONE) - ); - - tracing::info!(have_internet = %check_internet()?); - } - }) -} - -/// Returns true if Windows thinks we have Internet access per [IsConnectedToInternet](https://learn.microsoft.com/en-us/windows/win32/api/netlistmgr/nf-netlistmgr-inetworklistmanager-get_isconnectedtointernet) -/// -/// Call this when `Listener` notifies you. -pub fn check_internet() -> WinResult { - // Retrieving the INetworkListManager takes less than half a millisecond, and this - // makes the lifetimes and Send+Sync much simpler for callers, so just retrieve it - // every single time. - // SAFETY: No lifetime problems. TODO: Could threading be a problem? - // I think in practice we'll never call this from two threads, but what if we did? - // Maybe make it a method on a `!Send + !Sync` guard struct? - let network_list_manager: INetworkListManager = - unsafe { Com::CoCreateInstance(&NetworkListManager, None, Com::CLSCTX_ALL) }?; - // SAFETY: `network_list_manager` isn't shared between threads, and the lifetime - // should be good. - let have_internet = unsafe { network_list_manager.IsConnectedToInternet() }?.as_bool(); - - Ok(have_internet) -} - -/// Worker thread that can be joined explicitly, and joins on Drop -pub(crate) struct Worker { - inner: Option, - notify: Arc, -} - -/// Needed so that `Drop` can consume the oneshot Sender and the thread's JoinHandle -struct WorkerInner { - thread: std::thread::JoinHandle>, - stopper: tokio::sync::oneshot::Sender<()>, -} - -impl Worker { - pub fn new() -> std::io::Result { - let notify = Arc::new(Notify::new()); - - let (stopper, rx) = tokio::sync::oneshot::channel(); - let thread = { - let notify = Arc::clone(¬ify); - std::thread::Builder::new() - .name("Firezone COM worker".into()) - .spawn(move || { - { - let com = ComGuard::new()?; - let _network_change_listener = Listener::new(&com, notify)?; - rx.blocking_recv().ok(); - } - tracing::debug!("COM worker thread shut down gracefully"); - Ok(()) - })? - }; - - Ok(Self { - inner: Some(WorkerInner { thread, stopper }), - notify, - }) - } - - /// Same as `drop`, but you can catch errors - pub fn close(&mut self) -> Result<(), Error> { - if let Some(inner) = self.inner.take() { - inner - .stopper - .send(()) - .map_err(|_| Error::CouldntStopWorkerThread)?; - match inner.thread.join() { - Err(e) => std::panic::resume_unwind(e), - Ok(x) => x?, - } - } - Ok(()) - } - - pub async fn notified(&self) { - self.notify.notified().await; - } -} - -impl Drop for Worker { - fn drop(&mut self) { - self.close() - .expect("should be able to close Worker cleanly"); - } -} - -/// Enforces the initialize-use-uninitialize order for `Listener` and COM -/// -/// COM is meant to be initialized for a thread, and un-initialized for the same thread, -/// so don't pass this between threads. -struct ComGuard { - dropped: bool, - _unsend_unsync: PhantomUnsendUnsync, -} - -/// Marks a type as !Send and !Sync without nightly / unstable features -/// -/// -type PhantomUnsendUnsync = std::marker::PhantomData<*const ()>; - -impl ComGuard { - /// Initialize a "Multi-threaded apartment" so that Windows COM stuff - /// can be called, and COM callbacks can work. - pub fn new() -> Result { - // SAFETY: Threading shouldn't be a problem since this is meant to initialize - // COM per-thread anyway. - unsafe { Com::CoInitializeEx(None, Com::COINIT_MULTITHREADED) } - .map_err(Error::ComInitialize)?; - Ok(Self { - dropped: false, - _unsend_unsync: Default::default(), - }) - } -} - -impl Drop for ComGuard { - fn drop(&mut self) { - if !self.dropped { - self.dropped = true; - // Required, per [CoInitializeEx docs](https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex#remarks) - // Safety: Make sure all the COM objects are dropped before we call - // CoUninitialize or the program might segfault. - unsafe { Com::CoUninitialize() }; - tracing::debug!("Uninitialized COM"); - } - } -} - -/// Listens to network connectivity change eents -pub(crate) struct Listener<'a> { - /// The cookies we get back from `Advise`. Can be None if the owner called `close` - /// - /// This has to be mutable because we have to hook up the callbacks during - /// Listener's constructor - advise_cookie_net: Option, - cxn_point_net: Com::IConnectionPoint, - - inner: ListenerInner, - - /// Hold a reference to a `ComGuard` to enforce the right init-use-uninit order - _com: &'a ComGuard, -} - -/// This must be separate because we need to `Clone` that `Notify` and we can't -/// `Clone` the COM objects in `Listener` -// https://kennykerr.ca/rust-getting-started/how-to-implement-com-interface.html -#[windows_implement::implement(INetworkEvents)] -#[derive(Clone)] -struct ListenerInner { - notify: Arc, -} - -impl<'a> Drop for Listener<'a> { - fn drop(&mut self) { - self.close().unwrap(); - } -} - -impl<'a> Listener<'a> { - /// Creates a new Listener - /// - /// # Arguments - /// - /// * `com` - Makes sure that CoInitializeEx was called. `com` have been created - /// on the same thread as `new` is called on. - /// * `notify` - A Tokio `Notify` that will be notified when Windows detects - /// connectivity changes. Some notifications may be spurious. - fn new(com: &'a ComGuard, notify: Arc) -> Result { - // `windows-rs` automatically releases (de-refs) COM objects on Drop: - // https://github.com/microsoft/windows-rs/issues/2123#issuecomment-1293194755 - // https://github.com/microsoft/windows-rs/blob/cefdabd15e4a7a7f71b7a2d8b12d5dc148c99adb/crates/samples/windows/wmi/src/main.rs#L22 - // SAFETY: TODO - let network_list_manager: INetworkListManager = - unsafe { Com::CoCreateInstance(&NetworkListManager, None, Com::CLSCTX_ALL) } - .map_err(Error::CreateNetworkListManager)?; - let cpc: Com::IConnectionPointContainer = - network_list_manager.cast().map_err(Error::Listening)?; - // SAFETY: TODO - let cxn_point_net = - unsafe { cpc.FindConnectionPoint(&INetworkEvents::IID) }.map_err(Error::Listening)?; - - let mut this = Listener { - advise_cookie_net: None, - cxn_point_net, - inner: ListenerInner { notify }, - _com: com, - }; - - let callbacks: INetworkEvents = this.inner.clone().into(); - - // SAFETY: What happens if Windows sends us a network change event while - // we're dropping Listener? - // Is it safe to Advise on `this` and then immediately move it? - this.advise_cookie_net = - Some(unsafe { this.cxn_point_net.Advise(&callbacks) }.map_err(Error::Listening)?); - - // After we call `Advise`, notify. This should avoid a problem if this happens: - // - // 1. Caller spawns a worker thread for Listener, but the worker thread isn't scheduled - // 2. Caller continues setup, checks Internet is connected - // 3. Internet gets disconnected but caller isn't notified - // 4. Worker thread finally gets scheduled, but we never notify that the Internet was lost during setup. Caller is now out of sync with ground truth. - this.inner.notify.notify_one(); - - Ok(this) - } - - /// Like `drop` but you can catch errors - /// - /// Unregisters the network change callbacks - pub fn close(&mut self) -> Result<(), Error> { - if let Some(cookie) = self.advise_cookie_net.take() { - // SAFETY: I don't see any memory safety issues. - unsafe { self.cxn_point_net.Unadvise(cookie) }.map_err(Error::Unadvise)?; - tracing::debug!("Unadvised"); - } - Ok(()) - } -} - -impl INetworkEvents_Impl for ListenerInner { - fn NetworkAdded(&self, _networkid: &GUID) -> WinResult<()> { - Ok(()) - } - - fn NetworkDeleted(&self, _networkid: &GUID) -> WinResult<()> { - Ok(()) - } - - fn NetworkConnectivityChanged( - &self, - _networkid: &GUID, - _newconnectivity: NLM_CONNECTIVITY, - ) -> WinResult<()> { - self.notify.notify_one(); - Ok(()) - } - - fn NetworkPropertyChanged( - &self, - _networkid: &GUID, - _flags: NLM_NETWORK_PROPERTY_CHANGE, - ) -> WinResult<()> { - Ok(()) - } -} - -/// Checks what COM apartment the current thread is in. For debugging only. -fn get_apartment_type() -> WinResult<(Com::APTTYPE, Com::APTTYPEQUALIFIER)> { - let mut apt_type = Com::APTTYPE_CURRENT; - let mut apt_qualifier = Com::APTTYPEQUALIFIER_NONE; - - // SAFETY: We just created the variables, and they're out parameters, - // so Windows shouldn't store the pointers. - unsafe { Com::CoGetApartmentType(&mut apt_type, &mut apt_qualifier) }?; - Ok((apt_type, apt_qualifier)) -} +pub(crate) use imp::{check_internet, run_debug, Worker}; diff --git a/rust/gui-client/src-tauri/src/client/network_changes/linux.rs b/rust/gui-client/src-tauri/src/client/network_changes/linux.rs new file mode 100644 index 000000000..e8f49a55d --- /dev/null +++ b/rust/gui-client/src-tauri/src/client/network_changes/linux.rs @@ -0,0 +1,32 @@ +//! Not implemented for Linux yet + +use anyhow::Result; + +#[derive(thiserror::Error, Debug)] +pub(crate) enum Error {} + +pub(crate) fn run_debug() -> Result<()> { + tracing::warn!("network_changes not implemented yet on Linux"); + Ok(()) +} + +/// TODO: Implement for Linux +pub(crate) fn check_internet() -> Result { + Ok(true) +} + +pub(crate) struct Worker {} + +impl Worker { + pub(crate) fn new() -> Result { + Ok(Self {}) + } + + pub(crate) fn close(&mut self) -> Result<()> { + Ok(()) + } + + pub(crate) async fn notified(&self) { + futures::future::pending().await + } +} diff --git a/rust/gui-client/src-tauri/src/client/network_changes/windows.rs b/rust/gui-client/src-tauri/src/client/network_changes/windows.rs new file mode 100644 index 000000000..b9fd5dfc7 --- /dev/null +++ b/rust/gui-client/src-tauri/src/client/network_changes/windows.rs @@ -0,0 +1,355 @@ +//! A module for getting callbacks from Windows when we gain / lose Internet connectivity +//! +//! # Latency +//! +//! 2 or 3 seconds for the user clicking "Connect" or "Disconnect" on Wi-Fi, +//! or for plugging or unplugging an Ethernet cable. +//! +//! Plugging in Ethernet may take longer since it waits on DHCP. +//! Connecting to Wi-Fi usually notifies while Windows is showing the progress bar +//! in the Wi-Fi menu. +//! +//! DNS server changes are (TODO ) +//! +//! # Worker thread +//! +//! `Listener` must live in a worker thread if used from Tauri. +//! ` +//! This is because both `Listener` and some feature in Tauri (maybe drag-and-drop) depend on COM. +//! `Listener` works fine if we initialize COM with COINIT_MULTITHREADED, but +//! Tauri initializes COM some other way. +//! +//! In the debug command we don't need a worker thread because we're the only code +//! in the process using COM. +//! +//! I tried disabling file drag-and-drop in tauri.conf.json, that didn't work: +//! - +//! - +//! - +//! +//! There is some explanation of the COM threading stuff in MSDN here: +//! - +//! - +//! +//! Raymond Chen also explains it on his blog: + +use anyhow::Result; +use std::sync::Arc; +use tokio::{runtime::Runtime, sync::Notify}; +use windows::{ + core::{ComInterface, Result as WinResult, GUID}, + Win32::{ + Networking::NetworkListManager::{ + INetworkEvents, INetworkEvents_Impl, INetworkListManager, NetworkListManager, + NLM_CONNECTIVITY, NLM_NETWORK_PROPERTY_CHANGE, + }, + System::Com, + }, +}; + +#[derive(thiserror::Error, Debug)] +pub(crate) enum Error { + #[error("Couldn't initialize COM: {0}")] + ComInitialize(windows::core::Error), + #[error("Couldn't stop worker thread")] + CouldntStopWorkerThread, + #[error("Couldn't creat NetworkListManager")] + CreateNetworkListManager(windows::core::Error), + #[error("Couldn't start listening to network events: {0}")] + Listening(windows::core::Error), + #[error("Couldn't stop listening to network events: {0}")] + Unadvise(windows::core::Error), +} + +/// Debug subcommand to test network connectivity events +pub(crate) fn run_debug() -> Result<()> { + tracing_subscriber::fmt::init(); + + // Returns Err before COM is initialized + assert!(get_apartment_type().is_err()); + + let com_worker = Worker::new()?; + + // We have to initialize COM again for the main thread. This doesn't + // seem to be a problem in the main app since Tauri initializes COM for itself. + let _guard = ComGuard::new(); + + assert_eq!( + get_apartment_type(), + Ok((Com::APTTYPE_MTA, Com::APTTYPEQUALIFIER_NONE)) + ); + + let rt = Runtime::new().unwrap(); + + tracing::info!("Listening for network events..."); + + rt.block_on(async move { + loop { + com_worker.notified().await; + // Make sure whatever Tokio thread we're on is associated with COM + // somehow. + assert_eq!( + get_apartment_type()?, + (Com::APTTYPE_MTA, Com::APTTYPEQUALIFIER_NONE) + ); + + tracing::info!(have_internet = %check_internet()?); + } + }) +} + +/// Returns true if Windows thinks we have Internet access per [IsConnectedToInternet](https://learn.microsoft.com/en-us/windows/win32/api/netlistmgr/nf-netlistmgr-inetworklistmanager-get_isconnectedtointernet) +/// +/// Call this when `Listener` notifies you. +pub fn check_internet() -> Result { + // Retrieving the INetworkListManager takes less than half a millisecond, and this + // makes the lifetimes and Send+Sync much simpler for callers, so just retrieve it + // every single time. + // SAFETY: No lifetime problems. TODO: Could threading be a problem? + // I think in practice we'll never call this from two threads, but what if we did? + // Maybe make it a method on a `!Send + !Sync` guard struct? + let network_list_manager: INetworkListManager = + unsafe { Com::CoCreateInstance(&NetworkListManager, None, Com::CLSCTX_ALL) }?; + // SAFETY: `network_list_manager` isn't shared between threads, and the lifetime + // should be good. + let have_internet = unsafe { network_list_manager.IsConnectedToInternet() }?.as_bool(); + + Ok(have_internet) +} + +/// Worker thread that can be joined explicitly, and joins on Drop +pub(crate) struct Worker { + inner: Option, + notify: Arc, +} + +/// Needed so that `Drop` can consume the oneshot Sender and the thread's JoinHandle +struct WorkerInner { + thread: std::thread::JoinHandle>, + stopper: tokio::sync::oneshot::Sender<()>, +} + +impl Worker { + pub(crate) fn new() -> Result { + let notify = Arc::new(Notify::new()); + + let (stopper, rx) = tokio::sync::oneshot::channel(); + let thread = { + let notify = Arc::clone(¬ify); + std::thread::Builder::new() + .name("Firezone COM worker".into()) + .spawn(move || { + { + let com = ComGuard::new()?; + let _network_change_listener = Listener::new(&com, notify)?; + rx.blocking_recv().ok(); + } + tracing::debug!("COM worker thread shut down gracefully"); + Ok(()) + })? + }; + + Ok(Self { + inner: Some(WorkerInner { thread, stopper }), + notify, + }) + } + + /// Same as `drop`, but you can catch errors + pub(crate) fn close(&mut self) -> Result<()> { + if let Some(inner) = self.inner.take() { + inner + .stopper + .send(()) + .map_err(|_| Error::CouldntStopWorkerThread)?; + match inner.thread.join() { + Err(e) => std::panic::resume_unwind(e), + Ok(x) => x?, + } + } + Ok(()) + } + + pub(crate) async fn notified(&self) { + self.notify.notified().await; + } +} + +impl Drop for Worker { + fn drop(&mut self) { + self.close() + .expect("should be able to close Worker cleanly"); + } +} + +/// Enforces the initialize-use-uninitialize order for `Listener` and COM +/// +/// COM is meant to be initialized for a thread, and un-initialized for the same thread, +/// so don't pass this between threads. +struct ComGuard { + dropped: bool, + _unsend_unsync: PhantomUnsendUnsync, +} + +/// Marks a type as !Send and !Sync without nightly / unstable features +/// +/// +type PhantomUnsendUnsync = std::marker::PhantomData<*const ()>; + +impl ComGuard { + /// Initialize a "Multi-threaded apartment" so that Windows COM stuff + /// can be called, and COM callbacks can work. + pub fn new() -> Result { + // SAFETY: Threading shouldn't be a problem since this is meant to initialize + // COM per-thread anyway. + unsafe { Com::CoInitializeEx(None, Com::COINIT_MULTITHREADED) } + .map_err(Error::ComInitialize)?; + Ok(Self { + dropped: false, + _unsend_unsync: Default::default(), + }) + } +} + +impl Drop for ComGuard { + fn drop(&mut self) { + if !self.dropped { + self.dropped = true; + // Required, per [CoInitializeEx docs](https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex#remarks) + // Safety: Make sure all the COM objects are dropped before we call + // CoUninitialize or the program might segfault. + unsafe { Com::CoUninitialize() }; + tracing::debug!("Uninitialized COM"); + } + } +} + +/// Listens to network connectivity change eents +struct Listener<'a> { + /// The cookies we get back from `Advise`. Can be None if the owner called `close` + /// + /// This has to be mutable because we have to hook up the callbacks during + /// Listener's constructor + advise_cookie_net: Option, + cxn_point_net: Com::IConnectionPoint, + + inner: ListenerInner, + + /// Hold a reference to a `ComGuard` to enforce the right init-use-uninit order + _com: &'a ComGuard, +} + +/// This must be separate because we need to `Clone` that `Notify` and we can't +/// `Clone` the COM objects in `Listener` +// https://kennykerr.ca/rust-getting-started/how-to-implement-com-interface.html +#[windows_implement::implement(INetworkEvents)] +#[derive(Clone)] +struct ListenerInner { + notify: Arc, +} + +impl<'a> Drop for Listener<'a> { + fn drop(&mut self) { + self.close().unwrap(); + } +} + +impl<'a> Listener<'a> { + /// Creates a new Listener + /// + /// # Arguments + /// + /// * `com` - Makes sure that CoInitializeEx was called. `com` have been created + /// on the same thread as `new` is called on. + /// * `notify` - A Tokio `Notify` that will be notified when Windows detects + /// connectivity changes. Some notifications may be spurious. + fn new(com: &'a ComGuard, notify: Arc) -> Result { + // `windows-rs` automatically releases (de-refs) COM objects on Drop: + // https://github.com/microsoft/windows-rs/issues/2123#issuecomment-1293194755 + // https://github.com/microsoft/windows-rs/blob/cefdabd15e4a7a7f71b7a2d8b12d5dc148c99adb/crates/samples/windows/wmi/src/main.rs#L22 + // SAFETY: TODO + let network_list_manager: INetworkListManager = + unsafe { Com::CoCreateInstance(&NetworkListManager, None, Com::CLSCTX_ALL) } + .map_err(Error::CreateNetworkListManager)?; + let cpc: Com::IConnectionPointContainer = + network_list_manager.cast().map_err(Error::Listening)?; + // SAFETY: TODO + let cxn_point_net = + unsafe { cpc.FindConnectionPoint(&INetworkEvents::IID) }.map_err(Error::Listening)?; + + let mut this = Listener { + advise_cookie_net: None, + cxn_point_net, + inner: ListenerInner { notify }, + _com: com, + }; + + let callbacks: INetworkEvents = this.inner.clone().into(); + + // SAFETY: What happens if Windows sends us a network change event while + // we're dropping Listener? + // Is it safe to Advise on `this` and then immediately move it? + this.advise_cookie_net = + Some(unsafe { this.cxn_point_net.Advise(&callbacks) }.map_err(Error::Listening)?); + + // After we call `Advise`, notify. This should avoid a problem if this happens: + // + // 1. Caller spawns a worker thread for Listener, but the worker thread isn't scheduled + // 2. Caller continues setup, checks Internet is connected + // 3. Internet gets disconnected but caller isn't notified + // 4. Worker thread finally gets scheduled, but we never notify that the Internet was lost during setup. Caller is now out of sync with ground truth. + this.inner.notify.notify_one(); + + Ok(this) + } + + /// Like `drop` but you can catch errors + /// + /// Unregisters the network change callbacks + pub fn close(&mut self) -> Result<(), Error> { + if let Some(cookie) = self.advise_cookie_net.take() { + // SAFETY: I don't see any memory safety issues. + unsafe { self.cxn_point_net.Unadvise(cookie) }.map_err(Error::Unadvise)?; + tracing::debug!("Unadvised"); + } + Ok(()) + } +} + +impl INetworkEvents_Impl for ListenerInner { + fn NetworkAdded(&self, _networkid: &GUID) -> WinResult<()> { + Ok(()) + } + + fn NetworkDeleted(&self, _networkid: &GUID) -> WinResult<()> { + Ok(()) + } + + fn NetworkConnectivityChanged( + &self, + _networkid: &GUID, + _newconnectivity: NLM_CONNECTIVITY, + ) -> WinResult<()> { + self.notify.notify_one(); + Ok(()) + } + + fn NetworkPropertyChanged( + &self, + _networkid: &GUID, + _flags: NLM_NETWORK_PROPERTY_CHANGE, + ) -> WinResult<()> { + Ok(()) + } +} + +/// Checks what COM apartment the current thread is in. For debugging only. +fn get_apartment_type() -> WinResult<(Com::APTTYPE, Com::APTTYPEQUALIFIER)> { + let mut apt_type = Com::APTTYPE_CURRENT; + let mut apt_qualifier = Com::APTTYPEQUALIFIER_NONE; + + // SAFETY: We just created the variables, and they're out parameters, + // so Windows shouldn't store the pointers. + unsafe { Com::CoGetApartmentType(&mut apt_type, &mut apt_qualifier) }?; + Ok((apt_type, apt_qualifier)) +} diff --git a/rust/gui-client/src-tauri/src/client/resolvers.rs b/rust/gui-client/src-tauri/src/client/resolvers.rs index 9ff098520..2f217c60f 100644 --- a/rust/gui-client/src-tauri/src/client/resolvers.rs +++ b/rust/gui-client/src-tauri/src/client/resolvers.rs @@ -5,9 +5,16 @@ use std::net::IpAddr; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("can't get system DNS resolvers: {0}")] + #[cfg(target_os = "windows")] CantGetResolvers(#[from] ipconfig::error::Error), } +#[cfg(target_os = "linux")] +pub fn get() -> Result, Error> { + todo!() +} + +#[cfg(target_os = "windows")] pub fn get() -> Result, Error> { Ok(ipconfig::get_adapters()? .iter() diff --git a/rust/gui-client/src-tauri/src/main.rs b/rust/gui-client/src-tauri/src/main.rs index f54769261..ceb332524 100644 --- a/rust/gui-client/src-tauri/src/main.rs +++ b/rust/gui-client/src-tauri/src/main.rs @@ -7,16 +7,16 @@ fn main() -> anyhow::Result<()> { client::run() } -#[cfg(target_family = "unix")] +#[cfg(target_os = "linux")] +mod client; + +#[cfg(target_os = "macos")] mod client { pub(crate) fn run() -> anyhow::Result<()> { - println!("The Windows client does not compile on non-Windows platforms yet"); + println!("The GUI client does not compile on macOS yet"); Ok(()) } } -/// Everything is hidden inside the `client` module so that we can exempt the -/// Windows client from static analysis on other platforms where it would throw -/// compile errors. #[cfg(target_os = "windows")] mod client; diff --git a/scripts/tests/smoke-test-gui-linux.sh b/scripts/tests/smoke-test-gui-linux.sh index c520368fa..5c7120f41 100755 --- a/scripts/tests/smoke-test-gui-linux.sh +++ b/scripts/tests/smoke-test-gui-linux.sh @@ -9,28 +9,47 @@ PACKAGE=firezone-gui-client export RUST_LOG=firezone_gui_client=debug,warn export WEBKIT_DISABLE_COMPOSITING_MODE=1 -# Make sure the files we want to check don't exist on the system yet -stat "$HOME/.cache/$BUNDLE_ID" && exit 1 -stat "$HOME/.config/$BUNDLE_ID" && exit 1 +function smoke_test() { + # Make sure the files we want to check don't exist on the system yet + stat "$HOME/.cache/$BUNDLE_ID/data/logs" && exit 1 + stat "$HOME/.config/$BUNDLE_ID/config/advanced_settings.json" && exit 1 + stat "$HOME/.config/$BUNDLE_ID/config/device_id.json" && exit 1 -# Run the smoke test normally -xvfb-run --auto-servernum cargo run -p "$PACKAGE" -- smoke-test + # Run the smoke test normally + xvfb-run --auto-servernum cargo run -p "$PACKAGE" -- smoke-test -# Make sure the files were written in the right paths -# TODO: Inject some bogus sign-in sequence to test the actor_name file -stat "$HOME/.config/$BUNDLE_ID/config/advanced_settings.json" -stat "$HOME/.cache/$BUNDLE_ID/data/logs/"connlib*log -stat "$HOME/.config/$BUNDLE_ID/config/device_id.json" + # Make sure the files were written in the right paths + # TODO: Inject some bogus sign-in sequence to test the actor_name file + stat "$HOME/.cache/$BUNDLE_ID/data/logs/"connlib*log + stat "$HOME/.config/$BUNDLE_ID/config/advanced_settings.json" + stat "$HOME/.config/$BUNDLE_ID/config/device_id.json" -# Delete the crash file if present -rm -f "$DUMP_PATH" + # Clean up the files but not the folders + rm -rf "$HOME/.cache/$BUNDLE_ID/data/logs" + rm "$HOME/.config/$BUNDLE_ID/config/advanced_settings.json" + rm "$HOME/.config/$BUNDLE_ID/config/device_id.json" +} -# Fail if it returns success, this is supposed to crash -xvfb-run --auto-servernum cargo run -p "$PACKAGE" -- --crash && exit 1 +function crash_test() { + # Delete the crash file if present + rm -f "$DUMP_PATH" -# Fail if the crash file wasn't written -stat "$DUMP_PATH" -rm "$DUMP_PATH" + # Fail if it returns success, this is supposed to crash + xvfb-run --auto-servernum cargo run -p "$PACKAGE" -- --crash && exit 1 + + # Fail if the crash file wasn't written + stat "$DUMP_PATH" + + # Clean up + rm "$DUMP_PATH" +} + +# Run the tests twice to make sure it's okay for the directories to stay intact, +# and to make sure the tests can cycle. +smoke_test +smoke_test +crash_test +crash_test # I'm not sure if the last command is handled specially, so explicitly exit with 0 exit 0