diff --git a/Cargo.lock b/Cargo.lock
index fc0d9a9b8e..b21feede06 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,12 +1,62 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
+
+[[package]]
+name = "anstream"
+version = "0.6.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys 0.61.2",
+]
[[package]]
name = "asn1-rs"
-version = "0.7.1"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60"
+checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8"
dependencies = [
"asn1-rs-derive",
"asn1-rs-impl",
@@ -43,15 +93,33 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.5.0"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bitflags"
+version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+
+[[package]]
+name = "bytes"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cc"
-version = "1.2.49"
+version = "1.2.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215"
+checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
dependencies = [
"find-msvc-tools",
"shlex",
@@ -63,11 +131,63 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "clap"
+version = "4.5.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
+
[[package]]
name = "data-encoding"
-version = "2.10.0"
+version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
+checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "der-parser"
@@ -85,24 +205,30 @@ dependencies = [
[[package]]
name = "deranged"
-version = "0.5.5"
+version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
]
[[package]]
name = "displaydoc"
-version = "0.2.5"
+version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
+[[package]]
+name = "err-context"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "449aad22b1364e927ff3bf50f55404efd705c40065fb47f73f28704de707c89e"
+
[[package]]
name = "ffi-support"
version = "0.4.4"
@@ -115,32 +241,132 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.5"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
+name = "futures"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
+
+[[package]]
+name = "futures-task"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+
+[[package]]
+name = "futures-util"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "slab",
+]
[[package]]
name = "getrandom"
-version = "0.2.16"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+
[[package]]
name = "itoa"
-version = "1.0.15"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "lazy_static"
@@ -150,21 +376,30 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.178"
+version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "log"
-version = "0.4.29"
+version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
[[package]]
name = "memchr"
-version = "2.7.6"
+version = "2.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
[[package]]
name = "minimal-lexical"
@@ -172,6 +407,30 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+[[package]]
+name = "mio"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "nix"
+version = "0.31.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+ "memoffset",
+]
+
[[package]]
name = "nom"
version = "7.1.3"
@@ -194,9 +453,9 @@ dependencies = [
[[package]]
name = "num-conv"
-version = "0.1.0"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]]
name = "num-integer"
@@ -216,6 +475,19 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "obfuscators"
+version = "0.1.0"
+dependencies = [
+ "base64",
+ "clap",
+ "log",
+ "once_cell",
+ "rand",
+ "tokio",
+ "udp-over-tcp",
+]
+
[[package]]
name = "oid-registry"
version = "0.8.1"
@@ -225,30 +497,87 @@ dependencies = [
"asn1-rs",
]
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
[[package]]
name = "proc-macro2"
-version = "1.0.103"
+version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
-version = "1.0.42"
+version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
+[[package]]
+name = "rand"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
[[package]]
name = "ring"
version = "0.17.14"
@@ -260,7 +589,7 @@ dependencies = [
"getrandom",
"libc",
"untrusted",
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -272,15 +601,6 @@ dependencies = [
"nom",
]
-[[package]]
-name = "serde"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
-dependencies = [
- "serde_core",
-]
-
[[package]]
name = "serde_core"
version = "1.0.228"
@@ -303,9 +623,9 @@ dependencies = [
[[package]]
name = "shlex"
-version = "1.3.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "signature"
@@ -321,11 +641,33 @@ dependencies = [
"x509-parser",
]
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "socket2"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
[[package]]
name = "syn"
-version = "2.0.111"
+version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
@@ -365,40 +707,78 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.44"
+version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
- "serde",
+ "serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
-version = "0.1.6"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
+checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
-version = "0.2.24"
+version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
]
+[[package]]
+name = "tokio"
+version = "1.52.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "udp-over-tcp"
+version = "0.4.0"
+source = "git+https://github.com/mullvad/udp-over-tcp?rev=5c6d8f44a5aa12ed9bb4ae51dd17e5e22e5ec303#5c6d8f44a5aa12ed9bb4ae51dd17e5e22e5ec303"
+dependencies = [
+ "err-context",
+ "futures",
+ "log",
+ "nix",
+ "tokio",
+]
+
[[package]]
name = "unicode-ident"
-version = "1.0.22"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "untrusted"
@@ -406,12 +786,24 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
[[package]]
name = "windows-sys"
version = "0.52.0"
@@ -421,6 +813,15 @@ dependencies = [
"windows-targets",
]
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
[[package]]
name = "windows-targets"
version = "0.52.6"
@@ -502,3 +903,23 @@ dependencies = [
"thiserror",
"time",
]
+
+[[package]]
+name = "zerocopy"
+version = "0.8.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/Cargo.toml b/Cargo.toml
index c4a118e8cf..0572185481 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,7 @@
[workspace]
members = [
"signature",
+ "obfuscators",
]
resolver = "2"
diff --git a/android/daemon/build.gradle b/android/daemon/build.gradle
index 772bdd810f..a2ae856327 100644
--- a/android/daemon/build.gradle
+++ b/android/daemon/build.gradle
@@ -99,4 +99,5 @@ dependencies {
implementation project(path: ':common')
implementation project(path: ':tunnel')
+ implementation project(path: ':obfuscator')
}
diff --git a/android/daemon/src/main/java/org/mozilla/firefox/vpn/daemon/VPNService.kt b/android/daemon/src/main/java/org/mozilla/firefox/vpn/daemon/VPNService.kt
index c50ddc5cdd..0b1faeefc0 100644
--- a/android/daemon/src/main/java/org/mozilla/firefox/vpn/daemon/VPNService.kt
+++ b/android/daemon/src/main/java/org/mozilla/firefox/vpn/daemon/VPNService.kt
@@ -18,6 +18,7 @@ import com.wireguard.crypto.Key
import org.json.JSONObject
import org.mozilla.firefox.qt.common.CoreBinder
import org.mozilla.firefox.qt.common.Prefs
+import org.mozilla.firefox.vpn.obfuscator.Obfuscator
import org.mozilla.guardian.tunnel.WireGuardGo
import java.util.*
@@ -30,6 +31,7 @@ class VPNService : android.net.VpnService() {
private var mAlreadyInitialised = false
val mConnectionHealth = ConnectionHealth(this)
private var mCityname = ""
+ private var mObfuscator: Obfuscator? = null
private val mBackgroundPingTimerMSec: Long = 3 * 60 * 60 * 1000 // 3 hours, in milliseconds
private val mShortTimerBackgroundPingMSec: Long = 3 * 60 * 1000 // 3 minutes, in milliseconds
@@ -223,9 +225,31 @@ class VPNService : android.net.VpnService() {
}
Log.sensitive(tag, json.toString())
val jServer: JSONObject = getNextServerConfig(json, useFallbackServer)
- val wireguard_conf = buildWireguardConfig(jServer, json)
+
+ // If an obfuscator is configured, start its proxy now and rewrite the
+ // WireGuard endpoint to its loopback port. The relay's outbound socket
+ // is protected below (after the tunnel is up) so its packets bypass
+ // the VPN.
+ val obfuscationMethodRaw = json.optInt("obfuscationMethod", 0)
+ val obfuscationMethod = Obfuscator.Method.fromValue(obfuscationMethodRaw)
+ Log.i(tag, "Attempt to start obfuscator '$obfuscationMethodRaw'")
+ val obfuscator: Obfuscator? = if (obfuscationMethod != null && obfuscationMethod != Obfuscator.Method.NoObfuscation) {
+ val r = Obfuscator.start(
+ method = obfuscationMethod,
+ serverIpv4 = jServer.optString("ipv4AddrIn").ifEmpty { null },
+ serverIpv6 = jServer.optString("ipv6AddrIn").ifEmpty { null },
+ serverPort = jServer.getInt("port"),
+ serverPublicKey = jServer.optString("publicKey").ifEmpty { null },
+ publicKey = json.optJSONObject("device")?.optString("publicKey")?.ifEmpty { null },
+ ) ?: throw Error("Failed to start obfuscator method: $obfuscationMethod")
+ Log.i(tag, "Obfuscator '$obfuscationMethod' listening on 127.0.0.1:${r.localPort}")
+ r
+ } else { null }
+
+ val wireguard_conf = buildWireguardConfig(jServer, json, obfuscator)
val wgConfig: String = wireguard_conf.toWgUserspaceString()
if (wgConfig.isEmpty()) {
+ obfuscator?.stop()
throw Error("WG_Userspace config is empty, can't continue")
}
mCityname = json.getString("city")
@@ -247,6 +271,7 @@ class VPNService : android.net.VpnService() {
builder.establish().use { tun ->
if (tun == null) {
Log.e(tag, "Activation Error: did not get a TUN handle")
+ obfuscator?.stop()
return
}
// We should have everything to establish a new connection, turn down the old tunnel
@@ -254,10 +279,13 @@ class VPNService : android.net.VpnService() {
if (currentTunnelHandle != -1) {
Log.i(tag, "Currently have a connection, close old handle")
WireGuardGo.wgTurnOff(currentTunnelHandle)
+ mObfuscator?.stop()
+ mObfuscator = null
}
currentTunnelHandle = WireGuardGo.wgTurnOn("mvpn0", tun.detachFd(), wgConfig)
}
if (currentTunnelHandle < 0) {
+ obfuscator?.stop()
throw Error("Activation Error Wireguard-Error -> $currentTunnelHandle")
} else {
Log.i(tag, "Updated tunnel handle to: " + currentTunnelHandle)
@@ -265,6 +293,13 @@ class VPNService : android.net.VpnService() {
protect(WireGuardGo.wgGetSocketV4(currentTunnelHandle))
protect(WireGuardGo.wgGetSocketV6(currentTunnelHandle))
+ // Mark the obfuscator's outbound socket so its packets bypass the VPN.
+ obfuscator?.let {
+ if (it.socketV4 >= 0) protect(it.socketV4)
+ if (it.socketV6 >= 0) protect(it.socketV6)
+ mObfuscator = it
+ }
+
mConfig = json
// We don't want to update connection health in several situations:
// - If this is an app-caused server switch.
@@ -355,6 +390,8 @@ class VPNService : android.net.VpnService() {
WireGuardGo.wgTurnOff(currentTunnelHandle)
currentTunnelHandle = -1
+ mObfuscator?.stop()
+ mObfuscator = null
// If the client is "dead", on a disconnect the
// message won't be updated to 'you disconnected from X'
// so we should get rid of it. :)
@@ -418,14 +455,23 @@ class VPNService : android.net.VpnService() {
* Create a Wireguard [Config] from a [json] string - The [json] will be created in
* AndroidController.cpp
*/
- private fun buildWireguardConfig(jServer: JSONObject, obj: JSONObject): Config {
+ private fun buildWireguardConfig(
+ jServer: JSONObject,
+ obj: JSONObject,
+ obfuscator: Obfuscator? = null,
+ ): Config {
val confBuilder = Config.Builder()
val peerBuilder = Peer.Builder()
- val ep =
+ val ep = if (obfuscator != null) {
+ // Send WireGuard traffic to the local obfuscator instead of the
+ // real server
+ InetEndpoint.parse("127.0.0.1:${obfuscator.localPort}")
+ } else {
InetEndpoint.parse(
jServer.getString("ipv4AddrIn") + ":" + jServer.getString("port"),
)
+ }
peerBuilder.setEndpoint(ep)
peerBuilder.setPublicKey(Key.fromBase64(jServer.getString("publicKey")))
diff --git a/android/obfuscator/build.gradle.kts b/android/obfuscator/build.gradle.kts
new file mode 100644
index 0000000000..0e3f5a78a6
--- /dev/null
+++ b/android/obfuscator/build.gradle.kts
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+plugins {
+ id("com.android.library")
+ id("kotlin-android")
+}
+
+android {
+ namespace = "org.mozilla.firefox.vpn.obfuscator"
+ compileSdk = Config.compileSdkVersion
+ ndkVersion = Config.ndkVersion
+
+ defaultConfig {
+ minSdk = Config.minSdkVersion
+ targetSdk = Config.targetSdkVersion
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+
+ buildFeatures {
+ buildConfig = false
+ }
+
+ buildTypes {
+ debug {
+ isMinifyEnabled = false
+ }
+ release {
+ isMinifyEnabled = false
+ }
+ }
+}
+
+dependencies {
+ implementation("net.java.dev.jna:jna:5.18.1@aar")
+}
diff --git a/android/obfuscator/src/main/AndroidManifest.xml b/android/obfuscator/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..74b7379f73
--- /dev/null
+++ b/android/obfuscator/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/android/obfuscator/src/main/java/org/mozilla/firefox/vpn/obfuscator/Obfuscator.kt b/android/obfuscator/src/main/java/org/mozilla/firefox/vpn/obfuscator/Obfuscator.kt
new file mode 100644
index 0000000000..06dd64fecb
--- /dev/null
+++ b/android/obfuscator/src/main/java/org/mozilla/firefox/vpn/obfuscator/Obfuscator.kt
@@ -0,0 +1,142 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.firefox.vpn.obfuscator
+
+import com.sun.jna.Callback
+import com.sun.jna.Library
+import com.sun.jna.Native
+import com.sun.jna.Pointer
+import com.sun.jna.Structure
+
+/**
+ * Kotlin wrapper around the `obfuscators` Rust library.
+ *
+ * Usage from VPNService:
+ * val obfuscator = Obfuscator.start(
+ * method = Obfuscator.Method.UdpOverTcp,
+ * serverIpv4 = "1.2.3.4",
+ * serverPort = 51820,
+ * ...
+ * )
+ * protect(obfuscator.socketV4)
+ * protect(obfuscator.socketV6)
+ * // rewrite WG endpoint -> 127.0.0.1:relay.localPort
+ * obfuscator.stop()
+ */
+class Obfuscator private constructor(
+ private val handle: Pointer,
+ val localPort: Int,
+ val socketV4: Int,
+ val socketV6: Int,
+) {
+ @Volatile
+ private var stopped = false
+
+ fun stop() {
+ synchronized(this) {
+ if (stopped) return
+ stopped = true
+ LIB.obfuscator_stop(handle)
+ }
+ }
+
+ /** Mirrors `ObfuscationMethod` in obfuscators/src/obfuscator.rs. */
+ enum class Method(val value: Int) {
+ NoObfuscation(0),
+ Lwo(1),
+ Masque(2),
+ UdpOverTcp(3),
+ Shadowsocks(4),
+ ;
+
+ companion object {
+ fun fromValue(v: Int): Method? = values().firstOrNull { it.value == v }
+ }
+ }
+
+ companion object {
+ /** Mirrors `ObfuscatorConfig` in obfuscators/src/obfuscator.rs. */
+ @Structure.FieldOrder(
+ "obfuscation_method",
+ "server_ipv4_addr_in",
+ "server_ipv6_addr_in",
+ "server_port",
+ "listen_port",
+ "server_public_key",
+ "public_key",
+ )
+ open class Config : Structure() {
+ @JvmField var obfuscation_method: Int = 0
+ @JvmField var server_ipv4_addr_in: String? = null
+ @JvmField var server_ipv6_addr_in: String? = null
+ @JvmField var server_port: Short = 0
+ @JvmField var listen_port: Short = 0
+ @JvmField var server_public_key: String? = null
+ @JvmField var public_key: String? = null
+
+ class ByReference : Config(), Structure.ByReference
+ }
+
+ interface LogCallback : Callback {
+ fun invoke(level: Int, message: Pointer)
+ }
+
+ fun interface LogHandler {
+ fun onLog(level: Int, message: String)
+ }
+
+ private interface Lib : Library {
+ fun obfuscator_start(cfg: Config.ByReference): Pointer?
+ fun obfuscator_local_port(handle: Pointer): Short
+ fun obfuscator_socket_v4(handle: Pointer): Int
+ fun obfuscator_socket_v6(handle: Pointer): Int
+ fun obfuscator_stop(handle: Pointer)
+ fun obfuscators_set_log_handler(handler: LogCallback)
+ }
+
+ private val LIB: Lib by lazy {
+ Native.load("obfuscators", Lib::class.java)
+ }
+
+ // Avoid log callback being collected by GC
+ @Volatile
+ private var logCallback: LogCallback? = null
+
+ // Install the log handler
+ fun setLogHandler(handler: LogHandler) {
+ val cb = object : LogCallback {
+ override fun invoke(level: Int, message: Pointer) {
+ handler.onLog(level, message.getString(0))
+ }
+ }
+ logCallback = cb
+ LIB.obfuscators_set_log_handler(cb)
+ }
+
+ // Run the obfuscator, returns null on failure
+ fun start(
+ method: Method,
+ serverIpv4: String?,
+ serverIpv6: String?,
+ serverPort: Int,
+ serverPublicKey: String? = null,
+ publicKey: String? = null,
+ ): Obfuscator? {
+ val cfg = Config.ByReference().apply {
+ this.obfuscation_method = method.value
+ this.server_ipv4_addr_in = serverIpv4
+ this.server_ipv6_addr_in = serverIpv6
+ this.server_port = serverPort.toShort()
+ this.server_public_key = serverPublicKey
+ this.public_key = publicKey
+ }
+ val handle = LIB.obfuscator_start(cfg) ?: return null
+ val localPort = LIB.obfuscator_local_port(handle).toInt() and 0xFFFF
+ val socketV4 = LIB.obfuscator_socket_v4(handle)
+ val socketV6 = LIB.obfuscator_socket_v6(handle)
+ return Obfuscator(handle, localPort, socketV4, socketV6)
+ }
+ }
+}
diff --git a/android/settings.gradle b/android/settings.gradle
index ee1c3f15b5..9500b68f1d 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -12,6 +12,7 @@ buildCache {
include ':tunnel'
include ':daemon'
+include ':obfuscator'
include ':ClientCommon'
include ':vpnClient'
include ':common'
diff --git a/env-android.yml b/env-android.yml
index df8a86ffbf..d768c43402 100644
--- a/env-android.yml
+++ b/env-android.yml
@@ -5,12 +5,12 @@ dependencies:
- ccache=4.10.1
- python=3.12
- pip=25.0
- - rust=1.85
+ - rust=1.91
- go=1.24.5
- - rust-std-armv7-linux-androideabi=1.85
- - rust-std-x86_64-linux-android=1.85
- - rust-std-i686-linux-android=1.85
- - rust-std-aarch64-linux-android=1.85
+ - rust-std-armv7-linux-androideabi=1.91
+ - rust-std-x86_64-linux-android=1.91
+ - rust-std-i686-linux-android=1.91
+ - rust-std-aarch64-linux-android=1.91
- cmake=4.2.1
- ninja=1.11.0
- conda-forge::openjdk=17
diff --git a/env-apple.yml b/env-apple.yml
index 8d891acbbe..f2856dd378 100644
--- a/env-apple.yml
+++ b/env-apple.yml
@@ -11,11 +11,11 @@ dependencies:
- python=3.12
- nodejs=20.*
- pip=25.0
- - rust=1.85
- - rust-std-aarch64-apple-darwin=1.85
- - rust-std-x86_64-apple-darwin=1.85
- - rust-std-aarch64-apple-ios=1.85
- - rust-std-x86_64-apple-ios=1.85
+ - rust=1.91
+ - rust-std-aarch64-apple-darwin=1.91
+ - rust-std-x86_64-apple-darwin=1.91
+ - rust-std-aarch64-apple-ios=1.91
+ - rust-std-x86_64-apple-ios=1.91
- go=1.23
- compiler-rt=20.1.8
- compiler-rt_osx-64
diff --git a/env-wasm.yml b/env-wasm.yml
index d620354dd3..1e94cb30e7 100644
--- a/env-wasm.yml
+++ b/env-wasm.yml
@@ -5,8 +5,8 @@ dependencies:
- python=3.12
- nodejs=20.*
- pip=25.0
- - rust=1.85
- - rust-std-wasm32-unknown-emscripten=1.85
+ - rust=1.91
+ - rust-std-wasm32-unknown-emscripten=1.91
- cmake=3.26.3
- ninja=1.11.0
- pip:
diff --git a/env-windows.yml b/env-windows.yml
index ddceb8e168..a042b3b718 100644
--- a/env-windows.yml
+++ b/env-windows.yml
@@ -11,9 +11,9 @@ dependencies:
- python=3.12
- nodejs=20.*
- pip=25.0
- - rust=1.85.0
- - rust-std-aarch64-pc-windows-msvc=1.85.0
- - rust-std-x86_64-pc-windows-msvc=1.85.0
+ - rust=1.91.0
+ - rust-std-aarch64-pc-windows-msvc=1.91.0
+ - rust-std-x86_64-pc-windows-msvc=1.91.0
- compiler-rt=20.1.8
- compiler-rt_win-64
- cmake=4.2.1
diff --git a/linux/debian/control b/linux/debian/control
index 6c4f138ce2..ac8105f966 100644
--- a/linux/debian/control
+++ b/linux/debian/control
@@ -10,7 +10,7 @@ Build-Depends: debhelper (>= 13),
gcc:native (>=4:8.0.0~),
g++:native (>=4:8.0.0~),
golang:native (>=2:1.18~),
- cargo:native (>=1.85) | cargo-web:native (>=1.85) | cargo-1.85:native,
+ cargo:native (>=1.91) | cargo-web:native (>=1.91) | cargo-1.91:native,
python3-yaml:native,
python3-jinja2:native,
python3-click:native,
diff --git a/linux/debian/mozillavpn.install b/linux/debian/mozillavpn.install
index f45631bc69..91900cdff8 100644
--- a/linux/debian/mozillavpn.install
+++ b/linux/debian/mozillavpn.install
@@ -4,6 +4,7 @@ etc/opt/chrome/native-messaging-hosts/mozillavpn.json
${env:SYSTEMD_UNIT_PATH}/mozillavpn.service
${env:SYSTEMD_UNIT_PATH}/socksproxy.service
usr/bin/mozillavpn
+usr/bin/mozillavpn-obfuscator
usr/bin/socksproxy
lib/mozilla/native-messaging-hosts/mozillavpn.json
usr/share/applications/org.mozilla.vpn.desktop
diff --git a/linux/flatpak/flatpak-vpn-crates.json b/linux/flatpak/flatpak-vpn-crates.json
index 1d38433ea8..774454d006 100644
--- a/linux/flatpak/flatpak-vpn-crates.json
+++ b/linux/flatpak/flatpak-vpn-crates.json
@@ -1,68 +1,9 @@
[
{
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/adler2/adler2-2.0.1.crate",
- "sha256": "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa",
- "dest": "cargo/vendor/adler2-2.0.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa\", \"files\": {}}",
- "dest": "cargo/vendor/adler2-2.0.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/aho-corasick/aho-corasick-1.1.4.crate",
- "sha256": "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301",
- "dest": "cargo/vendor/aho-corasick-1.1.4"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301\", \"files\": {}}",
- "dest": "cargo/vendor/aho-corasick-1.1.4",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/android_log-sys/android_log-sys-0.2.0.crate",
- "sha256": "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e",
- "dest": "cargo/vendor/android_log-sys-0.2.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e\", \"files\": {}}",
- "dest": "cargo/vendor/android_log-sys-0.2.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/android_logger/android_logger-0.12.0.crate",
- "sha256": "037f3e1da32ddba7770530e69258b742c15ad67bdf90e5f6b35f4b6db9a60eb7",
- "dest": "cargo/vendor/android_logger-0.12.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"037f3e1da32ddba7770530e69258b742c15ad67bdf90e5f6b35f4b6db9a60eb7\", \"files\": {}}",
- "dest": "cargo/vendor/android_logger-0.12.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/android_system_properties/android_system_properties-0.1.5.crate",
- "sha256": "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311",
- "dest": "cargo/vendor/android_system_properties-0.1.5"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311\", \"files\": {}}",
- "dest": "cargo/vendor/android_system_properties-0.1.5",
- "dest-filename": ".cargo-checksum.json"
+ "type": "git",
+ "url": "https://github.com/mullvad/udp-over-tcp",
+ "commit": "5c6d8f44a5aa12ed9bb4ae51dd17e5e22e5ec303",
+ "dest": "flatpak-cargo/git/udp-over-tcp-5c6d8f4"
},
{
"type": "archive",
@@ -80,14 +21,14 @@
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/anstyle/anstyle-1.0.13.crate",
- "sha256": "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78",
- "dest": "cargo/vendor/anstyle-1.0.13"
+ "url": "https://static.crates.io/crates/anstyle/anstyle-1.0.14.crate",
+ "sha256": "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000",
+ "dest": "cargo/vendor/anstyle-1.0.14"
},
{
"type": "inline",
- "contents": "{\"package\": \"5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78\", \"files\": {}}",
- "dest": "cargo/vendor/anstyle-1.0.13",
+ "contents": "{\"package\": \"940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000\", \"files\": {}}",
+ "dest": "cargo/vendor/anstyle-1.0.14",
"dest-filename": ".cargo-checksum.json"
},
{
@@ -132,2107 +73,1085 @@
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/anyhow/anyhow-1.0.100.crate",
- "sha256": "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61",
- "dest": "cargo/vendor/anyhow-1.0.100"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61\", \"files\": {}}",
- "dest": "cargo/vendor/anyhow-1.0.100",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/arrayref/arrayref-0.3.9.crate",
- "sha256": "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb",
- "dest": "cargo/vendor/arrayref-0.3.9"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb\", \"files\": {}}",
- "dest": "cargo/vendor/arrayref-0.3.9",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/askama/askama-0.13.1.crate",
- "sha256": "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7",
- "dest": "cargo/vendor/askama-0.13.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7\", \"files\": {}}",
- "dest": "cargo/vendor/askama-0.13.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/askama_derive/askama_derive-0.13.1.crate",
- "sha256": "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac",
- "dest": "cargo/vendor/askama_derive-0.13.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac\", \"files\": {}}",
- "dest": "cargo/vendor/askama_derive-0.13.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/askama_parser/askama_parser-0.13.0.crate",
- "sha256": "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f",
- "dest": "cargo/vendor/askama_parser-0.13.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f\", \"files\": {}}",
- "dest": "cargo/vendor/askama_parser-0.13.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/asn1-rs/asn1-rs-0.7.1.crate",
- "sha256": "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60",
- "dest": "cargo/vendor/asn1-rs-0.7.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60\", \"files\": {}}",
- "dest": "cargo/vendor/asn1-rs-0.7.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/asn1-rs-derive/asn1-rs-derive-0.6.0.crate",
- "sha256": "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c",
- "dest": "cargo/vendor/asn1-rs-derive-0.6.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c\", \"files\": {}}",
- "dest": "cargo/vendor/asn1-rs-derive-0.6.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/asn1-rs-impl/asn1-rs-impl-0.2.0.crate",
- "sha256": "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7",
- "dest": "cargo/vendor/asn1-rs-impl-0.2.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7\", \"files\": {}}",
- "dest": "cargo/vendor/asn1-rs-impl-0.2.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/autocfg/autocfg-1.5.0.crate",
- "sha256": "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8",
- "dest": "cargo/vendor/autocfg-1.5.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8\", \"files\": {}}",
- "dest": "cargo/vendor/autocfg-1.5.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/basic-toml/basic-toml-0.1.10.crate",
- "sha256": "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a",
- "dest": "cargo/vendor/basic-toml-0.1.10"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a\", \"files\": {}}",
- "dest": "cargo/vendor/basic-toml-0.1.10",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/bincode/bincode-1.3.3.crate",
- "sha256": "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad",
- "dest": "cargo/vendor/bincode-1.3.3"
+ "url": "https://static.crates.io/crates/asn1-rs/asn1-rs-0.7.2.crate",
+ "sha256": "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8",
+ "dest": "cargo/vendor/asn1-rs-0.7.2"
},
{
"type": "inline",
- "contents": "{\"package\": \"b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad\", \"files\": {}}",
- "dest": "cargo/vendor/bincode-1.3.3",
+ "contents": "{\"package\": \"b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8\", \"files\": {}}",
+ "dest": "cargo/vendor/asn1-rs-0.7.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/bitflags/bitflags-2.10.0.crate",
- "sha256": "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3",
- "dest": "cargo/vendor/bitflags-2.10.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3\", \"files\": {}}",
- "dest": "cargo/vendor/bitflags-2.10.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/bumpalo/bumpalo-3.19.1.crate",
- "sha256": "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510",
- "dest": "cargo/vendor/bumpalo-3.19.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510\", \"files\": {}}",
- "dest": "cargo/vendor/bumpalo-3.19.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/bytes/bytes-1.11.0.crate",
- "sha256": "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3",
- "dest": "cargo/vendor/bytes-1.11.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3\", \"files\": {}}",
- "dest": "cargo/vendor/bytes-1.11.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/camino/camino-1.2.2.crate",
- "sha256": "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48",
- "dest": "cargo/vendor/camino-1.2.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48\", \"files\": {}}",
- "dest": "cargo/vendor/camino-1.2.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/cbindgen/cbindgen-0.29.2.crate",
- "sha256": "befbfd072a8e81c02f8c507aefce431fe5e7d051f83d48a23ffc9b9fe5a11799",
- "dest": "cargo/vendor/cbindgen-0.29.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"befbfd072a8e81c02f8c507aefce431fe5e7d051f83d48a23ffc9b9fe5a11799\", \"files\": {}}",
- "dest": "cargo/vendor/cbindgen-0.29.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/cc/cc-1.2.49.crate",
- "sha256": "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215",
- "dest": "cargo/vendor/cc-1.2.49"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215\", \"files\": {}}",
- "dest": "cargo/vendor/cc-1.2.49",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/cfg-if/cfg-if-1.0.4.crate",
- "sha256": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801",
- "dest": "cargo/vendor/cfg-if-1.0.4"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801\", \"files\": {}}",
- "dest": "cargo/vendor/cfg-if-1.0.4",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/chrono/chrono-0.4.42.crate",
- "sha256": "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2",
- "dest": "cargo/vendor/chrono-0.4.42"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2\", \"files\": {}}",
- "dest": "cargo/vendor/chrono-0.4.42",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/clap/clap-4.5.53.crate",
- "sha256": "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8",
- "dest": "cargo/vendor/clap-4.5.53"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8\", \"files\": {}}",
- "dest": "cargo/vendor/clap-4.5.53",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/clap_builder/clap_builder-4.5.53.crate",
- "sha256": "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00",
- "dest": "cargo/vendor/clap_builder-4.5.53"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00\", \"files\": {}}",
- "dest": "cargo/vendor/clap_builder-4.5.53",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/clap_lex/clap_lex-0.7.6.crate",
- "sha256": "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d",
- "dest": "cargo/vendor/clap_lex-0.7.6"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d\", \"files\": {}}",
- "dest": "cargo/vendor/clap_lex-0.7.6",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/colorchoice/colorchoice-1.0.4.crate",
- "sha256": "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75",
- "dest": "cargo/vendor/colorchoice-1.0.4"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75\", \"files\": {}}",
- "dest": "cargo/vendor/colorchoice-1.0.4",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/core-foundation-sys/core-foundation-sys-0.8.7.crate",
- "sha256": "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b",
- "dest": "cargo/vendor/core-foundation-sys-0.8.7"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b\", \"files\": {}}",
- "dest": "cargo/vendor/core-foundation-sys-0.8.7",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/crc32fast/crc32fast-1.5.0.crate",
- "sha256": "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511",
- "dest": "cargo/vendor/crc32fast-1.5.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511\", \"files\": {}}",
- "dest": "cargo/vendor/crc32fast-1.5.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/crossbeam-channel/crossbeam-channel-0.5.15.crate",
- "sha256": "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2",
- "dest": "cargo/vendor/crossbeam-channel-0.5.15"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2\", \"files\": {}}",
- "dest": "cargo/vendor/crossbeam-channel-0.5.15",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/crossbeam-utils/crossbeam-utils-0.8.21.crate",
- "sha256": "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28",
- "dest": "cargo/vendor/crossbeam-utils-0.8.21"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28\", \"files\": {}}",
- "dest": "cargo/vendor/crossbeam-utils-0.8.21",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/ctor/ctor-0.2.9.crate",
- "sha256": "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501",
- "dest": "cargo/vendor/ctor-0.2.9"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501\", \"files\": {}}",
- "dest": "cargo/vendor/ctor-0.2.9",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/dashmap/dashmap-4.0.2.crate",
- "sha256": "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c",
- "dest": "cargo/vendor/dashmap-4.0.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c\", \"files\": {}}",
- "dest": "cargo/vendor/dashmap-4.0.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/data-encoding/data-encoding-2.10.0.crate",
- "sha256": "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea",
- "dest": "cargo/vendor/data-encoding-2.10.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea\", \"files\": {}}",
- "dest": "cargo/vendor/data-encoding-2.10.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/der-parser/der-parser-10.0.0.crate",
- "sha256": "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6",
- "dest": "cargo/vendor/der-parser-10.0.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6\", \"files\": {}}",
- "dest": "cargo/vendor/der-parser-10.0.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/deranged/deranged-0.5.5.crate",
- "sha256": "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587",
- "dest": "cargo/vendor/deranged-0.5.5"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587\", \"files\": {}}",
- "dest": "cargo/vendor/deranged-0.5.5",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/displaydoc/displaydoc-0.2.5.crate",
- "sha256": "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0",
- "dest": "cargo/vendor/displaydoc-0.2.5"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0\", \"files\": {}}",
- "dest": "cargo/vendor/displaydoc-0.2.5",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/either/either-1.15.0.crate",
- "sha256": "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719",
- "dest": "cargo/vendor/either-1.15.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719\", \"files\": {}}",
- "dest": "cargo/vendor/either-1.15.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/env_logger/env_logger-0.10.2.crate",
- "sha256": "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580",
- "dest": "cargo/vendor/env_logger-0.10.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580\", \"files\": {}}",
- "dest": "cargo/vendor/env_logger-0.10.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/equivalent/equivalent-1.0.2.crate",
- "sha256": "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f",
- "dest": "cargo/vendor/equivalent-1.0.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f\", \"files\": {}}",
- "dest": "cargo/vendor/equivalent-1.0.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/errno/errno-0.3.14.crate",
- "sha256": "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb",
- "dest": "cargo/vendor/errno-0.3.14"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb\", \"files\": {}}",
- "dest": "cargo/vendor/errno-0.3.14",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/fastrand/fastrand-2.3.0.crate",
- "sha256": "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be",
- "dest": "cargo/vendor/fastrand-2.3.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be\", \"files\": {}}",
- "dest": "cargo/vendor/fastrand-2.3.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/ffi-support/ffi-support-0.4.4.crate",
- "sha256": "27838c6815cfe9de2d3aeb145ffd19e565f577414b33f3bdbf42fe040e9e0ff6",
- "dest": "cargo/vendor/ffi-support-0.4.4"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"27838c6815cfe9de2d3aeb145ffd19e565f577414b33f3bdbf42fe040e9e0ff6\", \"files\": {}}",
- "dest": "cargo/vendor/ffi-support-0.4.4",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/find-msvc-tools/find-msvc-tools-0.1.5.crate",
- "sha256": "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844",
- "dest": "cargo/vendor/find-msvc-tools-0.1.5"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844\", \"files\": {}}",
- "dest": "cargo/vendor/find-msvc-tools-0.1.5",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/flate2/flate2-1.1.5.crate",
- "sha256": "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb",
- "dest": "cargo/vendor/flate2-1.1.5"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb\", \"files\": {}}",
- "dest": "cargo/vendor/flate2-1.1.5",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/form_urlencoded/form_urlencoded-1.2.2.crate",
- "sha256": "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf",
- "dest": "cargo/vendor/form_urlencoded-1.2.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf\", \"files\": {}}",
- "dest": "cargo/vendor/form_urlencoded-1.2.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/fs-err/fs-err-2.11.0.crate",
- "sha256": "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41",
- "dest": "cargo/vendor/fs-err-2.11.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41\", \"files\": {}}",
- "dest": "cargo/vendor/fs-err-2.11.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/getrandom/getrandom-0.2.16.crate",
- "sha256": "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592",
- "dest": "cargo/vendor/getrandom-0.2.16"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592\", \"files\": {}}",
- "dest": "cargo/vendor/getrandom-0.2.16",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/getrandom/getrandom-0.3.4.crate",
- "sha256": "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd",
- "dest": "cargo/vendor/getrandom-0.3.4"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd\", \"files\": {}}",
- "dest": "cargo/vendor/getrandom-0.3.4",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/glob/glob-0.3.3.crate",
- "sha256": "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280",
- "dest": "cargo/vendor/glob-0.3.3"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280\", \"files\": {}}",
- "dest": "cargo/vendor/glob-0.3.3",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/goblin/goblin-0.8.2.crate",
- "sha256": "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47",
- "dest": "cargo/vendor/goblin-0.8.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47\", \"files\": {}}",
- "dest": "cargo/vendor/goblin-0.8.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/hashbrown/hashbrown-0.16.1.crate",
- "sha256": "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100",
- "dest": "cargo/vendor/hashbrown-0.16.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100\", \"files\": {}}",
- "dest": "cargo/vendor/hashbrown-0.16.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/heck/heck-0.5.0.crate",
- "sha256": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea",
- "dest": "cargo/vendor/heck-0.5.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea\", \"files\": {}}",
- "dest": "cargo/vendor/heck-0.5.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/hermit-abi/hermit-abi-0.5.2.crate",
- "sha256": "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c",
- "dest": "cargo/vendor/hermit-abi-0.5.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c\", \"files\": {}}",
- "dest": "cargo/vendor/hermit-abi-0.5.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/hex/hex-0.4.3.crate",
- "sha256": "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70",
- "dest": "cargo/vendor/hex-0.4.3"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70\", \"files\": {}}",
- "dest": "cargo/vendor/hex-0.4.3",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/humantime/humantime-2.3.0.crate",
- "sha256": "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424",
- "dest": "cargo/vendor/humantime-2.3.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424\", \"files\": {}}",
- "dest": "cargo/vendor/humantime-2.3.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/iana-time-zone/iana-time-zone-0.1.64.crate",
- "sha256": "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb",
- "dest": "cargo/vendor/iana-time-zone-0.1.64"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb\", \"files\": {}}",
- "dest": "cargo/vendor/iana-time-zone-0.1.64",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/iana-time-zone-haiku/iana-time-zone-haiku-0.1.2.crate",
- "sha256": "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f",
- "dest": "cargo/vendor/iana-time-zone-haiku-0.1.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f\", \"files\": {}}",
- "dest": "cargo/vendor/iana-time-zone-haiku-0.1.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/id-arena/id-arena-2.2.1.crate",
- "sha256": "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005",
- "dest": "cargo/vendor/id-arena-2.2.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005\", \"files\": {}}",
- "dest": "cargo/vendor/id-arena-2.2.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/idna/idna-0.4.0.crate",
- "sha256": "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c",
- "dest": "cargo/vendor/idna-0.4.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c\", \"files\": {}}",
- "dest": "cargo/vendor/idna-0.4.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/indexmap/indexmap-2.12.1.crate",
- "sha256": "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2",
- "dest": "cargo/vendor/indexmap-2.12.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2\", \"files\": {}}",
- "dest": "cargo/vendor/indexmap-2.12.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/inherent/inherent-1.0.13.crate",
- "sha256": "c727f80bfa4a6c6e2508d2f05b6f4bfce242030bd88ed15ae5331c5b5d30fba7",
- "dest": "cargo/vendor/inherent-1.0.13"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"c727f80bfa4a6c6e2508d2f05b6f4bfce242030bd88ed15ae5331c5b5d30fba7\", \"files\": {}}",
- "dest": "cargo/vendor/inherent-1.0.13",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/iri-string/iri-string-0.5.6.crate",
- "sha256": "bf071934ee7ee97e52fa1868a9540a7885eab75926bd70794030304a9797cea1",
- "dest": "cargo/vendor/iri-string-0.5.6"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"bf071934ee7ee97e52fa1868a9540a7885eab75926bd70794030304a9797cea1\", \"files\": {}}",
- "dest": "cargo/vendor/iri-string-0.5.6",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/is_terminal_polyfill/is_terminal_polyfill-1.70.2.crate",
- "sha256": "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695",
- "dest": "cargo/vendor/is_terminal_polyfill-1.70.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695\", \"files\": {}}",
- "dest": "cargo/vendor/is_terminal_polyfill-1.70.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/iso8601/iso8601-0.4.2.crate",
- "sha256": "e5b94fbeb759754d87e1daea745bc8efd3037cd16980331fe1d1524c9a79ce96",
- "dest": "cargo/vendor/iso8601-0.4.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"e5b94fbeb759754d87e1daea745bc8efd3037cd16980331fe1d1524c9a79ce96\", \"files\": {}}",
- "dest": "cargo/vendor/iso8601-0.4.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/itertools/itertools-0.10.5.crate",
- "sha256": "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473",
- "dest": "cargo/vendor/itertools-0.10.5"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473\", \"files\": {}}",
- "dest": "cargo/vendor/itertools-0.10.5",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/itoa/itoa-1.0.15.crate",
- "sha256": "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c",
- "dest": "cargo/vendor/itoa-1.0.15"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c\", \"files\": {}}",
- "dest": "cargo/vendor/itoa-1.0.15",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/js-sys/js-sys-0.3.83.crate",
- "sha256": "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8",
- "dest": "cargo/vendor/js-sys-0.3.83"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8\", \"files\": {}}",
- "dest": "cargo/vendor/js-sys-0.3.83",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/json-pointer/json-pointer-0.3.4.crate",
- "sha256": "5fe841b94e719a482213cee19dd04927cf412f26d8dc84c5a446c081e49c2997",
- "dest": "cargo/vendor/json-pointer-0.3.4"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"5fe841b94e719a482213cee19dd04927cf412f26d8dc84c5a446c081e49c2997\", \"files\": {}}",
- "dest": "cargo/vendor/json-pointer-0.3.4",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/jsonschema-valid/jsonschema-valid-0.5.2.crate",
- "sha256": "998c0b6acd4e20747af58157c9d55878970f546088e17c9870f4b41bc8a032a3",
- "dest": "cargo/vendor/jsonschema-valid-0.5.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"998c0b6acd4e20747af58157c9d55878970f546088e17c9870f4b41bc8a032a3\", \"files\": {}}",
- "dest": "cargo/vendor/jsonschema-valid-0.5.2",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/lazy_static/lazy_static-1.5.0.crate",
- "sha256": "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe",
- "dest": "cargo/vendor/lazy_static-1.5.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe\", \"files\": {}}",
- "dest": "cargo/vendor/lazy_static-1.5.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/libc/libc-0.2.178.crate",
- "sha256": "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091",
- "dest": "cargo/vendor/libc-0.2.178"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091\", \"files\": {}}",
- "dest": "cargo/vendor/libc-0.2.178",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/linux-raw-sys/linux-raw-sys-0.11.0.crate",
- "sha256": "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039",
- "dest": "cargo/vendor/linux-raw-sys-0.11.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039\", \"files\": {}}",
- "dest": "cargo/vendor/linux-raw-sys-0.11.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/log/log-0.4.29.crate",
- "sha256": "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897",
- "dest": "cargo/vendor/log-0.4.29"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897\", \"files\": {}}",
- "dest": "cargo/vendor/log-0.4.29",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/malloc_size_of_derive/malloc_size_of_derive-0.1.3.crate",
- "sha256": "f44db74bde26fdf427af23f1d146c211aed857c59e3be750cf2617f6b0b05c94",
- "dest": "cargo/vendor/malloc_size_of_derive-0.1.3"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"f44db74bde26fdf427af23f1d146c211aed857c59e3be750cf2617f6b0b05c94\", \"files\": {}}",
- "dest": "cargo/vendor/malloc_size_of_derive-0.1.3",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/memchr/memchr-2.7.6.crate",
- "sha256": "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273",
- "dest": "cargo/vendor/memchr-2.7.6"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273\", \"files\": {}}",
- "dest": "cargo/vendor/memchr-2.7.6",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/minimal-lexical/minimal-lexical-0.2.1.crate",
- "sha256": "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a",
- "dest": "cargo/vendor/minimal-lexical-0.2.1"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a\", \"files\": {}}",
- "dest": "cargo/vendor/minimal-lexical-0.2.1",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/miniz_oxide/miniz_oxide-0.8.9.crate",
- "sha256": "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316",
- "dest": "cargo/vendor/miniz_oxide-0.8.9"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316\", \"files\": {}}",
- "dest": "cargo/vendor/miniz_oxide-0.8.9",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/nom/nom-7.1.3.crate",
- "sha256": "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a",
- "dest": "cargo/vendor/nom-7.1.3"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a\", \"files\": {}}",
- "dest": "cargo/vendor/nom-7.1.3",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/num-bigint/num-bigint-0.4.6.crate",
- "sha256": "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9",
- "dest": "cargo/vendor/num-bigint-0.4.6"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9\", \"files\": {}}",
- "dest": "cargo/vendor/num-bigint-0.4.6",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/num-conv/num-conv-0.1.0.crate",
- "sha256": "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9",
- "dest": "cargo/vendor/num-conv-0.1.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9\", \"files\": {}}",
- "dest": "cargo/vendor/num-conv-0.1.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/num-integer/num-integer-0.1.46.crate",
- "sha256": "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f",
- "dest": "cargo/vendor/num-integer-0.1.46"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f\", \"files\": {}}",
- "dest": "cargo/vendor/num-integer-0.1.46",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/num-traits/num-traits-0.2.19.crate",
- "sha256": "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841",
- "dest": "cargo/vendor/num-traits-0.2.19"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841\", \"files\": {}}",
- "dest": "cargo/vendor/num-traits-0.2.19",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/num_cpus/num_cpus-1.17.0.crate",
- "sha256": "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b",
- "dest": "cargo/vendor/num_cpus-1.17.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b\", \"files\": {}}",
- "dest": "cargo/vendor/num_cpus-1.17.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/oid-registry/oid-registry-0.8.1.crate",
- "sha256": "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7",
- "dest": "cargo/vendor/oid-registry-0.8.1"
+ "url": "https://static.crates.io/crates/asn1-rs-derive/asn1-rs-derive-0.6.0.crate",
+ "sha256": "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c",
+ "dest": "cargo/vendor/asn1-rs-derive-0.6.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7\", \"files\": {}}",
- "dest": "cargo/vendor/oid-registry-0.8.1",
+ "contents": "{\"package\": \"3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c\", \"files\": {}}",
+ "dest": "cargo/vendor/asn1-rs-derive-0.6.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/once_cell/once_cell-1.21.4.crate",
- "sha256": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50",
- "dest": "cargo/vendor/once_cell-1.21.4"
+ "url": "https://static.crates.io/crates/asn1-rs-impl/asn1-rs-impl-0.2.0.crate",
+ "sha256": "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7",
+ "dest": "cargo/vendor/asn1-rs-impl-0.2.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50\", \"files\": {}}",
- "dest": "cargo/vendor/once_cell-1.21.4",
+ "contents": "{\"package\": \"7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7\", \"files\": {}}",
+ "dest": "cargo/vendor/asn1-rs-impl-0.2.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/once_cell_polyfill/once_cell_polyfill-1.70.2.crate",
- "sha256": "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe",
- "dest": "cargo/vendor/once_cell_polyfill-1.70.2"
+ "url": "https://static.crates.io/crates/autocfg/autocfg-1.5.1.crate",
+ "sha256": "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53",
+ "dest": "cargo/vendor/autocfg-1.5.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe\", \"files\": {}}",
- "dest": "cargo/vendor/once_cell_polyfill-1.70.2",
+ "contents": "{\"package\": \"f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53\", \"files\": {}}",
+ "dest": "cargo/vendor/autocfg-1.5.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/ordered-float/ordered-float-3.9.2.crate",
- "sha256": "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc",
- "dest": "cargo/vendor/ordered-float-3.9.2"
+ "url": "https://static.crates.io/crates/base64/base64-0.22.1.crate",
+ "sha256": "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6",
+ "dest": "cargo/vendor/base64-0.22.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc\", \"files\": {}}",
- "dest": "cargo/vendor/ordered-float-3.9.2",
+ "contents": "{\"package\": \"72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6\", \"files\": {}}",
+ "dest": "cargo/vendor/base64-0.22.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/oslog/oslog-0.1.0.crate",
- "sha256": "8343ce955f18e7e68c0207dd0ea776ec453035685395ababd2ea651c569728b3",
- "dest": "cargo/vendor/oslog-0.1.0"
+ "url": "https://static.crates.io/crates/bitflags/bitflags-2.11.1.crate",
+ "sha256": "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3",
+ "dest": "cargo/vendor/bitflags-2.11.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"8343ce955f18e7e68c0207dd0ea776ec453035685395ababd2ea651c569728b3\", \"files\": {}}",
- "dest": "cargo/vendor/oslog-0.1.0",
+ "contents": "{\"package\": \"c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3\", \"files\": {}}",
+ "dest": "cargo/vendor/bitflags-2.11.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/percent-encoding/percent-encoding-2.3.2.crate",
- "sha256": "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220",
- "dest": "cargo/vendor/percent-encoding-2.3.2"
+ "url": "https://static.crates.io/crates/bytes/bytes-1.11.1.crate",
+ "sha256": "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33",
+ "dest": "cargo/vendor/bytes-1.11.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220\", \"files\": {}}",
- "dest": "cargo/vendor/percent-encoding-2.3.2",
+ "contents": "{\"package\": \"1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33\", \"files\": {}}",
+ "dest": "cargo/vendor/bytes-1.11.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/plain/plain-0.2.3.crate",
- "sha256": "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6",
- "dest": "cargo/vendor/plain-0.2.3"
+ "url": "https://static.crates.io/crates/cc/cc-1.2.63.crate",
+ "sha256": "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f",
+ "dest": "cargo/vendor/cc-1.2.63"
},
{
"type": "inline",
- "contents": "{\"package\": \"b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6\", \"files\": {}}",
- "dest": "cargo/vendor/plain-0.2.3",
+ "contents": "{\"package\": \"556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f\", \"files\": {}}",
+ "dest": "cargo/vendor/cc-1.2.63",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/powerfmt/powerfmt-0.2.0.crate",
- "sha256": "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391",
- "dest": "cargo/vendor/powerfmt-0.2.0"
+ "url": "https://static.crates.io/crates/cfg-if/cfg-if-1.0.4.crate",
+ "sha256": "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801",
+ "dest": "cargo/vendor/cfg-if-1.0.4"
},
{
"type": "inline",
- "contents": "{\"package\": \"439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391\", \"files\": {}}",
- "dest": "cargo/vendor/powerfmt-0.2.0",
+ "contents": "{\"package\": \"9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801\", \"files\": {}}",
+ "dest": "cargo/vendor/cfg-if-1.0.4",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/proc-macro2/proc-macro2-1.0.103.crate",
- "sha256": "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8",
- "dest": "cargo/vendor/proc-macro2-1.0.103"
+ "url": "https://static.crates.io/crates/cfg_aliases/cfg_aliases-0.2.1.crate",
+ "sha256": "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724",
+ "dest": "cargo/vendor/cfg_aliases-0.2.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8\", \"files\": {}}",
- "dest": "cargo/vendor/proc-macro2-1.0.103",
+ "contents": "{\"package\": \"613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724\", \"files\": {}}",
+ "dest": "cargo/vendor/cfg_aliases-0.2.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/quote/quote-1.0.42.crate",
- "sha256": "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f",
- "dest": "cargo/vendor/quote-1.0.42"
+ "url": "https://static.crates.io/crates/clap/clap-4.5.20.crate",
+ "sha256": "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8",
+ "dest": "cargo/vendor/clap-4.5.20"
},
{
"type": "inline",
- "contents": "{\"package\": \"a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f\", \"files\": {}}",
- "dest": "cargo/vendor/quote-1.0.42",
+ "contents": "{\"package\": \"b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8\", \"files\": {}}",
+ "dest": "cargo/vendor/clap-4.5.20",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/r-efi/r-efi-5.3.0.crate",
- "sha256": "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f",
- "dest": "cargo/vendor/r-efi-5.3.0"
+ "url": "https://static.crates.io/crates/clap_builder/clap_builder-4.5.20.crate",
+ "sha256": "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54",
+ "dest": "cargo/vendor/clap_builder-4.5.20"
},
{
"type": "inline",
- "contents": "{\"package\": \"69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f\", \"files\": {}}",
- "dest": "cargo/vendor/r-efi-5.3.0",
+ "contents": "{\"package\": \"19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54\", \"files\": {}}",
+ "dest": "cargo/vendor/clap_builder-4.5.20",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/regex/regex-1.12.2.crate",
- "sha256": "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4",
- "dest": "cargo/vendor/regex-1.12.2"
+ "url": "https://static.crates.io/crates/clap_derive/clap_derive-4.5.18.crate",
+ "sha256": "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab",
+ "dest": "cargo/vendor/clap_derive-4.5.18"
},
{
"type": "inline",
- "contents": "{\"package\": \"843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4\", \"files\": {}}",
- "dest": "cargo/vendor/regex-1.12.2",
+ "contents": "{\"package\": \"4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab\", \"files\": {}}",
+ "dest": "cargo/vendor/clap_derive-4.5.18",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/regex-automata/regex-automata-0.4.13.crate",
- "sha256": "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c",
- "dest": "cargo/vendor/regex-automata-0.4.13"
+ "url": "https://static.crates.io/crates/clap_lex/clap_lex-0.7.7.crate",
+ "sha256": "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32",
+ "dest": "cargo/vendor/clap_lex-0.7.7"
},
{
"type": "inline",
- "contents": "{\"package\": \"5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c\", \"files\": {}}",
- "dest": "cargo/vendor/regex-automata-0.4.13",
+ "contents": "{\"package\": \"c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32\", \"files\": {}}",
+ "dest": "cargo/vendor/clap_lex-0.7.7",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/regex-syntax/regex-syntax-0.8.8.crate",
- "sha256": "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58",
- "dest": "cargo/vendor/regex-syntax-0.8.8"
+ "url": "https://static.crates.io/crates/colorchoice/colorchoice-1.0.5.crate",
+ "sha256": "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570",
+ "dest": "cargo/vendor/colorchoice-1.0.5"
},
{
"type": "inline",
- "contents": "{\"package\": \"7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58\", \"files\": {}}",
- "dest": "cargo/vendor/regex-syntax-0.8.8",
+ "contents": "{\"package\": \"1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570\", \"files\": {}}",
+ "dest": "cargo/vendor/colorchoice-1.0.5",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/ring/ring-0.17.14.crate",
- "sha256": "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7",
- "dest": "cargo/vendor/ring-0.17.14"
+ "url": "https://static.crates.io/crates/data-encoding/data-encoding-2.11.0.crate",
+ "sha256": "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8",
+ "dest": "cargo/vendor/data-encoding-2.11.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7\", \"files\": {}}",
- "dest": "cargo/vendor/ring-0.17.14",
+ "contents": "{\"package\": \"a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8\", \"files\": {}}",
+ "dest": "cargo/vendor/data-encoding-2.11.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/rkv/rkv-0.20.0.crate",
- "sha256": "0f67a9dbc634fcd36a2d1d800ca818065dcf71a1d907dc35130c2d1552c6e1dc",
- "dest": "cargo/vendor/rkv-0.20.0"
+ "url": "https://static.crates.io/crates/der-parser/der-parser-10.0.0.crate",
+ "sha256": "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6",
+ "dest": "cargo/vendor/der-parser-10.0.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"0f67a9dbc634fcd36a2d1d800ca818065dcf71a1d907dc35130c2d1552c6e1dc\", \"files\": {}}",
- "dest": "cargo/vendor/rkv-0.20.0",
+ "contents": "{\"package\": \"07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6\", \"files\": {}}",
+ "dest": "cargo/vendor/der-parser-10.0.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/rustc-hash/rustc-hash-2.1.1.crate",
- "sha256": "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d",
- "dest": "cargo/vendor/rustc-hash-2.1.1"
+ "url": "https://static.crates.io/crates/deranged/deranged-0.5.8.crate",
+ "sha256": "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c",
+ "dest": "cargo/vendor/deranged-0.5.8"
},
{
"type": "inline",
- "contents": "{\"package\": \"357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d\", \"files\": {}}",
- "dest": "cargo/vendor/rustc-hash-2.1.1",
+ "contents": "{\"package\": \"7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c\", \"files\": {}}",
+ "dest": "cargo/vendor/deranged-0.5.8",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/rusticata-macros/rusticata-macros-4.1.0.crate",
- "sha256": "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632",
- "dest": "cargo/vendor/rusticata-macros-4.1.0"
+ "url": "https://static.crates.io/crates/displaydoc/displaydoc-0.2.6.crate",
+ "sha256": "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f",
+ "dest": "cargo/vendor/displaydoc-0.2.6"
},
{
"type": "inline",
- "contents": "{\"package\": \"faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632\", \"files\": {}}",
- "dest": "cargo/vendor/rusticata-macros-4.1.0",
+ "contents": "{\"package\": \"1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f\", \"files\": {}}",
+ "dest": "cargo/vendor/displaydoc-0.2.6",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/rustix/rustix-1.1.2.crate",
- "sha256": "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e",
- "dest": "cargo/vendor/rustix-1.1.2"
+ "url": "https://static.crates.io/crates/err-context/err-context-0.1.0.crate",
+ "sha256": "449aad22b1364e927ff3bf50f55404efd705c40065fb47f73f28704de707c89e",
+ "dest": "cargo/vendor/err-context-0.1.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e\", \"files\": {}}",
- "dest": "cargo/vendor/rustix-1.1.2",
+ "contents": "{\"package\": \"449aad22b1364e927ff3bf50f55404efd705c40065fb47f73f28704de707c89e\", \"files\": {}}",
+ "dest": "cargo/vendor/err-context-0.1.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/rustversion/rustversion-1.0.22.crate",
- "sha256": "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d",
- "dest": "cargo/vendor/rustversion-1.0.22"
+ "url": "https://static.crates.io/crates/ffi-support/ffi-support-0.4.4.crate",
+ "sha256": "27838c6815cfe9de2d3aeb145ffd19e565f577414b33f3bdbf42fe040e9e0ff6",
+ "dest": "cargo/vendor/ffi-support-0.4.4"
},
{
"type": "inline",
- "contents": "{\"package\": \"b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d\", \"files\": {}}",
- "dest": "cargo/vendor/rustversion-1.0.22",
+ "contents": "{\"package\": \"27838c6815cfe9de2d3aeb145ffd19e565f577414b33f3bdbf42fe040e9e0ff6\", \"files\": {}}",
+ "dest": "cargo/vendor/ffi-support-0.4.4",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/scroll/scroll-0.12.0.crate",
- "sha256": "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6",
- "dest": "cargo/vendor/scroll-0.12.0"
+ "url": "https://static.crates.io/crates/find-msvc-tools/find-msvc-tools-0.1.9.crate",
+ "sha256": "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582",
+ "dest": "cargo/vendor/find-msvc-tools-0.1.9"
},
{
"type": "inline",
- "contents": "{\"package\": \"6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6\", \"files\": {}}",
- "dest": "cargo/vendor/scroll-0.12.0",
+ "contents": "{\"package\": \"5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582\", \"files\": {}}",
+ "dest": "cargo/vendor/find-msvc-tools-0.1.9",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/scroll_derive/scroll_derive-0.12.1.crate",
- "sha256": "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d",
- "dest": "cargo/vendor/scroll_derive-0.12.1"
+ "url": "https://static.crates.io/crates/futures/futures-0.3.32.crate",
+ "sha256": "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d",
+ "dest": "cargo/vendor/futures-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d\", \"files\": {}}",
- "dest": "cargo/vendor/scroll_derive-0.12.1",
+ "contents": "{\"package\": \"8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/serde/serde-1.0.228.crate",
- "sha256": "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e",
- "dest": "cargo/vendor/serde-1.0.228"
+ "url": "https://static.crates.io/crates/futures-channel/futures-channel-0.3.32.crate",
+ "sha256": "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d",
+ "dest": "cargo/vendor/futures-channel-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e\", \"files\": {}}",
- "dest": "cargo/vendor/serde-1.0.228",
+ "contents": "{\"package\": \"07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-channel-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/serde_core/serde_core-1.0.228.crate",
- "sha256": "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad",
- "dest": "cargo/vendor/serde_core-1.0.228"
+ "url": "https://static.crates.io/crates/futures-core/futures-core-0.3.32.crate",
+ "sha256": "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d",
+ "dest": "cargo/vendor/futures-core-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad\", \"files\": {}}",
- "dest": "cargo/vendor/serde_core-1.0.228",
+ "contents": "{\"package\": \"7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-core-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/serde_derive/serde_derive-1.0.228.crate",
- "sha256": "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79",
- "dest": "cargo/vendor/serde_derive-1.0.228"
+ "url": "https://static.crates.io/crates/futures-executor/futures-executor-0.3.32.crate",
+ "sha256": "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d",
+ "dest": "cargo/vendor/futures-executor-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79\", \"files\": {}}",
- "dest": "cargo/vendor/serde_derive-1.0.228",
+ "contents": "{\"package\": \"baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-executor-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/serde_json/serde_json-1.0.149.crate",
- "sha256": "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86",
- "dest": "cargo/vendor/serde_json-1.0.149"
+ "url": "https://static.crates.io/crates/futures-io/futures-io-0.3.32.crate",
+ "sha256": "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718",
+ "dest": "cargo/vendor/futures-io-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86\", \"files\": {}}",
- "dest": "cargo/vendor/serde_json-1.0.149",
+ "contents": "{\"package\": \"cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-io-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/serde_spanned/serde_spanned-1.0.4.crate",
- "sha256": "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776",
- "dest": "cargo/vendor/serde_spanned-1.0.4"
+ "url": "https://static.crates.io/crates/futures-macro/futures-macro-0.3.32.crate",
+ "sha256": "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b",
+ "dest": "cargo/vendor/futures-macro-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776\", \"files\": {}}",
- "dest": "cargo/vendor/serde_spanned-1.0.4",
+ "contents": "{\"package\": \"e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-macro-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/shlex/shlex-1.3.0.crate",
- "sha256": "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64",
- "dest": "cargo/vendor/shlex-1.3.0"
+ "url": "https://static.crates.io/crates/futures-sink/futures-sink-0.3.32.crate",
+ "sha256": "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893",
+ "dest": "cargo/vendor/futures-sink-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64\", \"files\": {}}",
- "dest": "cargo/vendor/shlex-1.3.0",
+ "contents": "{\"package\": \"c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-sink-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/simd-adler32/simd-adler32-0.3.8.crate",
- "sha256": "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2",
- "dest": "cargo/vendor/simd-adler32-0.3.8"
+ "url": "https://static.crates.io/crates/futures-task/futures-task-0.3.32.crate",
+ "sha256": "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393",
+ "dest": "cargo/vendor/futures-task-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2\", \"files\": {}}",
- "dest": "cargo/vendor/simd-adler32-0.3.8",
+ "contents": "{\"package\": \"037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-task-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/siphasher/siphasher-0.3.11.crate",
- "sha256": "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d",
- "dest": "cargo/vendor/siphasher-0.3.11"
+ "url": "https://static.crates.io/crates/futures-util/futures-util-0.3.32.crate",
+ "sha256": "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6",
+ "dest": "cargo/vendor/futures-util-0.3.32"
},
{
"type": "inline",
- "contents": "{\"package\": \"38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d\", \"files\": {}}",
- "dest": "cargo/vendor/siphasher-0.3.11",
+ "contents": "{\"package\": \"389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6\", \"files\": {}}",
+ "dest": "cargo/vendor/futures-util-0.3.32",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/smawk/smawk-0.3.2.crate",
- "sha256": "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c",
- "dest": "cargo/vendor/smawk-0.3.2"
+ "url": "https://static.crates.io/crates/getrandom/getrandom-0.2.17.crate",
+ "sha256": "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0",
+ "dest": "cargo/vendor/getrandom-0.2.17"
},
{
"type": "inline",
- "contents": "{\"package\": \"b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c\", \"files\": {}}",
- "dest": "cargo/vendor/smawk-0.3.2",
+ "contents": "{\"package\": \"ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0\", \"files\": {}}",
+ "dest": "cargo/vendor/getrandom-0.2.17",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/static_assertions/static_assertions-1.1.0.crate",
- "sha256": "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f",
- "dest": "cargo/vendor/static_assertions-1.1.0"
+ "url": "https://static.crates.io/crates/heck/heck-0.5.0.crate",
+ "sha256": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea",
+ "dest": "cargo/vendor/heck-0.5.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f\", \"files\": {}}",
- "dest": "cargo/vendor/static_assertions-1.1.0",
+ "contents": "{\"package\": \"2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea\", \"files\": {}}",
+ "dest": "cargo/vendor/heck-0.5.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/strsim/strsim-0.11.1.crate",
- "sha256": "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f",
- "dest": "cargo/vendor/strsim-0.11.1"
+ "url": "https://static.crates.io/crates/hex/hex-0.4.3.crate",
+ "sha256": "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70",
+ "dest": "cargo/vendor/hex-0.4.3"
},
{
"type": "inline",
- "contents": "{\"package\": \"7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f\", \"files\": {}}",
- "dest": "cargo/vendor/strsim-0.11.1",
+ "contents": "{\"package\": \"7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70\", \"files\": {}}",
+ "dest": "cargo/vendor/hex-0.4.3",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/syn/syn-2.0.111.crate",
- "sha256": "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87",
- "dest": "cargo/vendor/syn-2.0.111"
+ "url": "https://static.crates.io/crates/is_terminal_polyfill/is_terminal_polyfill-1.70.2.crate",
+ "sha256": "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695",
+ "dest": "cargo/vendor/is_terminal_polyfill-1.70.2"
},
{
"type": "inline",
- "contents": "{\"package\": \"390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87\", \"files\": {}}",
- "dest": "cargo/vendor/syn-2.0.111",
+ "contents": "{\"package\": \"a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695\", \"files\": {}}",
+ "dest": "cargo/vendor/is_terminal_polyfill-1.70.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/synstructure/synstructure-0.13.2.crate",
- "sha256": "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2",
- "dest": "cargo/vendor/synstructure-0.13.2"
+ "url": "https://static.crates.io/crates/itoa/itoa-1.0.18.crate",
+ "sha256": "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682",
+ "dest": "cargo/vendor/itoa-1.0.18"
},
{
"type": "inline",
- "contents": "{\"package\": \"728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2\", \"files\": {}}",
- "dest": "cargo/vendor/synstructure-0.13.2",
+ "contents": "{\"package\": \"8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682\", \"files\": {}}",
+ "dest": "cargo/vendor/itoa-1.0.18",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/tempfile/tempfile-3.23.0.crate",
- "sha256": "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16",
- "dest": "cargo/vendor/tempfile-3.23.0"
+ "url": "https://static.crates.io/crates/lazy_static/lazy_static-1.5.0.crate",
+ "sha256": "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe",
+ "dest": "cargo/vendor/lazy_static-1.5.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16\", \"files\": {}}",
- "dest": "cargo/vendor/tempfile-3.23.0",
+ "contents": "{\"package\": \"bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe\", \"files\": {}}",
+ "dest": "cargo/vendor/lazy_static-1.5.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/textwrap/textwrap-0.16.2.crate",
- "sha256": "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057",
- "dest": "cargo/vendor/textwrap-0.16.2"
+ "url": "https://static.crates.io/crates/libc/libc-0.2.186.crate",
+ "sha256": "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66",
+ "dest": "cargo/vendor/libc-0.2.186"
},
{
"type": "inline",
- "contents": "{\"package\": \"c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057\", \"files\": {}}",
- "dest": "cargo/vendor/textwrap-0.16.2",
+ "contents": "{\"package\": \"68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66\", \"files\": {}}",
+ "dest": "cargo/vendor/libc-0.2.186",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/thiserror/thiserror-2.0.18.crate",
- "sha256": "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4",
- "dest": "cargo/vendor/thiserror-2.0.18"
+ "url": "https://static.crates.io/crates/log/log-0.4.30.crate",
+ "sha256": "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5",
+ "dest": "cargo/vendor/log-0.4.30"
},
{
"type": "inline",
- "contents": "{\"package\": \"4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4\", \"files\": {}}",
- "dest": "cargo/vendor/thiserror-2.0.18",
+ "contents": "{\"package\": \"616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5\", \"files\": {}}",
+ "dest": "cargo/vendor/log-0.4.30",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/thiserror-impl/thiserror-impl-2.0.18.crate",
- "sha256": "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5",
- "dest": "cargo/vendor/thiserror-impl-2.0.18"
+ "url": "https://static.crates.io/crates/memchr/memchr-2.8.1.crate",
+ "sha256": "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8",
+ "dest": "cargo/vendor/memchr-2.8.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5\", \"files\": {}}",
- "dest": "cargo/vendor/thiserror-impl-2.0.18",
+ "contents": "{\"package\": \"6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8\", \"files\": {}}",
+ "dest": "cargo/vendor/memchr-2.8.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/time/time-0.3.44.crate",
- "sha256": "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d",
- "dest": "cargo/vendor/time-0.3.44"
+ "url": "https://static.crates.io/crates/memoffset/memoffset-0.9.1.crate",
+ "sha256": "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a",
+ "dest": "cargo/vendor/memoffset-0.9.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d\", \"files\": {}}",
- "dest": "cargo/vendor/time-0.3.44",
+ "contents": "{\"package\": \"488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a\", \"files\": {}}",
+ "dest": "cargo/vendor/memoffset-0.9.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/time-core/time-core-0.1.6.crate",
- "sha256": "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b",
- "dest": "cargo/vendor/time-core-0.1.6"
+ "url": "https://static.crates.io/crates/minimal-lexical/minimal-lexical-0.2.1.crate",
+ "sha256": "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a",
+ "dest": "cargo/vendor/minimal-lexical-0.2.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b\", \"files\": {}}",
- "dest": "cargo/vendor/time-core-0.1.6",
+ "contents": "{\"package\": \"68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a\", \"files\": {}}",
+ "dest": "cargo/vendor/minimal-lexical-0.2.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/time-macros/time-macros-0.2.24.crate",
- "sha256": "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3",
- "dest": "cargo/vendor/time-macros-0.2.24"
+ "url": "https://static.crates.io/crates/mio/mio-1.2.1.crate",
+ "sha256": "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda",
+ "dest": "cargo/vendor/mio-1.2.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3\", \"files\": {}}",
- "dest": "cargo/vendor/time-macros-0.2.24",
+ "contents": "{\"package\": \"02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda\", \"files\": {}}",
+ "dest": "cargo/vendor/mio-1.2.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/tinyvec/tinyvec-1.10.0.crate",
- "sha256": "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa",
- "dest": "cargo/vendor/tinyvec-1.10.0"
+ "url": "https://static.crates.io/crates/nix/nix-0.31.3.crate",
+ "sha256": "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d",
+ "dest": "cargo/vendor/nix-0.31.3"
},
{
"type": "inline",
- "contents": "{\"package\": \"bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa\", \"files\": {}}",
- "dest": "cargo/vendor/tinyvec-1.10.0",
+ "contents": "{\"package\": \"cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d\", \"files\": {}}",
+ "dest": "cargo/vendor/nix-0.31.3",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/tinyvec_macros/tinyvec_macros-0.1.1.crate",
- "sha256": "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20",
- "dest": "cargo/vendor/tinyvec_macros-0.1.1"
+ "url": "https://static.crates.io/crates/nom/nom-7.1.3.crate",
+ "sha256": "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a",
+ "dest": "cargo/vendor/nom-7.1.3"
},
{
"type": "inline",
- "contents": "{\"package\": \"1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20\", \"files\": {}}",
- "dest": "cargo/vendor/tinyvec_macros-0.1.1",
+ "contents": "{\"package\": \"d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a\", \"files\": {}}",
+ "dest": "cargo/vendor/nom-7.1.3",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/toml/toml-0.5.11.crate",
- "sha256": "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234",
- "dest": "cargo/vendor/toml-0.5.11"
+ "url": "https://static.crates.io/crates/num-bigint/num-bigint-0.4.6.crate",
+ "sha256": "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9",
+ "dest": "cargo/vendor/num-bigint-0.4.6"
},
{
"type": "inline",
- "contents": "{\"package\": \"f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234\", \"files\": {}}",
- "dest": "cargo/vendor/toml-0.5.11",
+ "contents": "{\"package\": \"a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9\", \"files\": {}}",
+ "dest": "cargo/vendor/num-bigint-0.4.6",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/toml/toml-0.9.9+spec-1.0.0.crate",
- "sha256": "eb5238e643fc34a1d5d7e753e1532a91912d74b63b92b3ea51fde8d1b7bc79dd",
- "dest": "cargo/vendor/toml-0.9.9+spec-1.0.0"
+ "url": "https://static.crates.io/crates/num-conv/num-conv-0.2.2.crate",
+ "sha256": "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441",
+ "dest": "cargo/vendor/num-conv-0.2.2"
},
{
"type": "inline",
- "contents": "{\"package\": \"eb5238e643fc34a1d5d7e753e1532a91912d74b63b92b3ea51fde8d1b7bc79dd\", \"files\": {}}",
- "dest": "cargo/vendor/toml-0.9.9+spec-1.0.0",
+ "contents": "{\"package\": \"521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441\", \"files\": {}}",
+ "dest": "cargo/vendor/num-conv-0.2.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/toml_datetime/toml_datetime-0.7.4+spec-1.0.0.crate",
- "sha256": "fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6",
- "dest": "cargo/vendor/toml_datetime-0.7.4+spec-1.0.0"
+ "url": "https://static.crates.io/crates/num-integer/num-integer-0.1.46.crate",
+ "sha256": "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f",
+ "dest": "cargo/vendor/num-integer-0.1.46"
},
{
"type": "inline",
- "contents": "{\"package\": \"fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6\", \"files\": {}}",
- "dest": "cargo/vendor/toml_datetime-0.7.4+spec-1.0.0",
+ "contents": "{\"package\": \"7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f\", \"files\": {}}",
+ "dest": "cargo/vendor/num-integer-0.1.46",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/toml_parser/toml_parser-1.0.5+spec-1.0.0.crate",
- "sha256": "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c",
- "dest": "cargo/vendor/toml_parser-1.0.5+spec-1.0.0"
+ "url": "https://static.crates.io/crates/num-traits/num-traits-0.2.19.crate",
+ "sha256": "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841",
+ "dest": "cargo/vendor/num-traits-0.2.19"
},
{
"type": "inline",
- "contents": "{\"package\": \"4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c\", \"files\": {}}",
- "dest": "cargo/vendor/toml_parser-1.0.5+spec-1.0.0",
+ "contents": "{\"package\": \"071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841\", \"files\": {}}",
+ "dest": "cargo/vendor/num-traits-0.2.19",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/toml_writer/toml_writer-1.0.5+spec-1.0.0.crate",
- "sha256": "a9cd6190959dce0994aa8970cd32ab116d1851ead27e866039acaf2524ce44fa",
- "dest": "cargo/vendor/toml_writer-1.0.5+spec-1.0.0"
+ "url": "https://static.crates.io/crates/oid-registry/oid-registry-0.8.1.crate",
+ "sha256": "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7",
+ "dest": "cargo/vendor/oid-registry-0.8.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"a9cd6190959dce0994aa8970cd32ab116d1851ead27e866039acaf2524ce44fa\", \"files\": {}}",
- "dest": "cargo/vendor/toml_writer-1.0.5+spec-1.0.0",
+ "contents": "{\"package\": \"12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7\", \"files\": {}}",
+ "dest": "cargo/vendor/oid-registry-0.8.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/unicode-bidi/unicode-bidi-0.3.18.crate",
- "sha256": "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5",
- "dest": "cargo/vendor/unicode-bidi-0.3.18"
+ "url": "https://static.crates.io/crates/once_cell/once_cell-1.21.4.crate",
+ "sha256": "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50",
+ "dest": "cargo/vendor/once_cell-1.21.4"
},
{
"type": "inline",
- "contents": "{\"package\": \"5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5\", \"files\": {}}",
- "dest": "cargo/vendor/unicode-bidi-0.3.18",
+ "contents": "{\"package\": \"9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50\", \"files\": {}}",
+ "dest": "cargo/vendor/once_cell-1.21.4",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/unicode-ident/unicode-ident-1.0.22.crate",
- "sha256": "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5",
- "dest": "cargo/vendor/unicode-ident-1.0.22"
+ "url": "https://static.crates.io/crates/once_cell_polyfill/once_cell_polyfill-1.70.2.crate",
+ "sha256": "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe",
+ "dest": "cargo/vendor/once_cell_polyfill-1.70.2"
},
{
"type": "inline",
- "contents": "{\"package\": \"9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5\", \"files\": {}}",
- "dest": "cargo/vendor/unicode-ident-1.0.22",
+ "contents": "{\"package\": \"384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe\", \"files\": {}}",
+ "dest": "cargo/vendor/once_cell_polyfill-1.70.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/unicode-normalization/unicode-normalization-0.1.25.crate",
- "sha256": "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8",
- "dest": "cargo/vendor/unicode-normalization-0.1.25"
+ "url": "https://static.crates.io/crates/pin-project-lite/pin-project-lite-0.2.17.crate",
+ "sha256": "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd",
+ "dest": "cargo/vendor/pin-project-lite-0.2.17"
},
{
"type": "inline",
- "contents": "{\"package\": \"5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8\", \"files\": {}}",
- "dest": "cargo/vendor/unicode-normalization-0.1.25",
+ "contents": "{\"package\": \"a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd\", \"files\": {}}",
+ "dest": "cargo/vendor/pin-project-lite-0.2.17",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi/uniffi-0.29.5.crate",
- "sha256": "3291800a6b06569f7d3e15bdb6dc235e0f0c8bd3eb07177f430057feb076415f",
- "dest": "cargo/vendor/uniffi-0.29.5"
+ "url": "https://static.crates.io/crates/powerfmt/powerfmt-0.2.0.crate",
+ "sha256": "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391",
+ "dest": "cargo/vendor/powerfmt-0.2.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"3291800a6b06569f7d3e15bdb6dc235e0f0c8bd3eb07177f430057feb076415f\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi-0.29.5",
+ "contents": "{\"package\": \"439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391\", \"files\": {}}",
+ "dest": "cargo/vendor/powerfmt-0.2.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi_bindgen/uniffi_bindgen-0.29.5.crate",
- "sha256": "a04b99fa7796eaaa7b87976a0dbdd1178dc1ee702ea00aca2642003aef9b669e",
- "dest": "cargo/vendor/uniffi_bindgen-0.29.5"
+ "url": "https://static.crates.io/crates/ppv-lite86/ppv-lite86-0.2.21.crate",
+ "sha256": "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9",
+ "dest": "cargo/vendor/ppv-lite86-0.2.21"
},
{
"type": "inline",
- "contents": "{\"package\": \"a04b99fa7796eaaa7b87976a0dbdd1178dc1ee702ea00aca2642003aef9b669e\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi_bindgen-0.29.5",
+ "contents": "{\"package\": \"85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9\", \"files\": {}}",
+ "dest": "cargo/vendor/ppv-lite86-0.2.21",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi_build/uniffi_build-0.29.5.crate",
- "sha256": "025a05cba02ee22b6624ac3d257e59c7395319ea8fe1aae33a7cdb4e2a3016cc",
- "dest": "cargo/vendor/uniffi_build-0.29.5"
+ "url": "https://static.crates.io/crates/proc-macro2/proc-macro2-1.0.106.crate",
+ "sha256": "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934",
+ "dest": "cargo/vendor/proc-macro2-1.0.106"
},
{
"type": "inline",
- "contents": "{\"package\": \"025a05cba02ee22b6624ac3d257e59c7395319ea8fe1aae33a7cdb4e2a3016cc\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi_build-0.29.5",
+ "contents": "{\"package\": \"8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934\", \"files\": {}}",
+ "dest": "cargo/vendor/proc-macro2-1.0.106",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi_core/uniffi_core-0.29.5.crate",
- "sha256": "f38a9a27529ccff732f8efddb831b65b1e07f7dea3fd4cacd4a35a8c4b253b98",
- "dest": "cargo/vendor/uniffi_core-0.29.5"
+ "url": "https://static.crates.io/crates/quote/quote-1.0.45.crate",
+ "sha256": "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924",
+ "dest": "cargo/vendor/quote-1.0.45"
},
{
"type": "inline",
- "contents": "{\"package\": \"f38a9a27529ccff732f8efddb831b65b1e07f7dea3fd4cacd4a35a8c4b253b98\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi_core-0.29.5",
+ "contents": "{\"package\": \"41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924\", \"files\": {}}",
+ "dest": "cargo/vendor/quote-1.0.45",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi_internal_macros/uniffi_internal_macros-0.29.5.crate",
- "sha256": "09acd2ce09c777dd65ee97c251d33c8a972afc04873f1e3b21eb3492ade16933",
- "dest": "cargo/vendor/uniffi_internal_macros-0.29.5"
+ "url": "https://static.crates.io/crates/rand/rand-0.8.6.crate",
+ "sha256": "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a",
+ "dest": "cargo/vendor/rand-0.8.6"
},
{
"type": "inline",
- "contents": "{\"package\": \"09acd2ce09c777dd65ee97c251d33c8a972afc04873f1e3b21eb3492ade16933\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi_internal_macros-0.29.5",
+ "contents": "{\"package\": \"5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a\", \"files\": {}}",
+ "dest": "cargo/vendor/rand-0.8.6",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi_macros/uniffi_macros-0.29.5.crate",
- "sha256": "5596f178c4f7aafa1a501c4e0b96236a96bc2ef92bdb453d83e609dad0040152",
- "dest": "cargo/vendor/uniffi_macros-0.29.5"
+ "url": "https://static.crates.io/crates/rand_chacha/rand_chacha-0.3.1.crate",
+ "sha256": "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88",
+ "dest": "cargo/vendor/rand_chacha-0.3.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"5596f178c4f7aafa1a501c4e0b96236a96bc2ef92bdb453d83e609dad0040152\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi_macros-0.29.5",
+ "contents": "{\"package\": \"e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88\", \"files\": {}}",
+ "dest": "cargo/vendor/rand_chacha-0.3.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi_meta/uniffi_meta-0.29.5.crate",
- "sha256": "beadc1f460eb2e209263c49c4f5b19e9a02e00a3b2b393f78ad10d766346ecff",
- "dest": "cargo/vendor/uniffi_meta-0.29.5"
+ "url": "https://static.crates.io/crates/rand_core/rand_core-0.6.4.crate",
+ "sha256": "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c",
+ "dest": "cargo/vendor/rand_core-0.6.4"
},
{
"type": "inline",
- "contents": "{\"package\": \"beadc1f460eb2e209263c49c4f5b19e9a02e00a3b2b393f78ad10d766346ecff\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi_meta-0.29.5",
+ "contents": "{\"package\": \"ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c\", \"files\": {}}",
+ "dest": "cargo/vendor/rand_core-0.6.4",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi_pipeline/uniffi_pipeline-0.29.5.crate",
- "sha256": "dd76b3ac8a2d964ca9fce7df21c755afb4c77b054a85ad7a029ad179cc5abb8a",
- "dest": "cargo/vendor/uniffi_pipeline-0.29.5"
+ "url": "https://static.crates.io/crates/ring/ring-0.17.14.crate",
+ "sha256": "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7",
+ "dest": "cargo/vendor/ring-0.17.14"
},
{
"type": "inline",
- "contents": "{\"package\": \"dd76b3ac8a2d964ca9fce7df21c755afb4c77b054a85ad7a029ad179cc5abb8a\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi_pipeline-0.29.5",
+ "contents": "{\"package\": \"a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7\", \"files\": {}}",
+ "dest": "cargo/vendor/ring-0.17.14",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uniffi_udl/uniffi_udl-0.29.5.crate",
- "sha256": "4319cf905911d70d5b97ce0f46f101619a22e9a189c8c46d797a9955e9233716",
- "dest": "cargo/vendor/uniffi_udl-0.29.5"
+ "url": "https://static.crates.io/crates/rusticata-macros/rusticata-macros-4.1.0.crate",
+ "sha256": "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632",
+ "dest": "cargo/vendor/rusticata-macros-4.1.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"4319cf905911d70d5b97ce0f46f101619a22e9a189c8c46d797a9955e9233716\", \"files\": {}}",
- "dest": "cargo/vendor/uniffi_udl-0.29.5",
+ "contents": "{\"package\": \"faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632\", \"files\": {}}",
+ "dest": "cargo/vendor/rusticata-macros-4.1.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/untrusted/untrusted-0.9.0.crate",
- "sha256": "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1",
- "dest": "cargo/vendor/untrusted-0.9.0"
+ "url": "https://static.crates.io/crates/serde_core/serde_core-1.0.228.crate",
+ "sha256": "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad",
+ "dest": "cargo/vendor/serde_core-1.0.228"
},
{
"type": "inline",
- "contents": "{\"package\": \"8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1\", \"files\": {}}",
- "dest": "cargo/vendor/untrusted-0.9.0",
+ "contents": "{\"package\": \"41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad\", \"files\": {}}",
+ "dest": "cargo/vendor/serde_core-1.0.228",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/url/url-2.4.1.crate",
- "sha256": "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5",
- "dest": "cargo/vendor/url-2.4.1"
+ "url": "https://static.crates.io/crates/serde_derive/serde_derive-1.0.228.crate",
+ "sha256": "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79",
+ "dest": "cargo/vendor/serde_derive-1.0.228"
},
{
"type": "inline",
- "contents": "{\"package\": \"143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5\", \"files\": {}}",
- "dest": "cargo/vendor/url-2.4.1",
+ "contents": "{\"package\": \"d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79\", \"files\": {}}",
+ "dest": "cargo/vendor/serde_derive-1.0.228",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/utf8parse/utf8parse-0.2.2.crate",
- "sha256": "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821",
- "dest": "cargo/vendor/utf8parse-0.2.2"
+ "url": "https://static.crates.io/crates/shlex/shlex-2.0.1.crate",
+ "sha256": "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba",
+ "dest": "cargo/vendor/shlex-2.0.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821\", \"files\": {}}",
- "dest": "cargo/vendor/utf8parse-0.2.2",
+ "contents": "{\"package\": \"f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba\", \"files\": {}}",
+ "dest": "cargo/vendor/shlex-2.0.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/uuid/uuid-1.19.0.crate",
- "sha256": "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a",
- "dest": "cargo/vendor/uuid-1.19.0"
+ "url": "https://static.crates.io/crates/slab/slab-0.4.12.crate",
+ "sha256": "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5",
+ "dest": "cargo/vendor/slab-0.4.12"
},
{
"type": "inline",
- "contents": "{\"package\": \"e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a\", \"files\": {}}",
- "dest": "cargo/vendor/uuid-1.19.0",
+ "contents": "{\"package\": \"0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5\", \"files\": {}}",
+ "dest": "cargo/vendor/slab-0.4.12",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/wasi/wasi-0.11.1+wasi-snapshot-preview1.crate",
- "sha256": "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b",
- "dest": "cargo/vendor/wasi-0.11.1+wasi-snapshot-preview1"
+ "url": "https://static.crates.io/crates/socket2/socket2-0.6.4.crate",
+ "sha256": "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51",
+ "dest": "cargo/vendor/socket2-0.6.4"
},
{
"type": "inline",
- "contents": "{\"package\": \"ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b\", \"files\": {}}",
- "dest": "cargo/vendor/wasi-0.11.1+wasi-snapshot-preview1",
+ "contents": "{\"package\": \"52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51\", \"files\": {}}",
+ "dest": "cargo/vendor/socket2-0.6.4",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/wasip2/wasip2-1.0.1+wasi-0.2.4.crate",
- "sha256": "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7",
- "dest": "cargo/vendor/wasip2-1.0.1+wasi-0.2.4"
+ "url": "https://static.crates.io/crates/strsim/strsim-0.11.1.crate",
+ "sha256": "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f",
+ "dest": "cargo/vendor/strsim-0.11.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7\", \"files\": {}}",
- "dest": "cargo/vendor/wasip2-1.0.1+wasi-0.2.4",
+ "contents": "{\"package\": \"7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f\", \"files\": {}}",
+ "dest": "cargo/vendor/strsim-0.11.1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/wasm-bindgen/wasm-bindgen-0.2.106.crate",
- "sha256": "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd",
- "dest": "cargo/vendor/wasm-bindgen-0.2.106"
+ "url": "https://static.crates.io/crates/syn/syn-2.0.117.crate",
+ "sha256": "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99",
+ "dest": "cargo/vendor/syn-2.0.117"
},
{
"type": "inline",
- "contents": "{\"package\": \"0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd\", \"files\": {}}",
- "dest": "cargo/vendor/wasm-bindgen-0.2.106",
+ "contents": "{\"package\": \"e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99\", \"files\": {}}",
+ "dest": "cargo/vendor/syn-2.0.117",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/wasm-bindgen-macro/wasm-bindgen-macro-0.2.106.crate",
- "sha256": "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3",
- "dest": "cargo/vendor/wasm-bindgen-macro-0.2.106"
+ "url": "https://static.crates.io/crates/synstructure/synstructure-0.13.2.crate",
+ "sha256": "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2",
+ "dest": "cargo/vendor/synstructure-0.13.2"
},
{
"type": "inline",
- "contents": "{\"package\": \"48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3\", \"files\": {}}",
- "dest": "cargo/vendor/wasm-bindgen-macro-0.2.106",
+ "contents": "{\"package\": \"728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2\", \"files\": {}}",
+ "dest": "cargo/vendor/synstructure-0.13.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/wasm-bindgen-macro-support/wasm-bindgen-macro-support-0.2.106.crate",
- "sha256": "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40",
- "dest": "cargo/vendor/wasm-bindgen-macro-support-0.2.106"
+ "url": "https://static.crates.io/crates/thiserror/thiserror-2.0.18.crate",
+ "sha256": "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4",
+ "dest": "cargo/vendor/thiserror-2.0.18"
},
{
"type": "inline",
- "contents": "{\"package\": \"cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40\", \"files\": {}}",
- "dest": "cargo/vendor/wasm-bindgen-macro-support-0.2.106",
+ "contents": "{\"package\": \"4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4\", \"files\": {}}",
+ "dest": "cargo/vendor/thiserror-2.0.18",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/wasm-bindgen-shared/wasm-bindgen-shared-0.2.106.crate",
- "sha256": "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4",
- "dest": "cargo/vendor/wasm-bindgen-shared-0.2.106"
+ "url": "https://static.crates.io/crates/thiserror-impl/thiserror-impl-2.0.18.crate",
+ "sha256": "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5",
+ "dest": "cargo/vendor/thiserror-impl-2.0.18"
},
{
"type": "inline",
- "contents": "{\"package\": \"cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4\", \"files\": {}}",
- "dest": "cargo/vendor/wasm-bindgen-shared-0.2.106",
+ "contents": "{\"package\": \"ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5\", \"files\": {}}",
+ "dest": "cargo/vendor/thiserror-impl-2.0.18",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/weedle2/weedle2-5.0.0.crate",
- "sha256": "998d2c24ec099a87daf9467808859f9d82b61f1d9c9701251aea037f514eae0e",
- "dest": "cargo/vendor/weedle2-5.0.0"
+ "url": "https://static.crates.io/crates/time/time-0.3.47.crate",
+ "sha256": "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c",
+ "dest": "cargo/vendor/time-0.3.47"
},
{
"type": "inline",
- "contents": "{\"package\": \"998d2c24ec099a87daf9467808859f9d82b61f1d9c9701251aea037f514eae0e\", \"files\": {}}",
- "dest": "cargo/vendor/weedle2-5.0.0",
+ "contents": "{\"package\": \"743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c\", \"files\": {}}",
+ "dest": "cargo/vendor/time-0.3.47",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/whatsys/whatsys-0.3.2.crate",
- "sha256": "192bcd2925a9791ba474bc673938f8c59b8978b3f304ef2c50672156bacf943b",
- "dest": "cargo/vendor/whatsys-0.3.2"
+ "url": "https://static.crates.io/crates/time-core/time-core-0.1.8.crate",
+ "sha256": "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca",
+ "dest": "cargo/vendor/time-core-0.1.8"
},
{
"type": "inline",
- "contents": "{\"package\": \"192bcd2925a9791ba474bc673938f8c59b8978b3f304ef2c50672156bacf943b\", \"files\": {}}",
- "dest": "cargo/vendor/whatsys-0.3.2",
+ "contents": "{\"package\": \"7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca\", \"files\": {}}",
+ "dest": "cargo/vendor/time-core-0.1.8",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/winapi/winapi-0.3.9.crate",
- "sha256": "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419",
- "dest": "cargo/vendor/winapi-0.3.9"
+ "url": "https://static.crates.io/crates/time-macros/time-macros-0.2.27.crate",
+ "sha256": "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215",
+ "dest": "cargo/vendor/time-macros-0.2.27"
},
{
"type": "inline",
- "contents": "{\"package\": \"5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419\", \"files\": {}}",
- "dest": "cargo/vendor/winapi-0.3.9",
+ "contents": "{\"package\": \"2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215\", \"files\": {}}",
+ "dest": "cargo/vendor/time-macros-0.2.27",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/winapi-i686-pc-windows-gnu-0.4.0.crate",
- "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6",
- "dest": "cargo/vendor/winapi-i686-pc-windows-gnu-0.4.0"
+ "url": "https://static.crates.io/crates/tokio/tokio-1.52.3.crate",
+ "sha256": "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe",
+ "dest": "cargo/vendor/tokio-1.52.3"
},
{
"type": "inline",
- "contents": "{\"package\": \"ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6\", \"files\": {}}",
- "dest": "cargo/vendor/winapi-i686-pc-windows-gnu-0.4.0",
+ "contents": "{\"package\": \"8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe\", \"files\": {}}",
+ "dest": "cargo/vendor/tokio-1.52.3",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/winapi-x86_64-pc-windows-gnu-0.4.0.crate",
- "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f",
- "dest": "cargo/vendor/winapi-x86_64-pc-windows-gnu-0.4.0"
+ "url": "https://static.crates.io/crates/tokio-macros/tokio-macros-2.7.0.crate",
+ "sha256": "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496",
+ "dest": "cargo/vendor/tokio-macros-2.7.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f\", \"files\": {}}",
- "dest": "cargo/vendor/winapi-x86_64-pc-windows-gnu-0.4.0",
+ "contents": "{\"package\": \"385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496\", \"files\": {}}",
+ "dest": "cargo/vendor/tokio-macros-2.7.0",
"dest-filename": ".cargo-checksum.json"
},
{
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/windows-core/windows-core-0.62.2.crate",
- "sha256": "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb",
- "dest": "cargo/vendor/windows-core-0.62.2"
+ "type": "shell",
+ "commands": [
+ "cp -r --reflink=auto \"flatpak-cargo/git/udp-over-tcp-5c6d8f4/.\" \"cargo/vendor/udp-over-tcp\""
+ ]
},
{
"type": "inline",
- "contents": "{\"package\": \"b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb\", \"files\": {}}",
- "dest": "cargo/vendor/windows-core-0.62.2",
+ "contents": "[package]\nname = \"udp-over-tcp\"\nversion = \"0.4.0\"\nauthors = [\"Mullvad VPN\"]\nlicense = \"MIT OR Apache-2.0\"\ndescription = \"Tunnel UDP traffic inside a TCP stream. Each datagram is prefixed with a 16 bit unsigned integer containing the length\"\nrepository = \"https://github.com/mullvad/udp-over-tcp\"\nedition = \"2024\"\nrust-version = \"1.87.0\"\npublish = false\n\n[[bin]]\nname = \"tcp2udp\"\nrequired-features = [\"clap\"]\n\n[[bin]]\nname = \"udp2tcp\"\nrequired-features = [\"clap\"]\n\n[profile.release]\nopt-level = 3\nlto = true\ncodegen-units = 1\n\n[features]\nstatsd = [\"cadence\"]\n\n[dependencies]\nerr-context = \"0.1.0\"\nlog = \"0.4.11\"\nfutures = \"0.3.31\"\n\n[dependencies.tokio]\nversion = \"1.0\"\nfeatures = [\"rt-multi-thread\", \"macros\", \"net\", \"time\", \"io-util\"]\n\n[dependencies.clap]\nversion = \"4.0\"\nfeatures = [\"derive\"]\noptional = true\n\n[dependencies.env_logger]\nversion = \"0.11.3\"\noptional = true\n\n[dependencies.cadence]\nversion = \"1.0.0\"\noptional = true\n\n[target.\"cfg(target_os = \\\"linux\\\")\".dependencies.nix]\nversion = \"0.31.2\"\nfeatures = [\"socket\"]\n",
+ "dest": "cargo/vendor/udp-over-tcp",
+ "dest-filename": "Cargo.toml"
+ },
+ {
+ "type": "inline",
+ "contents": "{\"package\": null, \"files\": {}}",
+ "dest": "cargo/vendor/udp-over-tcp",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/windows-implement/windows-implement-0.60.2.crate",
- "sha256": "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf",
- "dest": "cargo/vendor/windows-implement-0.60.2"
+ "url": "https://static.crates.io/crates/unicode-ident/unicode-ident-1.0.24.crate",
+ "sha256": "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75",
+ "dest": "cargo/vendor/unicode-ident-1.0.24"
},
{
"type": "inline",
- "contents": "{\"package\": \"053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf\", \"files\": {}}",
- "dest": "cargo/vendor/windows-implement-0.60.2",
+ "contents": "{\"package\": \"e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75\", \"files\": {}}",
+ "dest": "cargo/vendor/unicode-ident-1.0.24",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/windows-interface/windows-interface-0.59.3.crate",
- "sha256": "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358",
- "dest": "cargo/vendor/windows-interface-0.59.3"
+ "url": "https://static.crates.io/crates/untrusted/untrusted-0.9.0.crate",
+ "sha256": "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1",
+ "dest": "cargo/vendor/untrusted-0.9.0"
},
{
"type": "inline",
- "contents": "{\"package\": \"3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358\", \"files\": {}}",
- "dest": "cargo/vendor/windows-interface-0.59.3",
+ "contents": "{\"package\": \"8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1\", \"files\": {}}",
+ "dest": "cargo/vendor/untrusted-0.9.0",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/windows-link/windows-link-0.2.1.crate",
- "sha256": "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5",
- "dest": "cargo/vendor/windows-link-0.2.1"
+ "url": "https://static.crates.io/crates/utf8parse/utf8parse-0.2.2.crate",
+ "sha256": "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821",
+ "dest": "cargo/vendor/utf8parse-0.2.2"
},
{
"type": "inline",
- "contents": "{\"package\": \"f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5\", \"files\": {}}",
- "dest": "cargo/vendor/windows-link-0.2.1",
+ "contents": "{\"package\": \"06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821\", \"files\": {}}",
+ "dest": "cargo/vendor/utf8parse-0.2.2",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/windows-result/windows-result-0.4.1.crate",
- "sha256": "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5",
- "dest": "cargo/vendor/windows-result-0.4.1"
+ "url": "https://static.crates.io/crates/wasi/wasi-0.11.1+wasi-snapshot-preview1.crate",
+ "sha256": "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b",
+ "dest": "cargo/vendor/wasi-0.11.1+wasi-snapshot-preview1"
},
{
"type": "inline",
- "contents": "{\"package\": \"7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5\", \"files\": {}}",
- "dest": "cargo/vendor/windows-result-0.4.1",
+ "contents": "{\"package\": \"ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b\", \"files\": {}}",
+ "dest": "cargo/vendor/wasi-0.11.1+wasi-snapshot-preview1",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/windows-strings/windows-strings-0.5.1.crate",
- "sha256": "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091",
- "dest": "cargo/vendor/windows-strings-0.5.1"
+ "url": "https://static.crates.io/crates/windows-link/windows-link-0.2.1.crate",
+ "sha256": "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5",
+ "dest": "cargo/vendor/windows-link-0.2.1"
},
{
"type": "inline",
- "contents": "{\"package\": \"7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091\", \"files\": {}}",
- "dest": "cargo/vendor/windows-strings-0.5.1",
+ "contents": "{\"package\": \"f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5\", \"files\": {}}",
+ "dest": "cargo/vendor/windows-link-0.2.1",
"dest-filename": ".cargo-checksum.json"
},
{
@@ -2378,45 +1297,6 @@
"dest": "cargo/vendor/windows_x86_64_msvc-0.52.6",
"dest-filename": ".cargo-checksum.json"
},
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/winnow/winnow-0.7.14.crate",
- "sha256": "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829",
- "dest": "cargo/vendor/winnow-0.7.14"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829\", \"files\": {}}",
- "dest": "cargo/vendor/winnow-0.7.14",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/wit-bindgen/wit-bindgen-0.46.0.crate",
- "sha256": "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59",
- "dest": "cargo/vendor/wit-bindgen-0.46.0"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59\", \"files\": {}}",
- "dest": "cargo/vendor/wit-bindgen-0.46.0",
- "dest-filename": ".cargo-checksum.json"
- },
- {
- "type": "archive",
- "archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/wr_malloc_size_of/wr_malloc_size_of-0.2.2.crate",
- "sha256": "482308b684f11723b200a32808094bb460b5ac4840903ccbcb78ad92a6354a1f",
- "dest": "cargo/vendor/wr_malloc_size_of-0.2.2"
- },
- {
- "type": "inline",
- "contents": "{\"package\": \"482308b684f11723b200a32808094bb460b5ac4840903ccbcb78ad92a6354a1f\", \"files\": {}}",
- "dest": "cargo/vendor/wr_malloc_size_of-0.2.2",
- "dest-filename": ".cargo-checksum.json"
- },
{
"type": "archive",
"archive-type": "tar-gzip",
@@ -2433,32 +1313,32 @@
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/zeitstempel/zeitstempel-0.2.0.crate",
- "sha256": "f523a0d9326c4f3242ad3a9d306baa7fe4572fd532cc891cabecfb714c786c1e",
- "dest": "cargo/vendor/zeitstempel-0.2.0"
+ "url": "https://static.crates.io/crates/zerocopy/zerocopy-0.8.50.crate",
+ "sha256": "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1",
+ "dest": "cargo/vendor/zerocopy-0.8.50"
},
{
"type": "inline",
- "contents": "{\"package\": \"f523a0d9326c4f3242ad3a9d306baa7fe4572fd532cc891cabecfb714c786c1e\", \"files\": {}}",
- "dest": "cargo/vendor/zeitstempel-0.2.0",
+ "contents": "{\"package\": \"3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1\", \"files\": {}}",
+ "dest": "cargo/vendor/zerocopy-0.8.50",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "archive",
"archive-type": "tar-gzip",
- "url": "https://static.crates.io/crates/zmij/zmij-1.0.19.crate",
- "sha256": "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445",
- "dest": "cargo/vendor/zmij-1.0.19"
+ "url": "https://static.crates.io/crates/zerocopy-derive/zerocopy-derive-0.8.50.crate",
+ "sha256": "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639",
+ "dest": "cargo/vendor/zerocopy-derive-0.8.50"
},
{
"type": "inline",
- "contents": "{\"package\": \"3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445\", \"files\": {}}",
- "dest": "cargo/vendor/zmij-1.0.19",
+ "contents": "{\"package\": \"0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639\", \"files\": {}}",
+ "dest": "cargo/vendor/zerocopy-derive-0.8.50",
"dest-filename": ".cargo-checksum.json"
},
{
"type": "inline",
- "contents": "[source.vendored-sources]\ndirectory = \"cargo/vendor\"\n\n[source.crates-io]\nreplace-with = \"vendored-sources\"\n",
+ "contents": "[source.vendored-sources]\ndirectory = \"cargo/vendor\"\n\n[source.crates-io]\nreplace-with = \"vendored-sources\"\n\n[source.\"https://github.com/mullvad/udp-over-tcp\"]\ngit = \"https://github.com/mullvad/udp-over-tcp\"\nreplace-with = \"vendored-sources\"\nrev = \"5c6d8f44a5aa12ed9bb4ae51dd17e5e22e5ec303\"\n",
"dest": "cargo",
"dest-filename": "config"
}
diff --git a/linux/mozillavpn.spec b/linux/mozillavpn.spec
index 82ae89c05b..1240867446 100644
--- a/linux/mozillavpn.spec
+++ b/linux/mozillavpn.spec
@@ -69,6 +69,7 @@ install %{_srcdir}/LICENSE.md %{buildroot}/%{_licensedir}/%{name}/
%{_unitdir}/mozillavpn.service
%{_unitdir}/socksproxy.service
%{_bindir}/mozillavpn
+%{_bindir}/mozillavpn-obfuscator
%{_bindir}/socksproxy
%{_prefix}/lib/mozilla/native-messaging-hosts/mozillavpn.json
%{_datadir}/applications/org.mozilla.vpn.desktop
diff --git a/obfuscators/Cargo.toml b/obfuscators/Cargo.toml
new file mode 100644
index 0000000000..74bdcf01d8
--- /dev/null
+++ b/obfuscators/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "obfuscators"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "obfuscators"
+path = "src/lib.rs"
+crate-type = ["cdylib", "rlib"]
+
+[[bin]]
+name = "mozillavpn-obfuscator"
+path = "src/main.rs"
+
+[dependencies]
+rand = "0.8"
+log = "0.4"
+base64 = "0.22"
+once_cell = "1.21.4"
+tokio = { version = "1", features = ["rt", "macros", "time", "net"] }
+udp-over-tcp = { git = "https://github.com/mullvad/udp-over-tcp", rev = "5c6d8f44a5aa12ed9bb4ae51dd17e5e22e5ec303" }
+clap = { version = "=4.5.20", features = ["derive"] }
diff --git a/obfuscators/src/factory.rs b/obfuscators/src/factory.rs
new file mode 100644
index 0000000000..352448201a
--- /dev/null
+++ b/obfuscators/src/factory.rs
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::io;
+
+use crate::obfuscator::{Config, Obfuscator, ObfuscationMethod};
+
+use crate::udp_over_tcp::UdpOverTcpObfuscator;
+
+pub fn create_obfuscator(cfg: &Config) -> io::Result> {
+ log::info!("Creating obfuscator for config: {cfg:#?}");
+ match cfg.method {
+ ObfuscationMethod::UdpOverTcp => Ok(Box::new(UdpOverTcpObfuscator::new(cfg)?)),
+
+ // No obfuscation should raise an error as the daemon should skip start the obfuscator in this case
+ m => Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ format!("obfuscation method not implemented: {m:?}"),
+ )),
+ }
+}
diff --git a/obfuscators/src/lib.rs b/obfuscators/src/lib.rs
new file mode 100644
index 0000000000..baed8a0618
--- /dev/null
+++ b/obfuscators/src/lib.rs
@@ -0,0 +1,115 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::os::raw::c_char;
+use std::ptr;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+use std::thread::{self, JoinHandle};
+use logger::Logger;
+
+pub mod factory;
+mod logger;
+#[allow(dead_code)]
+mod obfuscator;
+mod udp_over_tcp;
+
+pub use obfuscator::{Config, ObfuscationMethod, Obfuscator, ObfuscatorConfig};
+
+/// Opaque handle held by the JNA caller.
+/// Owns the runner thread and the shutdown flag dropping it stops the obfuscator.
+pub struct ObfuscatorHandle {
+ shutdown: Arc,
+ thread: Option>,
+ local_port: u16,
+ socket_v4: i32,
+ socket_v6: i32,
+}
+
+impl Drop for ObfuscatorHandle {
+ fn drop(&mut self) {
+ self.shutdown.store(true, Ordering::Release);
+ if let Some(t) = self.thread.take() {
+ let _ = t.join();
+ }
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn obfuscator_start(
+ cfg: *const ObfuscatorConfig,
+) -> *mut ObfuscatorHandle {
+ let Some(cfg) = Config::from_c(cfg) else {
+ return ptr::null_mut();
+ };
+
+ let Ok(mut obf) = factory::create_obfuscator(&cfg) else {
+ return ptr::null_mut();
+ };
+
+ let local_port = obf.local_port();
+ let socket_v4 = obf.socket_v4();
+ let socket_v6 = obf.socket_v6();
+
+ let shutdown = Arc::new(AtomicBool::new(false));
+ let shutdown_run = Arc::clone(&shutdown);
+ let Ok(thread) = thread::Builder::new()
+ .name("obf-runner".into())
+ .spawn(move || obf.run(shutdown_run))
+ else {
+ return ptr::null_mut();
+ };
+
+ Box::into_raw(Box::new(ObfuscatorHandle {
+ shutdown,
+ thread: Some(thread),
+ local_port,
+ socket_v4,
+ socket_v6,
+ }))
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn obfuscator_local_port(
+ handle: *const ObfuscatorHandle,
+) -> u16 {
+ if handle.is_null() {
+ return 0;
+ }
+ (*handle).local_port
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn obfuscator_socket_v4(
+ handle: *const ObfuscatorHandle,
+) -> i32 {
+ if handle.is_null() {
+ return -1;
+ }
+ (*handle).socket_v4
+}
+
+
+#[no_mangle]
+pub unsafe extern "C" fn obfuscator_socket_v6(
+ handle: *const ObfuscatorHandle,
+) -> i32 {
+ if handle.is_null() {
+ return -1;
+ }
+ (*handle).socket_v6
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn obfuscator_stop(handle: *mut ObfuscatorHandle) {
+ if handle.is_null() {
+ return;
+ }
+ drop(Box::from_raw(handle));
+}
+
+#[no_mangle]
+pub extern "C" fn obfuscators_set_log_handler(message_handler: extern "C" fn(i32, *mut c_char)){
+ Logger::init(message_handler);
+}
diff --git a/obfuscators/src/logger.rs b/obfuscators/src/logger.rs
new file mode 100644
index 0000000000..041c22fc2b
--- /dev/null
+++ b/obfuscators/src/logger.rs
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::os::raw::c_char;
+use std::ffi::CString;
+
+use log::{Level, LevelFilter, Log, Metadata, Record};
+use once_cell::sync::OnceCell;
+
+// Logger implementation to integrate the Rust logs with src/loghandler.cpp.
+//
+// This message handler should be initialized alogside the Qt message handler.
+pub struct Logger {
+ message_handler: extern "C" fn(i32, *mut c_char)
+}
+
+static LOGGER: OnceCell = OnceCell::new();
+
+impl Logger {
+ fn new(f: extern "C" fn(i32, *mut c_char)) -> Logger {
+ Logger {
+ message_handler: f
+ }
+ }
+
+ pub fn init(f: extern "C" fn(i32, *mut c_char)) {
+ let logger = LOGGER.get_or_init(|| Logger::new(f));
+ if log::set_logger(logger).is_ok() {
+ log::set_max_level(LevelFilter::Info);
+ }
+ }
+}
+
+impl Log for Logger {
+ fn enabled(&self, _metadata: &Metadata) -> bool {
+ true
+ }
+
+ fn log(&self, record: &Record) {
+ // The numbers here are dictated by the order of the enum on
+ // src/loglevel.h
+ let log_level: i32 = match record.level() {
+ Level::Error => 4,
+ Level::Warn => 3,
+ Level::Info => 2,
+ Level::Debug => 1,
+ Level::Trace => 0,
+ };
+
+ if let Ok(message) = CString::new(record.args().to_string()) {
+ (self.message_handler)(log_level, message.as_ptr() as *mut c_char);
+ }
+ }
+
+ fn flush(&self) {}
+}
\ No newline at end of file
diff --git a/obfuscators/src/main.rs b/obfuscators/src/main.rs
new file mode 100644
index 0000000000..f83a7ea2c4
--- /dev/null
+++ b/obfuscators/src/main.rs
@@ -0,0 +1,104 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::ffi::{c_char, CStr};
+use std::net::IpAddr;
+use std::process::ExitCode;
+use std::sync::atomic::AtomicBool;
+use std::sync::Arc;
+
+use clap::{Parser, Subcommand};
+
+use obfuscators::factory::create_obfuscator;
+use obfuscators::{Config, ObfuscationMethod};
+
+#[derive(Parser)]
+#[command(
+ name = "mozillavpn-obfuscator",
+ about = "Run a Mozilla VPN obfuscator as a standalone local proxy",
+ version
+)]
+struct Cli {
+ #[command(subcommand)]
+ obfuscator: Obfuscator,
+}
+
+#[derive(Subcommand)]
+enum Obfuscator {
+ /// Tunnel WireGuard UDP through a TCP stream.
+ UdpOverTcp {
+ /// Server IPv4 or IPv6 address.
+ #[arg(short, long)]
+ server: IpAddr,
+ /// Server TCP port.
+ #[arg(short, long, value_parser = parse_nonzero_port)]
+ port: u16,
+ /// Local UDP port to listen on (127.0.0.1). Defaults to OS-assigned.
+ #[arg(short, long, value_parser = parse_nonzero_port)]
+ listen_port: Option,
+ /// Fwmark
+ #[arg(long)]
+ fwmark: Option,
+ },
+}
+
+fn parse_nonzero_port(s: &str) -> Result {
+ let port: u16 = s.parse().map_err(|_| format!("invalid port '{s}'"))?;
+ if port == 0 {
+ return Err("port cannot be 0".into());
+ }
+ Ok(port)
+}
+
+fn main() -> ExitCode {
+ let cli = Cli::parse();
+ obfuscators::obfuscators_set_log_handler(log_to_stderr);
+
+ let cfg = match cli.obfuscator {
+ Obfuscator::UdpOverTcp { server, port, listen_port, fwmark } => {
+ let (server_ipv4, server_ipv6) = match server {
+ IpAddr::V4(v4) => (Some(v4), None),
+ IpAddr::V6(v6) => (None, Some(v6)),
+ };
+ Config {
+ method: ObfuscationMethod::UdpOverTcp,
+ public_key: None,
+ server_ipv4,
+ server_ipv6,
+ server_port: port,
+ listen_port: listen_port.unwrap_or(0),
+ server_public_key: None,
+ fwmark,
+ }
+ }
+ };
+
+ let mut obf = match create_obfuscator(&cfg) {
+ Ok(o) => o,
+ Err(e) => {
+ eprintln!("error: failed to start obfuscator: {e}");
+ return ExitCode::FAILURE;
+ }
+ };
+
+ println!("listening on 127.0.0.1:{}", obf.local_port());
+ obf.run(Arc::new(AtomicBool::new(false)));
+ ExitCode::SUCCESS
+}
+
+extern "C" fn log_to_stderr(level: i32, msg: *mut c_char) {
+ if msg.is_null() {
+ return;
+ }
+ let tag = match level {
+ 0 => "TRACE",
+ 1 => "DEBUG",
+ 2 => "INFO",
+ 3 => "WARN",
+ 4 => "ERROR",
+ _ => "LOG",
+ };
+ let s = unsafe { CStr::from_ptr(msg) }.to_string_lossy();
+ eprintln!("[{tag}] {s}");
+}
diff --git a/obfuscators/src/obfuscator.rs b/obfuscators/src/obfuscator.rs
new file mode 100644
index 0000000000..9157c3b36a
--- /dev/null
+++ b/obfuscators/src/obfuscator.rs
@@ -0,0 +1,159 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::sync::atomic::AtomicBool;
+use std::sync::Arc;
+
+use std::ffi::{c_char, CStr};
+use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
+use std::str::FromStr;
+
+use base64::engine::general_purpose::STANDARD as BASE64;
+use base64::Engine;
+
+pub const WG_KEY_LEN: usize = 32;
+
+
+/// An obfuscator is a self-contained proxy. Implementations bind their own
+/// sockets in their constructor, expose the bound port + outbound socket FDs.
+pub trait Obfuscator: Send {
+ /// Local UDP port the obfuscator is listening on (always on 127.0.0.1).
+ /// Callers rewrite the WireGuard peer endpoint to this port.
+ fn local_port(&self) -> u16;
+
+ /// Outbound socket FD for IPv4 traffic, or -1 if not used.
+ /// The platform layer marks/protects this.
+ fn socket_v4(&self) -> i32 {
+ -1
+ }
+
+ /// Outbound socket FD for IPv6 traffic, or -1 if not used.
+ fn socket_v6(&self) -> i32 {
+ -1
+ }
+
+ fn run(&mut self, shutdown: Arc);
+}
+
+// Obfuscation methods enum, remember to keep in sync with the ones in settingsholder.h and models/server.h
+#[repr(u32)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+
+pub enum ObfuscationMethod {
+ NoObfuscation = 0,
+ Lwo = 1,
+ Masque = 2,
+ UdpOverTcp = 3,
+ Shadowsocks = 4,
+}
+
+impl ObfuscationMethod {
+ pub fn from_u32(v: u32) -> Option {
+ match v {
+ 0 => Some(Self::NoObfuscation),
+ 1 => Some(Self::Lwo),
+ 2 => Some(Self::Masque),
+ 3 => Some(Self::UdpOverTcp),
+ 4 => Some(Self::Shadowsocks),
+ _ => None,
+ }
+ }
+}
+
+/// C-ABI view: what the JNA caller actually fills in and passes across the FFI.
+#[repr(C)]
+pub struct ObfuscatorConfig {
+ pub obfuscation_method: u32,
+ pub server_ipv4_addr_in: *const c_char,
+ pub server_ipv6_addr_in: *const c_char,
+ pub server_port: u16,
+ /// Local UDP port the obfuscator should listen on (127.0.0.1).
+ /// 0 = let the OS pick a free port.
+ pub listen_port: u16,
+ // Wireguard keys required by LWO
+ pub server_public_key: *const c_char,
+ pub public_key: *const c_char,
+ #[cfg(target_os = "linux")]
+ pub fwmark: u32,
+}
+
+/// Safe Rust mirror used inside the crate.
+#[derive(Debug, Clone)]
+pub struct Config {
+ pub method: ObfuscationMethod,
+ pub public_key: Option<[u8; WG_KEY_LEN]>,
+ pub server_ipv4: Option,
+ pub server_ipv6: Option,
+ pub server_port: u16,
+ pub listen_port: u16,
+ pub server_public_key: Option<[u8; WG_KEY_LEN]>,
+ #[cfg(target_os = "linux")]
+ pub fwmark: Option,
+}
+
+impl Config {
+ /// Convert a C struct from C++ into a safe Rust value.
+ /// Returns None in case of error: obfuscation method is unknown, the port is zero, or both addresses are missing / unparseable.
+ pub unsafe fn from_c(cfg: *const ObfuscatorConfig) -> Option {
+ if cfg.is_null() {
+ return None;
+ }
+ let cfg = &*cfg;
+ let method = ObfuscationMethod::from_u32(cfg.obfuscation_method)?;
+ if cfg.server_port == 0 {
+ return None;
+ }
+ let server_ipv4 = parse_addr::(cfg.server_ipv4_addr_in);
+ let server_ipv6 = parse_addr::(cfg.server_ipv6_addr_in);
+ if server_ipv4.is_none() && server_ipv6.is_none() {
+ return None;
+ }
+ let public_key = parse_wg_key(cfg.public_key);
+ let server_public_key = parse_wg_key(cfg.server_public_key);
+
+ Some(Self {
+ method,
+ public_key,
+ server_ipv4,
+ server_ipv6,
+ server_port: cfg.server_port,
+ listen_port: cfg.listen_port,
+ server_public_key,
+ #[cfg(target_os = "linux")]
+ fwmark: if cfg.fwmark == 0 { None } else { Some(cfg.fwmark) },
+ })
+ }
+
+ pub fn server_addr(&self) -> SocketAddr {
+ let ip: IpAddr = match (self.server_ipv4, self.server_ipv6) {
+ (Some(v4), _) => v4.into(),
+ (None, Some(v6)) => v6.into(),
+ (None, None) => unreachable!("from_c rejects configs with no address"),
+ };
+ SocketAddr::new(ip, self.server_port)
+ }
+}
+
+unsafe fn parse_addr(p: *const c_char) -> Option {
+ if p.is_null() {
+ return None;
+ }
+ let s = CStr::from_ptr(p).to_str().ok()?;
+ if s.is_empty() {
+ return None;
+ }
+ T::from_str(s).ok()
+}
+
+unsafe fn parse_wg_key(p: *const c_char) -> Option<[u8; WG_KEY_LEN]> {
+ if p.is_null() {
+ return None;
+ }
+ let s = CStr::from_ptr(p).to_str().ok()?;
+ if s.is_empty() {
+ return None;
+ }
+ let decoded = BASE64.decode(s).ok()?;
+ decoded.try_into().ok()
+}
diff --git a/obfuscators/src/udp_over_tcp.rs b/obfuscators/src/udp_over_tcp.rs
new file mode 100644
index 0000000000..62adc0d619
--- /dev/null
+++ b/obfuscators/src/udp_over_tcp.rs
@@ -0,0 +1,111 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::io;
+use std::net::SocketAddr;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+use std::time::Duration;
+
+use tokio::runtime::Runtime;
+use udp_over_tcp::{TcpOptions, Udp2Tcp};
+
+use crate::obfuscator::{Obfuscator, Config};
+
+const SHUTDOWN_POLL: Duration = Duration::from_millis(200);
+
+/// Tunnels UDP traffic through a TCP stream using mullvad/udp-over-tcp.
+/// The TCP socket is what callers must mark/protect.
+
+pub struct UdpOverTcpObfuscator {
+ runtime: Option,
+ inner: Option,
+ local_port: u16,
+ socket_v4: i32,
+ socket_v6: i32,
+}
+
+impl UdpOverTcpObfuscator {
+ pub fn new(cfg: &Config) -> io::Result {
+ let server = cfg.server_addr();
+ let runtime = tokio::runtime::Builder::new_current_thread()
+ .enable_all()
+ .build()?;
+
+ let listen_addr: SocketAddr = ([127, 0, 0, 1], cfg.listen_port).into();
+ let mut tcp_options = TcpOptions::default();
+ tcp_options.nodelay = true;
+ #[cfg(target_os = "linux")]
+ {
+ tcp_options.fwmark = cfg.fwmark;
+ }
+ let inner = runtime
+ .block_on(Udp2Tcp::new(listen_addr, server, tcp_options))
+ .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
+
+ let local_port = inner.local_udp_addr()?.port();
+ let (socket_v4, socket_v6) = tcp_fds(&inner, server);
+
+ Ok(Self {
+ runtime: Some(runtime),
+ inner: Some(inner),
+ local_port,
+ socket_v4,
+ socket_v6,
+ })
+ }
+}
+
+impl Obfuscator for UdpOverTcpObfuscator {
+ fn local_port(&self) -> u16 {
+ self.local_port
+ }
+ fn socket_v4(&self) -> i32 {
+ self.socket_v4
+ }
+ fn socket_v6(&self) -> i32 {
+ self.socket_v6
+ }
+
+ fn run(&mut self, shutdown: Arc) {
+ let Some(runtime) = self.runtime.take() else {
+ return;
+ };
+ let Some(inner) = self.inner.take() else {
+ return;
+ };
+
+ runtime.block_on(async move {
+ let run_fut = inner.run();
+ tokio::pin!(run_fut);
+ loop {
+ tokio::select! {
+ res = &mut run_fut => {
+ let _ = res;
+ return;
+ }
+ _ = tokio::time::sleep(SHUTDOWN_POLL) => {
+ if shutdown.load(Ordering::Acquire) {
+ return;
+ }
+ }
+ }
+ }
+ });
+ }
+}
+
+#[cfg(unix)]
+fn tcp_fds(inner: &Udp2Tcp, server: SocketAddr) -> (i32, i32) {
+ let fd = inner.remote_tcp_fd();
+ match server {
+ SocketAddr::V4(_) => (fd, -1),
+ SocketAddr::V6(_) => (-1, fd),
+ }
+}
+
+#[cfg(not(unix))]
+fn tcp_fds(_inner: &Udp2Tcp, _server: SocketAddr) -> (i32, i32) {
+ (-1, -1)
+}
\ No newline at end of file
diff --git a/scripts/cmake/rustlang.cmake b/scripts/cmake/rustlang.cmake
index 1cfb4d90f8..31ff6e547e 100644
--- a/scripts/cmake/rustlang.cmake
+++ b/scripts/cmake/rustlang.cmake
@@ -155,6 +155,117 @@ if(NOT HAS_CARGO_POOL)
set_property(GLOBAL APPEND PROPERTY JOB_POOLS cargo=1)
endif()
+## Append the appropriate SDKROOT entry to an env list for Apple targets.
+## Updates the variable named by OUT_VAR in the caller's scope.
+function(__rust_append_apple_sdkroot OUT_VAR ARCH)
+ set(_env "${${OUT_VAR}}")
+ if((ARCH STREQUAL "aarch64-apple-ios-sim") OR (ARCH STREQUAL "x86_64-apple-ios"))
+ execute_process(OUTPUT_VARIABLE IOS_SDK_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE
+ COMMAND xcrun --sdk ${CMAKE_OSX_SYSROOT} --show-sdk-version)
+ execute_process(OUTPUT_VARIABLE IOS_SIMULATOR_SDKROOT OUTPUT_STRIP_TRAILING_WHITESPACE
+ COMMAND xcrun --sdk iphonesimulator${IOS_SDK_VERSION} --show-sdk-path)
+ list(APPEND _env SDKROOT=${IOS_SIMULATOR_SDKROOT})
+ elseif(APPLE AND CMAKE_OSX_SYSROOT)
+ if(IS_DIRECTORY ${CMAKE_OSX_SYSROOT})
+ list(APPEND _env "SDKROOT=${CMAKE_OSX_SYSROOT}")
+ else()
+ execute_process(OUTPUT_VARIABLE _sdkroot OUTPUT_STRIP_TRAILING_WHITESPACE
+ COMMAND xcrun --sdk ${CMAKE_OSX_SYSROOT} --show-sdk-path)
+ list(APPEND _env "SDKROOT=${_sdkroot}")
+ endif()
+ endif()
+ set(${OUT_VAR} "${_env}" PARENT_SCOPE)
+endfunction()
+
+## Guess rust target architecture(s) from the CMake configuration.
+function(__rust_guess_target_arch OUT_VAR)
+ set(_arch "")
+ if((CMAKE_SYSTEM_NAME STREQUAL "Darwin") AND CMAKE_OSX_ARCHITECTURES)
+ # Special case for MacOS universal binaries.
+ foreach(OSXARCH ${CMAKE_OSX_ARCHITECTURES})
+ string(REPLACE "arm64" "aarch64" OSXARCH ${OSXARCH})
+ list(APPEND _arch "${OSXARCH}-apple-darwin")
+ endforeach()
+ elseif(NOT CMAKE_CROSSCOMPILING)
+ set(_arch ${RUSTC_HOST_ARCH})
+ elseif(CMAKE_C_COMPILER_TARGET)
+ # If set, the C compiler triple makes a reasonable guess.
+ set(_arch ${CMAKE_C_COMPILER_TARGET})
+ else()
+ message(FATAL_ERROR "Unable to determine rust target architecture when cross compiling.")
+ endif()
+ set(${OUT_VAR} "${_arch}" PARENT_SCOPE)
+endfunction()
+
+## Emit add_custom_command rules that lipo per-arch artifacts into
+## ${BINARY_DIR}/unified/{debug,release}/${FILENAME}.
+function(__rust_lipo_unified)
+ cmake_parse_arguments(LIPO
+ ""
+ "BINARY_DIR;FILENAME"
+ "RELEASE_INPUTS;DEBUG_INPUTS"
+ ${ARGN})
+ foreach(PROFILE debug release)
+ if(PROFILE STREQUAL "release")
+ set(_inputs ${LIPO_RELEASE_INPUTS})
+ else()
+ set(_inputs ${LIPO_DEBUG_INPUTS})
+ endif()
+ add_custom_command(
+ OUTPUT ${LIPO_BINARY_DIR}/unified/${PROFILE}/${LIPO_FILENAME}
+ DEPENDS ${_inputs}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${LIPO_BINARY_DIR}/unified/${PROFILE}
+ COMMAND ${LIPO_BUILD_TOOL} -create -output ${LIPO_BINARY_DIR}/unified/${PROFILE}/${LIPO_FILENAME}
+ ${_inputs}
+ )
+ endforeach()
+endfunction()
+
+## Emit add_custom_command rules to invoke cargo for one (ARCH, PROFILE) pair.
+## CARGO_ARGS supplies the subcommand args that differ between libraries
+## (--lib) and binaries (--bin NAME); everything else is shared.
+function(__rust_add_cargo_build_command)
+ cmake_parse_arguments(RUST_BUILD
+ ""
+ "ARCH;BINARY_DIR;PACKAGE_DIR;OUTPUT_FILE;DEPFILE;PROFILE"
+ "CARGO_ENV;CARGO_ARGS"
+ ${ARGN})
+
+ set(_outdir ${RUST_BUILD_BINARY_DIR}/${RUST_BUILD_ARCH}/${RUST_BUILD_PROFILE})
+ set(_args ${RUST_BUILD_CARGO_ARGS} --target ${RUST_BUILD_ARCH} --target-dir ${RUST_BUILD_BINARY_DIR})
+ if(RUST_BUILD_PROFILE STREQUAL "release")
+ list(APPEND _args --release)
+ endif()
+
+ if((CMAKE_GENERATOR MATCHES "Ninja") OR (CMAKE_GENERATOR MATCHES "Makefiles") OR XCODE)
+ ## If the generator supports it, we can improve build times by setting
+ ## a DEPFILE to let CMake know when the artifact needs rebuilding.
+ cmake_policy(PUSH)
+ cmake_policy(SET CMP0116 NEW)
+ add_custom_command(
+ OUTPUT ${_outdir}/${RUST_BUILD_OUTPUT_FILE}
+ DEPFILE ${_outdir}/${RUST_BUILD_DEPFILE}
+ JOB_POOL cargo
+ WORKING_DIRECTORY ${RUST_BUILD_PACKAGE_DIR}
+ COMMAND ${CMAKE_COMMAND} -E env ${RUST_BUILD_CARGO_ENV}
+ ${CARGO_BUILD_TOOL} build ${_args}
+ )
+ cmake_policy(POP)
+ else()
+ ## For all other generators, set a non-existent output file to force
+ ## the command to be invoked on every build. This relies on cargo to
+ ## rebuild if necessary.
+ add_custom_command(
+ OUTPUT
+ ${_outdir}/${RUST_BUILD_OUTPUT_FILE}
+ ${_outdir}/.noexist
+ WORKING_DIRECTORY ${RUST_BUILD_PACKAGE_DIR}
+ COMMAND ${CMAKE_COMMAND} -E env ${RUST_BUILD_CARGO_ENV}
+ ${CARGO_BUILD_TOOL} build ${_args}
+ )
+ endif()
+endfunction()
+
### Helper function to get the rust library filename with extension.
#
# Sets the variable "RUST_LIBRARY_FILENAME" with the value.
@@ -170,35 +281,30 @@ function(get_rust_library_filename SHARED CRATE_NAME)
endif()
endfunction()
-### Helper function to build Rust static libraries.
+### Helper to build a Rust artifact (library or binary) for one architecture.
#
# Accepts the following arguments:
-# ARCH: Rust target architecture to build with --target ${ARCH}
+# KIND: "library" or "binary". Required.
+# ARCH: Rust target architecture to build with --target ${ARCH}. Required.
# BINARY_DIR: Binary directory to output build artifacts to.
-# PACKAGE_DIR: Soruce directory where Cargo.toml can be found.
-# LIBRARY_FILE: Filename of the expected library to be built.
-# CARGO_ENV: Environment variables to pass to cargo
-# SHARED: Whether or not we are building a shared library. Defaults to "false".
-#
-# This function generates commands necessary to build static archives
-# in ${BINARY_DIR}/${ARCH}/debug/ and ${BINARY_DIR}/${ARCH}/release/
-# and it is up to the caller of this function to link the artifacts
-# into their targets as necessary.
-#
-# This function is intended to be used internally by add_rust_library,
-# you should consider using that instead.
+# PACKAGE_DIR: Source directory where Cargo.toml can be found.
+# CRATE_NAME: Name of the crate to build. Required.
+# BIN_NAME: For KIND=binary, the name of the [[bin]] target. Defaults to CRATE_NAME.
+# CARGO_ENV: Environment variables to pass to cargo.
+# SHARED: For KIND=library, whether to build a shared library. Required for libraries.
#
-function(build_rust_archives)
+# Generates the add_custom_command rules to produce the artifact in
+# ${BINARY_DIR}/${ARCH}/{debug,release}/. Intended for internal use only;
+# callers should prefer add_rust_library / add_rust_binary.
+function(__rust_build_cargo_target)
cmake_parse_arguments(RUST_BUILD
""
- "ARCH;BINARY_DIR;PACKAGE_DIR;CRATE_NAME"
+ "KIND;ARCH;BINARY_DIR;PACKAGE_DIR;CRATE_NAME;BIN_NAME"
"CARGO_ENV;SHARED"
${ARGN})
- list(APPEND RUST_BUILD_CARGO_ENV CARGO_HOME=${CMAKE_BINARY_DIR}/cargo_home)
-
- if(NOT DEFINED RUST_BUILD_SHARED)
- message(FATAL_ERROR "Mandatory argument SHARED was not found")
+ if(NOT RUST_BUILD_KIND)
+ message(FATAL_ERROR "Mandatory argument KIND was not found")
endif()
if(NOT RUST_BUILD_CRATE_NAME)
message(FATAL_ERROR "Mandatory argument CRATE_NAME was not found")
@@ -213,86 +319,43 @@ function(build_rust_archives)
set(RUST_BUILD_PACKAGE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
endif()
- ## Some files that we will be building.
- file(MAKE_DIRECTORY ${RUST_BUILD_BINARY_DIR})
- get_rust_library_filename(${RUST_BUILD_SHARED} ${RUST_BUILD_CRATE_NAME})
+ list(APPEND RUST_BUILD_CARGO_ENV CARGO_HOME=${CMAKE_BINARY_DIR}/cargo_home)
- ## For iOS simulator targets, find the SDKROOT of the simulator matching the
- ## iOS platform SDK.
- if((RUST_BUILD_ARCH STREQUAL "aarch64-apple-ios-sim") OR (RUST_BUILD_ARCH STREQUAL "x86_64-apple-ios"))
- execute_process(OUTPUT_VARIABLE IOS_SDK_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE
- COMMAND xcrun --sdk ${CMAKE_OSX_SYSROOT} --show-sdk-version)
- execute_process(OUTPUT_VARIABLE IOS_SIMULATOR_SDKROOT OUTPUT_STRIP_TRAILING_WHITESPACE
- COMMAND xcrun --sdk iphonesimulator${IOS_SDK_VERSION} --show-sdk-path)
- list(APPEND RUST_BUILD_CARGO_ENV SDKROOT=${IOS_SIMULATOR_SDKROOT})
- elseif(APPLE AND CMAKE_OSX_SYSROOT)
- if (IS_DIRECTORY ${CMAKE_OSX_SYSROOT})
- list(APPEND RUST_BUILD_CARGO_ENV "SDKROOT=${CMAKE_OSX_SYSROOT}")
- else()
- execute_process(OUTPUT_VARIABLE RUST_BUILD_SDKROOT OUTPUT_STRIP_TRAILING_WHITESPACE
- COMMAND xcrun --sdk ${CMAKE_OSX_SYSROOT} --show-sdk-path)
- list(APPEND RUST_BUILD_CARGO_ENV "SDKROOT=${RUST_BUILD_SDKROOT}")
+ if(RUST_BUILD_KIND STREQUAL "library")
+ if(NOT DEFINED RUST_BUILD_SHARED)
+ message(FATAL_ERROR "Mandatory argument SHARED was not found")
endif()
- endif()
-
- if((CMAKE_GENERATOR MATCHES "Ninja") OR (CMAKE_GENERATOR MATCHES "Makefiles") OR XCODE)
- ## If the generator supports it, we can improve build times by setting
- # a DEPFILE to let CMake know when the library needs building and when
- # we can skip it.
- set(RUST_BUILD_DEPENDENCY_FILE
- ${CMAKE_STATIC_LIBRARY_PREFIX}${RUST_BUILD_CRATE_NAME}.d
- )
- cmake_policy(PUSH)
- cmake_policy(SET CMP0116 NEW)
-
- ## Outputs for the release build
- add_custom_command(
- OUTPUT ${RUST_BUILD_BINARY_DIR}/${ARCH}/release/${RUST_LIBRARY_FILENAME}
- DEPFILE ${RUST_BUILD_BINARY_DIR}/${ARCH}/release/${RUST_BUILD_DEPENDENCY_FILE}
- JOB_POOL cargo
- WORKING_DIRECTORY ${RUST_BUILD_PACKAGE_DIR}
- COMMAND ${CMAKE_COMMAND} -E env ${RUST_BUILD_CARGO_ENV}
- ${CARGO_BUILD_TOOL} build --lib --release --target ${ARCH} --target-dir ${RUST_BUILD_BINARY_DIR}
- )
-
- ## Outputs for the debug build
- add_custom_command(
- OUTPUT ${RUST_BUILD_BINARY_DIR}/${ARCH}/debug/${RUST_LIBRARY_FILENAME}
- DEPFILE ${RUST_BUILD_BINARY_DIR}/${ARCH}/debug/${RUST_BUILD_DEPENDENCY_FILE}
- JOB_POOL cargo
- WORKING_DIRECTORY ${RUST_BUILD_PACKAGE_DIR}
- COMMAND ${CMAKE_COMMAND} -E env ${RUST_BUILD_CARGO_ENV}
- ${CARGO_BUILD_TOOL} build --lib --target ${ARCH} --target-dir ${RUST_BUILD_BINARY_DIR}
- )
-
- ## Reset our policy changes
- cmake_policy(POP)
+ get_rust_library_filename(${RUST_BUILD_SHARED} ${RUST_BUILD_CRATE_NAME})
+ set(_output ${RUST_LIBRARY_FILENAME})
+ set(_depfile ${CMAKE_STATIC_LIBRARY_PREFIX}${RUST_BUILD_CRATE_NAME}.d)
+ set(_cargo_args --lib)
+ elseif(RUST_BUILD_KIND STREQUAL "binary")
+ if(NOT RUST_BUILD_BIN_NAME)
+ set(RUST_BUILD_BIN_NAME ${RUST_BUILD_CRATE_NAME})
+ endif()
+ get_rust_binary_filename(${RUST_BUILD_BIN_NAME})
+ set(_output ${RUST_BINARY_FILENAME})
+ set(_depfile ${RUST_BUILD_BIN_NAME}.d)
+ set(_cargo_args --bin ${RUST_BUILD_BIN_NAME})
else()
- ## For all other generators, set a non-existent output file to force
- # the command to be invoked on every build. This ensures that the
- # library stays up todate with the sources, and relies on cargo to
- # rebuild if necessary.
-
- ## Outputs for the release build
- add_custom_command(
- OUTPUT
- ${RUST_BUILD_BINARY_DIR}/${ARCH}/release/${RUST_LIBRARY_FILENAME}
- ${RUST_BUILD_BINARY_DIR}/${ARCH}/release/.noexist
- WORKING_DIRECTORY ${RUST_BUILD_PACKAGE_DIR}
- COMMAND ${CMAKE_COMMAND} -E env ${RUST_BUILD_CARGO_ENV}
- ${CARGO_BUILD_TOOL} build --lib --release --target ${ARCH} --target-dir ${RUST_BUILD_BINARY_DIR}
- )
+ message(FATAL_ERROR "Invalid KIND \"${RUST_BUILD_KIND}\": must be 'library' or 'binary'")
+ endif()
- ## Outputs for the debug build
- add_custom_command(
- OUTPUT
- ${RUST_BUILD_BINARY_DIR}/${ARCH}/debug/${RUST_LIBRARY_FILENAME}
- ${RUST_BUILD_BINARY_DIR}/${ARCH}/debug/.noexist
- WORKING_DIRECTORY ${RUST_BUILD_PACKAGE_DIR}
- COMMAND ${CMAKE_COMMAND} -E env ${RUST_BUILD_CARGO_ENV}
- ${CARGO_BUILD_TOOL} build --lib --target ${ARCH} --target-dir ${RUST_BUILD_BINARY_DIR}
+ file(MAKE_DIRECTORY ${RUST_BUILD_BINARY_DIR})
+ __rust_append_apple_sdkroot(RUST_BUILD_CARGO_ENV ${RUST_BUILD_ARCH})
+
+ foreach(PROFILE debug release)
+ __rust_add_cargo_build_command(
+ ARCH ${RUST_BUILD_ARCH}
+ BINARY_DIR ${RUST_BUILD_BINARY_DIR}
+ PACKAGE_DIR ${RUST_BUILD_PACKAGE_DIR}
+ OUTPUT_FILE ${_output}
+ DEPFILE ${_depfile}
+ PROFILE ${PROFILE}
+ CARGO_ENV ${RUST_BUILD_CARGO_ENV}
+ CARGO_ARGS ${_cargo_args}
)
- endif()
+ endforeach()
endfunction()
### Helper function to create a linkable target from a Rust package.
@@ -350,30 +413,16 @@ function(add_rust_library TARGET_NAME)
set(RUST_TARGET_PACKAGE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
endif()
- # Guess the target architecture if not set.
if(NOT RUST_TARGET_ARCH)
- if((CMAKE_SYSTEM_NAME STREQUAL "Darwin") AND CMAKE_OSX_ARCHITECTURES)
- # Special case for MacOS universal binaries.
- foreach(OSXARCH ${CMAKE_OSX_ARCHITECTURES})
- string(REPLACE "arm64" "aarch64" OSXARCH ${OSXARCH})
- list(APPEND RUST_TARGET_ARCH "${OSXARCH}-apple-darwin")
- endforeach()
- elseif(NOT CMAKE_CROSSCOMPILING)
- set(RUST_TARGET_ARCH ${RUSTC_HOST_ARCH})
- elseif(CMAKE_C_COMPILER_TARGET)
- # If set, the C compiler triple makes a reasonable guess.
- set(RUST_TARGET_ARCH ${CMAKE_C_COMPILER_TARGET})
- else()
- # TODO: We could write something here for Android and IOS maybe
- message(FATAL_ERROR "Unable to determine rust target architecture when cross compiling.")
- endif()
+ __rust_guess_target_arch(RUST_TARGET_ARCH)
endif()
get_rust_library_filename(${RUST_TARGET_SHARED} ${RUST_TARGET_CRATE_NAME})
## Build the rust library file(s)
foreach(ARCH ${RUST_TARGET_ARCH})
- build_rust_archives(
+ __rust_build_cargo_target(
+ KIND library
ARCH ${ARCH}
BINARY_DIR ${RUST_TARGET_BINARY_DIR}
PACKAGE_DIR ${RUST_TARGET_PACKAGE_DIR}
@@ -428,19 +477,11 @@ function(add_rust_library TARGET_NAME)
IMPORTED_LOCATION_DEBUG ${RUST_TARGET_BINARY_DIR}/unified/debug/${FW_NAME}.framework/${FW_NAME}
)
else()
- add_custom_command(
- OUTPUT ${RUST_TARGET_BINARY_DIR}/unified/release/${RUST_LIBRARY_FILENAME}
- DEPENDS ${RUST_TARGET_RELEASE_LIBS}
- COMMAND ${CMAKE_COMMAND} -E make_directory ${RUST_TARGET_BINARY_DIR}/unified/release
- COMMAND ${LIPO_BUILD_TOOL} -create -output ${RUST_TARGET_BINARY_DIR}/unified/release/${RUST_LIBRARY_FILENAME}
- ${RUST_TARGET_RELEASE_LIBS}
- )
- add_custom_command(
- OUTPUT ${RUST_TARGET_BINARY_DIR}/unified/debug/${RUST_LIBRARY_FILENAME}
- DEPENDS ${RUST_TARGET_DEBUG_LIBS}
- COMMAND ${CMAKE_COMMAND} -E make_directory ${RUST_TARGET_BINARY_DIR}/unified/debug
- COMMAND ${LIPO_BUILD_TOOL} -create -output ${RUST_TARGET_BINARY_DIR}/unified/debug/${RUST_LIBRARY_FILENAME}
- ${RUST_TARGET_DEBUG_LIBS}
+ __rust_lipo_unified(
+ BINARY_DIR ${RUST_TARGET_BINARY_DIR}
+ FILENAME ${RUST_LIBRARY_FILENAME}
+ RELEASE_INPUTS ${RUST_TARGET_RELEASE_LIBS}
+ DEBUG_INPUTS ${RUST_TARGET_DEBUG_LIBS}
)
add_custom_target(${TARGET_NAME}_builder
@@ -480,3 +521,109 @@ function(add_rust_library TARGET_NAME)
add_dependencies(${TARGET_NAME} ${TARGET_NAME}_builder)
set_property(TARGET ${TARGET_NAME} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS})
endfunction()
+
+### Helper function to get the rust binary filename with platform extension.
+#
+# Sets the variable "RUST_BINARY_FILENAME" with the value.
+function(get_rust_binary_filename BIN_NAME)
+ set(RUST_BINARY_FILENAME
+ ${BIN_NAME}${CMAKE_EXECUTABLE_SUFFIX}
+ PARENT_SCOPE)
+endfunction()
+
+### Helper function to create an IMPORTED executable target from a Rust crate.
+#
+# This function takes one mandatory argument: TARGET_NAME which sets the
+# name of the CMake target to produce.
+#
+# Accepts the following optional arguments:
+# ARCH: Rust target architecture(s) to build with --target ${ARCH}
+# BINARY_DIR: Binary directory to output build artifacts to.
+# PACKAGE_DIR: Source directory where Cargo.toml can be found.
+# CRATE_NAME: Name of the crate (Cargo package) to build. Required.
+# BIN_NAME: Name of the [[bin]] target inside the crate. Defaults to CRATE_NAME.
+# CARGO_ENV: Environment variables to pass to cargo.
+# DEPENDS: Additional files on which the binary depends.
+#
+# Produces a custom target named ${TARGET_NAME} that builds the binary and
+# sets ${TARGET_NAME}_EXECUTABLE in the caller's scope to the path of the built
+# binary (usable in install()).
+# On macOS multiple architectures are combined into a universal binary using
+# lipo, mirroring add_rust_library. iOS targets are not supported and should
+# not call this function.
+function(add_rust_binary TARGET_NAME)
+ cmake_parse_arguments(RUST_TARGET
+ ""
+ "BINARY_DIR;PACKAGE_DIR;CRATE_NAME;BIN_NAME"
+ "ARCH;CARGO_ENV;DEPENDS"
+ ${ARGN})
+
+ if(IOS)
+ message(FATAL_ERROR "add_rust_binary is not supported on iOS; use add_rust_library instead.")
+ endif()
+
+ if(NOT RUST_TARGET_CRATE_NAME)
+ message(FATAL_ERROR "Mandatory argument CRATE_NAME was not found")
+ endif()
+ if(NOT RUST_TARGET_BIN_NAME)
+ set(RUST_TARGET_BIN_NAME ${RUST_TARGET_CRATE_NAME})
+ endif()
+ if(NOT RUST_TARGET_BINARY_DIR)
+ set(RUST_TARGET_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
+ endif()
+ if(NOT RUST_TARGET_PACKAGE_DIR)
+ set(RUST_TARGET_PACKAGE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+ endif()
+
+ if(NOT RUST_TARGET_ARCH)
+ __rust_guess_target_arch(RUST_TARGET_ARCH)
+ endif()
+
+ get_rust_binary_filename(${RUST_TARGET_BIN_NAME})
+
+ ## Build the rust binary for each requested architecture.
+ foreach(ARCH ${RUST_TARGET_ARCH})
+ __rust_build_cargo_target(
+ KIND binary
+ ARCH ${ARCH}
+ BINARY_DIR ${RUST_TARGET_BINARY_DIR}
+ PACKAGE_DIR ${RUST_TARGET_PACKAGE_DIR}
+ CRATE_NAME ${RUST_TARGET_CRATE_NAME}
+ BIN_NAME ${RUST_TARGET_BIN_NAME}
+ CARGO_ENV ${RUST_TARGET_CARGO_ENV}
+ )
+
+ if(RUST_TARGET_DEPENDS)
+ add_custom_command(APPEND
+ OUTPUT ${RUST_TARGET_BINARY_DIR}/${ARCH}/release/${RUST_BINARY_FILENAME}
+ DEPENDS ${RUST_TARGET_DEPENDS}
+ )
+ add_custom_command(APPEND
+ OUTPUT ${RUST_TARGET_BINARY_DIR}/${ARCH}/debug/${RUST_BINARY_FILENAME}
+ DEPENDS ${RUST_TARGET_DEPENDS}
+ )
+ endif()
+
+ list(APPEND RUST_TARGET_RELEASE_BINS ${RUST_TARGET_BINARY_DIR}/${ARCH}/release/${RUST_BINARY_FILENAME})
+ list(APPEND RUST_TARGET_DEBUG_BINS ${RUST_TARGET_BINARY_DIR}/${ARCH}/debug/${RUST_BINARY_FILENAME})
+ endforeach()
+
+ if((CMAKE_SYSTEM_NAME STREQUAL "Darwin"))
+ ## Combine all architectures into a single universal binary.
+ __rust_lipo_unified(
+ BINARY_DIR ${RUST_TARGET_BINARY_DIR}
+ FILENAME ${RUST_BINARY_FILENAME}
+ RELEASE_INPUTS ${RUST_TARGET_RELEASE_BINS}
+ DEBUG_INPUTS ${RUST_TARGET_DEBUG_BINS}
+ )
+ set(_executable ${RUST_TARGET_BINARY_DIR}/unified/$,debug,release>/${RUST_BINARY_FILENAME})
+ else()
+ ## For non-Darwin platforms, only build the first architecture.
+ list(GET RUST_TARGET_ARCH 0 RUST_FIRST_ARCH)
+ set(_executable ${RUST_TARGET_BINARY_DIR}/${RUST_FIRST_ARCH}/$,debug,release>/${RUST_BINARY_FILENAME})
+ endif()
+
+ add_custom_target(${TARGET_NAME} DEPENDS ${_executable})
+ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tools")
+ set(${TARGET_NAME}_EXECUTABLE ${_executable} PARENT_SCOPE)
+endfunction()
diff --git a/src/cmake/android.cmake b/src/cmake/android.cmake
index c81d02739e..b2725976a3 100644
--- a/src/cmake/android.cmake
+++ b/src/cmake/android.cmake
@@ -10,7 +10,38 @@ if(QT_KNOWN_POLICY_QTP0002)
qt_policy(SET QTP0002 OLD)
endif()
+## Build the obfuscators shared library.
+## This is used by Android via JNA.
+include(${CMAKE_SOURCE_DIR}/scripts/cmake/rustlang.cmake)
+
+if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
+ set(OBFUSCATORS_RUST_ARCH "aarch64-linux-android")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7-a")
+ set(OBFUSCATORS_RUST_ARCH "armv7-linux-androideabi")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686")
+ set(OBFUSCATORS_RUST_ARCH "i686-linux-android")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
+ set(OBFUSCATORS_RUST_ARCH "x86_64-linux-android")
+else()
+ message(FATAL_ERROR "Unsupported Android architecture for obfuscators: ${CMAKE_SYSTEM_PROCESSOR}")
+endif()
+
+set(OBFUSCATORS_CARGO_ENV
+ CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}/obfuscators
+ "RUSTFLAGS=-Ctarget-feature=-crt-static -C link-arg=-Wl,-z,max-page-size=16384 -C link-arg=-Wl,-z,common-page-size=16384"
+)
+
+add_rust_library(obfuscators
+ ARCH ${OBFUSCATORS_RUST_ARCH}
+ PACKAGE_DIR ${CMAKE_SOURCE_DIR}/obfuscators
+ BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/obfuscators
+ CRATE_NAME obfuscators
+ CARGO_ENV ${OBFUSCATORS_CARGO_ENV}
+ SHARED 1
+)
+
target_link_libraries(mozillavpn PRIVATE
+ obfuscators
Qt6::Test
Qt6::Xml)
@@ -40,6 +71,7 @@ target_sources(mozillavpn PRIVATE
)
get_property(OPENSSL_LIBS_DIR GLOBAL PROPERTY OPENSSL_LIBS)
+get_property(OBFUSCATORS_LIB_LOCATION TARGET obfuscators PROPERTY LOCATION_${CMAKE_BUILD_TYPE})
# This property flags the build system to copy these
# shared libraries into the expected Android shared library folder.
@@ -64,6 +96,7 @@ set_property(TARGET mozillavpn PROPERTY QT_ANDROID_EXTRA_LIBS
${OPENSSL_LIBS_DIR}/libssl.so
${OPENSSL_LIBS_DIR}/libcrypto_1_1.so
${OPENSSL_LIBS_DIR}/libssl_1_1.so
+ ${OBFUSCATORS_LIB_LOCATION}
APPEND)
diff --git a/src/cmake/ios.cmake b/src/cmake/ios.cmake
index 1b1e26ebea..27ad032309 100644
--- a/src/cmake/ios.cmake
+++ b/src/cmake/ios.cmake
@@ -104,6 +104,8 @@ target_sources(mozillavpn PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/platforms/macos/macospingsender.h
${CMAKE_CURRENT_SOURCE_DIR}/tasks/purchase/taskpurchase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tasks/purchase/taskpurchase.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/dummyobfuscator.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/dummyobfuscator.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosauthenticationlistener.mm
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosauthenticationlistener.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosiaphandler.swift
diff --git a/src/cmake/linux.cmake b/src/cmake/linux.cmake
index eba217b7dc..483b44644c 100644
--- a/src/cmake/linux.cmake
+++ b/src/cmake/linux.cmake
@@ -106,6 +106,19 @@ if(NOT BUILD_FLATPAK)
include(${CMAKE_SOURCE_DIR}/scripts/cmake/golang.cmake)
add_go_library(netfilter ${CMAKE_SOURCE_DIR}/linux/netfilter/netfilter.go)
target_link_libraries(mozillavpn PRIVATE netfilter)
+
+ # Add the obfuscator binary
+ add_rust_binary(mozillavpn-obfuscator
+ PACKAGE_DIR ${CMAKE_SOURCE_DIR}/obfuscators
+ BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/obfuscators_target
+ CRATE_NAME obfuscators
+ BIN_NAME mozillavpn-obfuscator
+ CARGO_ENV CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}/obfuscators_target
+ )
+ add_dependencies(mozillavpn mozillavpn-obfuscator)
+
+ # Install the obfuscator binary
+ install(PROGRAMS ${mozillavpn-obfuscator_EXECUTABLE} DESTINATION bin)
else()
# Linux source files for sandboxed builds
target_compile_definitions(mozillavpn PRIVATE MZ_FLATPAK)
diff --git a/src/cmake/macos-daemon.cmake b/src/cmake/macos-daemon.cmake
index 680a4a3328..42daa88cb8 100644
--- a/src/cmake/macos-daemon.cmake
+++ b/src/cmake/macos-daemon.cmake
@@ -37,6 +37,9 @@ target_sources(daemon PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/daemon/daemonerrors.h
${CMAKE_CURRENT_SOURCE_DIR}/daemon/dnsutils.h
${CMAKE_CURRENT_SOURCE_DIR}/daemon/iputils.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/obfuscator.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/qprocessobfuscator.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/qprocessobfuscator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/daemon/wireguardutils.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/macos/daemon/dnsutilsmacos.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/macos/daemon/dnsutilsmacos.h
diff --git a/src/cmake/sources.cmake b/src/cmake/sources.cmake
index 93c20237ff..3effb05f0f 100644
--- a/src/cmake/sources.cmake
+++ b/src/cmake/sources.cmake
@@ -70,6 +70,7 @@ target_sources(mozillavpn-sources INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/controller_p.h
${CMAKE_CURRENT_SOURCE_DIR}/daemon/daemon.cpp
${CMAKE_CURRENT_SOURCE_DIR}/daemon/daemon.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/obfuscator.h
${CMAKE_CURRENT_SOURCE_DIR}/daemon/daemonlocalserverconnection.cpp
${CMAKE_CURRENT_SOURCE_DIR}/daemon/daemonlocalserverconnection.h
${CMAKE_CURRENT_SOURCE_DIR}/daemon/dnsutils.h
@@ -194,11 +195,19 @@ if(NOT QT_FEATURE_zstd)
set_property(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/resources/public_keys/public_keys.qrc PROPERTY AUTORCC_OPTIONS "--no-zstd")
endif()
+if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten" AND
+ NOT ${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
+ target_sources(mozillavpn-sources INTERFACE
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/qprocessobfuscator.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/qprocessobfuscator.h
+ )
+endif()
+
# Sources for desktop platforms.
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
${CMAKE_SYSTEM_NAME} STREQUAL "Windows" OR
${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
- target_sources(mozillavpn-sources INTERFACE
+ target_sources(mozillavpn-sources INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/systemtraynotificationhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/systemtraynotificationhandler.h
)
diff --git a/src/cmake/wasm.cmake b/src/cmake/wasm.cmake
index c071c57020..32855a9eb0 100644
--- a/src/cmake/wasm.cmake
+++ b/src/cmake/wasm.cmake
@@ -15,6 +15,8 @@ target_sources(mozillavpn PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/platforms/wasm/wasmwindowcontroller.h
${CMAKE_CURRENT_SOURCE_DIR}/platforms/wasm/wasmiaphandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/platforms/wasm/wasmiaphandler.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/dummyobfuscator.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/daemon/obfuscator/dummyobfuscator.h
${CMAKE_CURRENT_SOURCE_DIR}/systemtraynotificationhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/systemtraynotificationhandler.h
${CMAKE_CURRENT_SOURCE_DIR}/tasks/purchase/taskpurchase.cpp
diff --git a/src/controller.cpp b/src/controller.cpp
index 724394d462..b6d95f8694 100644
--- a/src/controller.cpp
+++ b/src/controller.cpp
@@ -376,6 +376,9 @@ auto Controller::setupConfigs(DNSPortPolicy dnsPort,
}
SettingsHolder* settingsHolder = SettingsHolder::instance();
QList returnList;
+ Server::ObfuscationMethod obfuscationMethod =
+ static_cast(
+ settingsHolder->obfuscationMethod());
auto allowedIPList = m_initiator == ExtensionUser
? getExtensionProxyAddressRanges(exitServer)
@@ -383,6 +386,7 @@ auto Controller::setupConfigs(DNSPortPolicy dnsPort,
// Prepare the exit server's connection data.
InterfaceConfig exitConfig;
exitConfig.m_privateKey = vpn->keys()->privateKey();
+ exitConfig.m_publicKey = vpn->keys()->publicKey();
exitConfig.m_deviceIpv4Address = device->ipv4Address();
exitConfig.m_deviceIpv6Address = device->ipv6Address();
exitConfig.m_serverIpv4Gateway = exitServer.ipv4Gateway();
@@ -394,6 +398,7 @@ auto Controller::setupConfigs(DNSPortPolicy dnsPort,
exitConfig.m_allowedIPAddressRanges = allowedIPList;
exitConfig.m_dnsServer = DNSHelper::getDNS(exitServer.ipv4Gateway());
exitConfig.m_exitCity = exitServer.cityName();
+ exitConfig.m_obfuscationMethod = Server::ObfuscationMethod::NoObfuscation;
logger.debug() << "DNS Set" << exitConfig.m_dnsServer;
if (m_impl->splitTunnelSupported()) {
@@ -407,12 +412,19 @@ auto Controller::setupConfigs(DNSPortPolicy dnsPort,
if (!Feature::multiHop.supported || !m_serverData.multihop()) {
logger.info() << "Activating single hop";
exitConfig.m_hopType = InterfaceConfig::SingleHop;
+ exitConfig.m_obfuscationMethod = obfuscationMethod;
// If requested, force the use of port 53/DNS.
if (dnsPort == ForceDNSPort) {
logger.info() << "Forcing port 53";
exitConfig.m_serverPort = 53;
}
+
+ // If UDP over TCP is enabled choose a dedicated port
+ if (settingsHolder->obfuscationMethod() ==
+ Server::ObfuscationMethod::UdpOverTcp) {
+ exitConfig.m_serverPort = exitServer.chooseTcpPort();
+ }
}
// For controllers that support multiple hops, create a queue of connections.
// The entry server should start first, followed by the exit server.
@@ -435,6 +447,7 @@ auto Controller::setupConfigs(DNSPortPolicy dnsPort,
InterfaceConfig entryConfig;
entryConfig.m_privateKey = vpn->keys()->privateKey();
+ entryConfig.m_publicKey = vpn->keys()->publicKey();
entryConfig.m_deviceIpv4Address = device->ipv4Address();
entryConfig.m_deviceIpv6Address = device->ipv6Address();
entryConfig.m_serverPublicKey = entryServer.publicKey();
@@ -442,6 +455,7 @@ auto Controller::setupConfigs(DNSPortPolicy dnsPort,
entryConfig.m_serverIpv6AddrIn = entryServer.ipv6AddrIn();
entryConfig.m_serverPort = entryServer.choosePort();
entryConfig.m_hopType = InterfaceConfig::MultiHopEntry;
+ entryConfig.m_obfuscationMethod = obfuscationMethod;
entryConfig.m_allowedIPAddressRanges.append(
IPAddress(exitServer.ipv4AddrIn()));
if (!exitServer.ipv6AddrIn().isEmpty()) {
@@ -455,6 +469,11 @@ auto Controller::setupConfigs(DNSPortPolicy dnsPort,
logger.info() << "Forcing port 53";
entryConfig.m_serverPort = 53;
}
+ // If UDP over TCP is enabled choose a dedicated port
+ if (settingsHolder->obfuscationMethod() ==
+ Server::ObfuscationMethod::UdpOverTcp) {
+ entryConfig.m_serverPort = exitServer.chooseTcpPort();
+ }
returnList.append(entryConfig);
}
@@ -484,6 +503,12 @@ auto Controller::setupConfigs(DNSPortPolicy dnsPort,
exitConfig.m_serverIpv4AddrIn = entryServer.ipv4AddrIn();
exitConfig.m_serverIpv6AddrIn = entryServer.ipv6AddrIn();
exitConfig.m_entryCity = entryServer.cityName();
+ // We cannot emulate multihop and use UDP over TCP at the same time, LWO
+ // will work
+ exitConfig.m_obfuscationMethod =
+ obfuscationMethod == Server::ObfuscationMethod::LWO
+ ? Server::ObfuscationMethod::LWO
+ : Server::ObfuscationMethod::NoObfuscation;
}
returnList.append(exitConfig);
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index fcc5c60756..f30ea285b3 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -18,8 +18,15 @@
#include "leakdetector.h"
#include "logger.h"
#include "loghandler.h"
+#include "obfuscator/obfuscator.h"
#include "wireguardutils.h"
+#if defined(MZ_WASM) || defined(MZ_IOS)
+# include "obfuscator/dummyobfuscator.h"
+#else
+# include "obfuscator/qprocessobfuscator.h"
+#endif
+
constexpr const char* JSON_ALLOWEDIPADDRESSRANGES = "allowedIPAddressRanges";
constexpr int HANDSHAKE_POLL_MSEC = 250;
@@ -137,8 +144,27 @@ bool Daemon::activate(const InterfaceConfig& config) {
}
}
+ // If an obfuscator is configured, start the relay and rewrite the peer
+ // endpoint so WireGuard talks to it instead of the real server.
+ // For multi-hop configure obfuscator only on the entry node.
+ std::unique_ptr obfuscator;
+ InterfaceConfig peerConfig = config;
+ if (config.m_obfuscationMethod != Server::ObfuscationMethod::NoObfuscation &&
+ config.m_hopType != InterfaceConfig::MultiHopExit) {
+ obfuscator = createObfuscator(config);
+ if (!obfuscator->start()) {
+ logger.error() << "Failed to start obfuscator"
+ << config.m_obfuscationMethod;
+ return false;
+ }
+ peerConfig.m_serverIpv4AddrIn = "127.0.0.1";
+ peerConfig.m_serverIpv6AddrIn = "::1";
+ peerConfig.m_serverPort = obfuscator->localPort();
+ m_obfuscator = std::move(obfuscator);
+ }
+
// Add the peer to this interface.
- if (!wgutils()->updatePeer(config)) {
+ if (!wgutils()->updatePeer(peerConfig)) {
logger.error() << "Peer creation failed.";
return false;
}
@@ -224,6 +250,7 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
}
GETVALUE("privateKey", config.m_privateKey, String);
+ GETVALUE("publicKey", config.m_publicKey, String);
GETVALUE("serverPublicKey", config.m_serverPublicKey, String);
GETVALUE("serverPort", config.m_serverPort, Double);
@@ -332,6 +359,27 @@ bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
return false;
}
+
+ if (!obj.contains("obfuscationMethod")) {
+ config.m_obfuscationMethod = Server::ObfuscationMethod::NoObfuscation;
+ } else {
+ QJsonValue value = obj.value("obfuscationMethod");
+ if (!value.isString()) {
+ logger.error() << "obfuscationMethod is not a string";
+ return false;
+ }
+
+ bool okay;
+ QMetaEnum meta = QMetaEnum::fromType();
+ config.m_obfuscationMethod = Server::ObfuscationMethod(
+ meta.keyToValue(value.toString().toUtf8().constData(), &okay));
+ if (!okay) {
+ logger.error() << "obfuscationMethod" << value.toString()
+ << "is not valid";
+ return false;
+ }
+ }
+
return true;
}
@@ -369,6 +417,9 @@ bool Daemon::deactivate(bool emitSignals) {
}
m_connections.clear();
+ // Delete the obfuscator
+ m_obfuscator.reset();
+
// Delete the interface
return wgutils()->deleteInterface();
}
@@ -386,6 +437,15 @@ QString Daemon::logs() {
void Daemon::cleanLogs() { LogHandler::instance()->cleanupLogs(); }
+std::unique_ptr Daemon::createObfuscator(
+ const InterfaceConfig& config) {
+#if defined(MZ_WASM) || defined(MZ_IOS)
+ return std::make_unique(config);
+#else
+ return std::make_unique(config);
+#endif
+}
+
bool Daemon::supportServerSwitching(const InterfaceConfig& config) const {
if (!m_connections.contains(config.m_hopType)) {
return false;
@@ -409,8 +469,25 @@ bool Daemon::switchServer(const InterfaceConfig& config) {
const InterfaceConfig& lastConfig =
m_connections.value(config.m_hopType).m_config;
+ // Stand up a new obfuscator for the new endpoint (entry hop only)
+ std::unique_ptr obfuscator;
+ InterfaceConfig peerConfig = config;
+ if (config.m_obfuscationMethod != Server::ObfuscationMethod::NoObfuscation &&
+ config.m_hopType != InterfaceConfig::MultiHopExit) {
+ obfuscator = createObfuscator(config);
+ if (!obfuscator->start()) {
+ logger.error() << "Failed to start obfuscator on switch"
+ << config.m_obfuscationMethod;
+ return false;
+ }
+ peerConfig.m_serverIpv4AddrIn = "127.0.0.1";
+ peerConfig.m_serverIpv6AddrIn = QString();
+ peerConfig.m_serverPort = obfuscator->localPort();
+ m_obfuscator = std::move(obfuscator);
+ }
+
// Activate the new peer and its routes.
- if (!wgutils()->updatePeer(config)) {
+ if (!wgutils()->updatePeer(peerConfig)) {
logger.error()
<< "Server switch failed to update the peer wireguard config";
return false;
diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h
index 365cf457fb..26cc2a6324 100644
--- a/src/daemon/daemon.h
+++ b/src/daemon/daemon.h
@@ -7,13 +7,16 @@
#include
#include
+#include
#include "daemon/daemonerrors.h"
#include "daemonerrors.h"
#include "interfaceconfig.h"
+#include "obfuscator/obfuscator.h"
class DnsUtils;
class IPUtils;
+class Obfuscator;
class WireguardUtils;
class Daemon : public QObject {
@@ -56,6 +59,7 @@ class Daemon : public QObject {
private:
bool maybeUpdateResolvers(const InterfaceConfig& config);
+ std::unique_ptr createObfuscator(const InterfaceConfig& config);
protected:
virtual bool run(Op op, const InterfaceConfig& config) {
@@ -84,6 +88,7 @@ class Daemon : public QObject {
};
QMap m_connections;
QTimer m_handshakeTimer;
+ std::unique_ptr m_obfuscator;
};
#endif // DAEMON_H
diff --git a/src/daemon/obfuscator/dummyobfuscator.cpp b/src/daemon/obfuscator/dummyobfuscator.cpp
new file mode 100644
index 0000000000..31f08dd9a1
--- /dev/null
+++ b/src/daemon/obfuscator/dummyobfuscator.cpp
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "dummyobfuscator.h"
+
+#include "leakdetector.h"
+#include "logger.h"
+
+namespace {
+Logger logger("DummyObfuscator");
+}
+
+DummyObfuscator::DummyObfuscator(const InterfaceConfig& config) {
+ MZ_COUNT_CTOR(DummyObfuscator);
+ Q_UNUSED(config);
+}
+
+bool DummyObfuscator::start() {
+ logger.warning() << "Obfuscation is not supported on this platform";
+ return false;
+}
+
+DummyObfuscator::~DummyObfuscator() { MZ_COUNT_DTOR(DummyObfuscator); }
diff --git a/src/daemon/obfuscator/dummyobfuscator.h b/src/daemon/obfuscator/dummyobfuscator.h
new file mode 100644
index 0000000000..ba9f46b3dd
--- /dev/null
+++ b/src/daemon/obfuscator/dummyobfuscator.h
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DUMMYOBFUSCATOR_H
+#define DUMMYOBFUSCATOR_H
+
+#include
+
+#include "../utils/interfaceconfig.h"
+#include "obfuscator.h"
+
+// No-op obfuscator used on WASM
+class DummyObfuscator final : public Obfuscator {
+ public:
+ explicit DummyObfuscator(const InterfaceConfig& config);
+ ~DummyObfuscator() override;
+
+ bool start() override;
+ quint16 localPort() const override { return m_localPort; }
+
+ private:
+ quint16 m_localPort = 0;
+};
+
+#endif // DUMMYOBFUSCATOR_H
diff --git a/src/daemon/obfuscator/obfuscator.h b/src/daemon/obfuscator/obfuscator.h
new file mode 100644
index 0000000000..44ee3eb895
--- /dev/null
+++ b/src/daemon/obfuscator/obfuscator.h
@@ -0,0 +1,26 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef OBFUSCATOR_H
+#define OBFUSCATOR_H
+
+#include
+
+// Generic obfuscator interface.
+class Obfuscator {
+ public:
+ virtual ~Obfuscator() = default;
+
+ Obfuscator(const Obfuscator&) = delete;
+ Obfuscator& operator=(const Obfuscator&) = delete;
+
+ virtual bool start() = 0;
+
+ virtual quint16 localPort() const = 0;
+
+ protected:
+ Obfuscator() = default;
+};
+
+#endif // OBFUSCATOR_H
diff --git a/src/daemon/obfuscator/qprocessobfuscator.cpp b/src/daemon/obfuscator/qprocessobfuscator.cpp
new file mode 100644
index 0000000000..1dd04cc1e6
--- /dev/null
+++ b/src/daemon/obfuscator/qprocessobfuscator.cpp
@@ -0,0 +1,130 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "qprocessobfuscator.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "leakdetector.h"
+#include "logger.h"
+
+constexpr int OBFUSCATOR_PROC_TIMEOUT_MS = 5000;
+#ifdef MZ_LINUX
+constexpr uint32_t WG_FIREWALL_MARK = 0xca6c;
+#endif
+
+namespace {
+Logger logger("QProcessObfuscator");
+}
+
+QProcessObfuscator::QProcessObfuscator(const InterfaceConfig& config) {
+ MZ_COUNT_CTOR(QProcessObfuscator);
+
+ const QStringList args = buildArgs(config);
+ if (args.isEmpty()) {
+ logger.error() << "Unsupported obfuscation method"
+ << config.m_obfuscationMethod;
+ return;
+ }
+
+ const QString binary = binaryName();
+ m_process.setProgram(binary);
+ m_process.setArguments(args);
+ // Merge stderr into stdout so we can read the "listening on" announce line
+ m_process.setProcessChannelMode(QProcess::MergedChannels);
+}
+
+bool QProcessObfuscator::start() {
+ logger.debug() << "Starting obfuscator";
+ m_process.start();
+ if (!m_process.waitForStarted(OBFUSCATOR_PROC_TIMEOUT_MS)) {
+ logger.error() << "Failed to start obfuscator process"
+ << m_process.errorString();
+ return false;
+ }
+
+ // Block until the helper either announces its port or exits
+ while (m_process.state() == QProcess::Running) {
+ if (!m_process.waitForReadyRead(OBFUSCATOR_PROC_TIMEOUT_MS)) {
+ logger.error() << "Obfuscator did not announce a listening port";
+ m_process.kill();
+ m_process.waitForFinished(OBFUSCATOR_PROC_TIMEOUT_MS);
+ return false;
+ }
+ while (m_process.canReadLine()) {
+ const QByteArray line = m_process.readLine().trimmed();
+ logger.debug() << "obf:" << QString::fromUtf8(line);
+ const quint16 port = parseListeningPort(line);
+ if (port != 0) {
+ m_localPort = port;
+ return true;
+ }
+ }
+ }
+
+ logger.error() << "Obfuscator exited before announcing a port"
+ << m_process.exitCode();
+ return false;
+}
+
+quint16 QProcessObfuscator::parseListeningPort(const QByteArray& line) const {
+ static const QByteArray prefix = "listening on 127.0.0.1:";
+ const int idx = line.indexOf(prefix);
+ if (idx < 0) {
+ return 0;
+ }
+ bool ok = false;
+ const quint16 port = line.mid(idx + prefix.size()).toUShort(&ok);
+ return ok ? port : 0;
+}
+
+QStringList QProcessObfuscator::buildArgs(const InterfaceConfig& config) {
+ QStringList args;
+ switch (config.m_obfuscationMethod) {
+ case Server::ObfuscationMethod::UdpOverTcp:
+ args << QStringLiteral("udp-over-tcp");
+ break;
+ default:
+ return {};
+ }
+
+ const QString server = !config.m_serverIpv4AddrIn.isEmpty()
+ ? config.m_serverIpv4AddrIn
+ : config.m_serverIpv6AddrIn;
+ args << QStringLiteral("--server") << server;
+ args << QStringLiteral("--port") << QString::number(config.m_serverPort);
+#ifdef MZ_LINUX
+ args << QStringLiteral("--fwmark") << QString::number(WG_FIREWALL_MARK);
+#endif
+ // The obfuscator needs to bind to the same interface as the WireGuard peer
+ return args;
+}
+
+QString QProcessObfuscator::binaryName() const {
+#if defined(MZ_WINDOWS)
+ return QStringLiteral("mozillavpn-obfuscator.exe");
+#elif defined(MZ_LINUX)
+ return QStringLiteral("mozillavpn-obfuscator");
+#else
+ logger.error() << "Obfuscation is not supported on this platform";
+ return QString();
+#endif
+}
+
+QProcessObfuscator::~QProcessObfuscator() {
+ MZ_COUNT_DTOR(QProcessObfuscator);
+ if (m_process.state() == QProcess::NotRunning) {
+ return;
+ }
+ logger.debug() << "Stopping obfuscator";
+ m_process.terminate();
+ if (!m_process.waitForFinished(OBFUSCATOR_PROC_TIMEOUT_MS)) {
+ m_process.kill();
+ m_process.waitForFinished(OBFUSCATOR_PROC_TIMEOUT_MS);
+ }
+}
diff --git a/src/daemon/obfuscator/qprocessobfuscator.h b/src/daemon/obfuscator/qprocessobfuscator.h
new file mode 100644
index 0000000000..3f633cb450
--- /dev/null
+++ b/src/daemon/obfuscator/qprocessobfuscator.h
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef QPROCESSOBFUSCATOR_H
+#define QPROCESSOBFUSCATOR_H
+
+#include
+#include
+
+#include "../utils/interfaceconfig.h"
+#include "obfuscator.h"
+
+class QProcessObfuscator final : public Obfuscator {
+ public:
+ explicit QProcessObfuscator(const InterfaceConfig& config);
+ ~QProcessObfuscator() override;
+
+ bool start() override;
+ quint16 localPort() const override { return m_localPort; }
+
+ private:
+ bool isRunning() const { return m_process.state() != QProcess::NotRunning; }
+ quint16 parseListeningPort(const QByteArray& line) const;
+ QStringList buildArgs(const InterfaceConfig& config);
+ QString binaryName() const;
+
+ QProcess m_process;
+ quint16 m_localPort = 0;
+};
+
+#endif // QPROCESSOBFUSCATOR_H
diff --git a/src/feature/featuremodel.h b/src/feature/featuremodel.h
index 791ed6b490..c465c105ab 100644
--- a/src/feature/featuremodel.h
+++ b/src/feature/featuremodel.h
@@ -34,6 +34,10 @@ inline const AnyFeature s_exposedFeatures[] = {
ref(multiHop),
ref(networkExtension),
ref(notificationControl),
+ ref(obfuscationLwo),
+ ref(obfuscationMasque),
+ ref(obfuscationShadowsocks),
+ ref(obfuscationUdpOverTcp),
ref(recommendedServers),
ref(replacerAddon),
ref(serverUnavailableNotification),
diff --git a/src/feature/features.h b/src/feature/features.h
index b67fabb362..936f09f5a0 100644
--- a/src/feature/features.h
+++ b/src/feature/features.h
@@ -263,6 +263,30 @@ inline constexpr ConstantFeature freeTrial = {
.supported = false,
};
+inline constexpr ConstantFeature obfuscationLwo = {
+ .id = "obfuscationLwo",
+ .name = "LWO obfuscation",
+ .supported = false,
+};
+
+inline constexpr ConstantFeature obfuscationMasque = {
+ .id = "obfuscationMasque",
+ .name = "MASQUE obfuscation",
+ .supported = false,
+};
+
+inline constexpr ConstantFeature obfuscationShadowsocks = {
+ .id = "obfuscationShadowsocks",
+ .name = "SHADOWSOCKS obfuscation",
+ .supported = false,
+};
+
+inline constexpr ConstantFeature obfuscationUdpOverTcp = {
+ .id = "obfuscationUdpOverTcp",
+ .name = "UDP over TCP",
+ .supported = Platform::linux_ || Platform::android,
+};
+
inline const OverridableFeature replacerAddon = {
.id = "replacerAddon",
.name = "Replacer Addon",
diff --git a/src/platforms/android/androidcontroller.cpp b/src/platforms/android/androidcontroller.cpp
index a2ab1b0cdf..dc68e18a22 100644
--- a/src/platforms/android/androidcontroller.cpp
+++ b/src/platforms/android/androidcontroller.cpp
@@ -204,6 +204,7 @@ void AndroidController::activate(const InterfaceConfig& config,
args["allowedIPs"] = jAllowedIPs;
args["excludedApps"] = excludedApps;
args["dns"] = config.m_dnsServer;
+ args["obfuscationMethod"] = (int)config.m_obfuscationMethod;
// Build the "canned" Notification messages
// They will be used in case this config will be re-enabled
diff --git a/src/platforms/linux/daemon/dbusservice.cpp b/src/platforms/linux/daemon/dbusservice.cpp
index f07dd4be60..f2c595bff0 100644
--- a/src/platforms/linux/daemon/dbusservice.cpp
+++ b/src/platforms/linux/daemon/dbusservice.cpp
@@ -293,3 +293,15 @@ bool DBusService::isCallerAuthorized(const QString& actionId) {
logger.warning() << "Polkit authorization denied";
return false;
}
+
+void DBusService::markObfuscatorSockets(int v4, int v6) {
+ constexpr uint32_t WG_FIREWALL_MARK = 0xca6c;
+ for (int fd : {v4, v6}) {
+ if (fd < 0) continue;
+ if (::setsockopt(fd, SOL_SOCKET, SO_MARK, &WG_FIREWALL_MARK,
+ sizeof(WG_FIREWALL_MARK)) < 0) {
+ logger.warning() << "Failed to mark obfuscator socket:"
+ << strerror(errno);
+ }
+ }
+}
diff --git a/src/platforms/linux/daemon/dbusservice.h b/src/platforms/linux/daemon/dbusservice.h
index a2a022ca76..17506b3e97 100644
--- a/src/platforms/linux/daemon/dbusservice.h
+++ b/src/platforms/linux/daemon/dbusservice.h
@@ -43,6 +43,7 @@ class DBusService final : public Daemon, protected QDBusContext {
WireguardUtils* wgutils() const override { return m_wgutils; }
IPUtils* iputils() override;
DnsUtils* dnsutils() override;
+ void markObfuscatorSockets(int v4, int v6);
private:
bool removeInterfaceIfExists();
diff --git a/src/settingsholder.h b/src/settingsholder.h
index 7aa0d12f3d..e9d75476fb 100644
--- a/src/settingsholder.h
+++ b/src/settingsholder.h
@@ -47,6 +47,17 @@ class SettingsHolder final : public QObject {
};
Q_ENUM(DNSProviderFlags)
+ // Obfuscation methods enum, remember to keep in sync with the one in server.h
+ // and in the obfuscators crate
+ enum ObfuscationMethod {
+ NoObfuscation,
+ LWO,
+ Masque,
+ UdpOverTcp,
+ Shadowsocks,
+ };
+ Q_ENUM(ObfuscationMethod)
+
#define SETTING(type, toType, getter, setter, remover, has, ...) \
bool has() const { return m_##getter->isSet(); } \
type getter() const { return m_##getter->get().toType(); } \
diff --git a/src/settingslist.h b/src/settingslist.h
index 3033f500e2..d06f3e3538 100644
--- a/src/settingslist.h
+++ b/src/settingslist.h
@@ -315,6 +315,16 @@ SETTING_STRINGLIST(missingApps, // getter
false // sensitive (do not log)
)
+SETTING_INT(obfuscationMethod, // getter
+ setObfuscationMethod, // setter
+ removeObfuscationMethod, // remover
+ hasObfuscationMethod, // has
+ "obfuscationMethod", // key
+ SettingsHolder::ObfuscationMethod::NoObfuscation, // default value
+ false, // remove when reset
+ false // sensitive (do not log)
+)
+
SETTING_BOOL(onboardingCompleted, // getter
setOnboardingCompleted, // setter
removeOnboardingCompleted, // remover
diff --git a/src/translations/strings.yaml b/src/translations/strings.yaml
index 1f7c4f3bc1..08c5f46199 100644
--- a/src/translations/strings.yaml
+++ b/src/translations/strings.yaml
@@ -329,6 +329,18 @@ systray:
settings:
privacySettings: Privacy features
+ antiCensorshipSettings: Anti-Censorship features
+ antiCensorshipSettingsWarning: These options may cause connection stability issues.
+ antiCensorshipPort53Title: Connect using port 53
+ antiCensorshipPort53Body: This may help on networks that block ports or UDP traffic.
+ antiCensorshipMasqueTitle: MASQUE
+ antiCensorshipMasqueBody: This may help on networks that block VPN protocols by disguising VPN traffic as regular HTTPS traffic.
+ antiCensorshipLwoTitle: Lightweight obfuscation
+ antiCensorshipLwoBody: The fastest obfuscation method. May help on networks that detect or block WireGuard traffic without adding significant overhead.
+ antiCensorshipShadowsocksTitle: SHADOWSOCKS
+ antiCensorshipShadowsocksBody: A reliable censorship circumvention protocol that may help bypass restrictive or heavily filtered networks.
+ antiCensorshipUdpOverTcpTitle: UDP over TCP
+ antiCensorshipUdpOverTcpBody: This may help on networks that block UDP traffic by tunneling it over TCP.
appExclusionTitle: Excluded apps
appearance:
value: Appearance
@@ -880,6 +892,18 @@ devices:
comment: Title for the menu bar when viewing the devices or device limit pages
helpSheets:
+ antiCensorshipTitle:
+ value: Anti-Censorship features
+ comment: Title label for the Anti-Censorship features help sheet
+ antiCensorshipHeader:
+ value: What can Anti-Censorship features do for me?
+ comment: Header label for the Anti-Censorship features help sheet
+ antiCensorshipBody1:
+ value: Anti-Censorship features can help when your provider or network blocks VPN or UDP traffic.
+ comment: Body label for the Anti-Censorship features help sheet
+ antiCensorshipBody2:
+ value: These features may slow down your connection, so turn them off when you no longer need them.
+ comment: Body label for the Anti-Censorship features help sheet
dnsTitle:
value: Custom DNS settings
comment: Title label for the custom dns help sheet
@@ -1084,6 +1108,9 @@ controller:
regeneratingKey:
value: Preparing your connection
comment: Connection subtitle label for the main screen when the app is regenerating WireGuard keys
+ antiCensorshipOn:
+ value: Anti-Censorship is on
+ comment: Label for the main screen when Anti-Censorship features are enabled
iosAppIntentsMain:
turnOnAction:
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index 275580e367..7258280fa6 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -105,6 +105,8 @@ qt_add_qml_module(mozillavpn-ui
screens/settings/ViewSettingsMenu.qml
screens/settings/privacy/ViewPrivacy.qml
screens/settings/privacy/PrivacyFeaturesList.qml
+ screens/settings/antiCensorship/ViewAntiCensorship.qml
+ screens/settings/antiCensorship/AntiCensorshipFeaturesList.qml
screens/subscriptionNeeded/ViewSubscriptionNeeded.qml
sharedViews/ViewErrorFullScreen.qml
sharedViews/ViewPermissionRequiredOSX.qml
diff --git a/src/ui/screens/home/controller/ControllerView.qml b/src/ui/screens/home/controller/ControllerView.qml
index c1dc08091c..4e03424377 100644
--- a/src/ui/screens/home/controller/ControllerView.qml
+++ b/src/ui/screens/home/controller/ControllerView.qml
@@ -634,6 +634,20 @@ Item {
implicitHeight: childrenRect.height
}
+ MZInterLabel {
+ id: obfuscationMethodDescription
+ objectName: "obfuscationMethodDescription"
+ anchors.horizontalCenter: parent.horizontalCenter
+ opacity: 0.8
+ color: MZTheme.colors.fontColorInverted
+ lineHeight: MZTheme.theme.controllerInterLineHeight
+ visible: VPNController.state === VPNController.StateOn &&
+ MZSettings.obfuscationMethod !== MZSettings.NoObfuscation
+ Accessible.ignored: ipInfoPanel.isOpen || !visible
+
+ text: MZI18n.ControllerAntiCensorshipOn
+ }
+
}
VPNToggle {
diff --git a/src/ui/screens/settings/ViewSettingsMenu.qml b/src/ui/screens/settings/ViewSettingsMenu.qml
index 0caf1f0d1f..fc61b1f2f0 100644
--- a/src/ui/screens/settings/ViewSettingsMenu.qml
+++ b/src/ui/screens/settings/ViewSettingsMenu.qml
@@ -74,6 +74,17 @@ MZViewBase {
}
}
+ MZSettingsItem {
+ objectName: "antiCensorshipSettings"
+ settingTitle: MZI18n.SettingsAntiCensorshipSettings
+ imageLeftSrc: MZAssetLookup.getImageSource("EyeHidden")
+ imageRightSrc: MZAssetLookup.getImageSource("Chevron")
+ imageRightMirror: MZLocalizer.isRightToLeft
+ onClicked: {
+ stackview.push("qrc:/qt/qml/Mozilla/VPN/screens/settings/antiCensorship/ViewAntiCensorship.qml")
+ }
+ }
+
MZSettingsItem {
objectName: "appExclusionSettings"
settingTitle: MZI18n.SettingsAppExclusionTitle
diff --git a/src/ui/screens/settings/antiCensorship/AntiCensorshipFeaturesList.qml b/src/ui/screens/settings/antiCensorship/AntiCensorshipFeaturesList.qml
new file mode 100644
index 0000000000..e795e117d8
--- /dev/null
+++ b/src/ui/screens/settings/antiCensorship/AntiCensorshipFeaturesList.qml
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.15
+import QtQuick.Layouts 1.15
+
+import Mozilla.Shared 1.0
+import components 0.1
+
+ColumnLayout {
+ id: root
+
+ property int dividerSpacing: MZTheme.theme.toggleRowDividerSpacing
+
+
+ spacing: dividerSpacing
+ MZToggleRow {
+ objectName: "alwaysUsePort53"
+
+ labelText: MZI18n.SettingsAntiCensorshipPort53Title
+ subLabelText: MZI18n.SettingsAntiCensorshipPort53Body
+ checked: MZFeatureList.get("alwaysPort53").isSupported
+ dividerTopMargin: dividerSpacing
+ onClicked: {
+ MZFeatureList.toggle("alwaysPort53");
+ }
+ }
+
+ Repeater {
+ id: repeater
+
+ model: [
+ {
+ objectName: "obfuscationUdpOverTcp",
+ settingValue: MZSettings.UdpOverTcp,
+ settingTitle: MZI18n.SettingsAntiCensorshipUdpOverTcpTitle,
+ settingDescription: MZI18n.SettingsAntiCensorshipUdpOverTcpBody,
+ }, {
+ objectName: "obfuscationLwo",
+ settingValue: MZSettings.LWO,
+ settingTitle: MZI18n.SettingsAntiCensorshipLwoTitle,
+ settingDescription: MZI18n.SettingsAntiCensorshipLwoBody,
+ }, {
+ objectName: "obfuscationMasque",
+ settingValue: MZSettings.Masque,
+ settingTitle: MZI18n.SettingsAntiCensorshipMasqueTitle,
+ settingDescription: MZI18n.SettingsAntiCensorshipMasqueBody,
+ },{
+ objectName: "obfuscationShadowsocks",
+ settingValue: MZSettings.Shadowsocks,
+ settingTitle: MZI18n.SettingsAntiCensorshipShadowsocksTitle,
+ settingDescription: MZI18n.SettingsAntiCensorshipShadowsocksBody,
+ }
+ ];
+
+ delegate: MZToggleRow {
+ objectName: modelData.objectName
+
+ labelText: modelData.settingTitle
+ subLabelText: modelData.settingDescription
+ checked: MZSettings.obfuscationMethod == modelData.settingValue
+ dividerTopMargin: dividerSpacing
+ visible: MZFeatureList.get(modelData.objectName).isSupported
+ showDivider: !isOnboarding || index + 1 !== repeater.model.length
+
+ onClicked: {
+ if(MZSettings.obfuscationMethod == modelData.settingValue) {
+ MZSettings.obfuscationMethod = MZSettings.NoObfuscation;
+ } else {
+ MZSettings.obfuscationMethod = modelData.settingValue;
+ // Maybe automatically disable alwaysPort53 when enabling an obfuscation method
+ // that requires a specific port. Right now the port it's overridden in the controller.
+ }
+ }
+ }
+ }
+}
diff --git a/src/ui/screens/settings/antiCensorship/ViewAntiCensorship.qml b/src/ui/screens/settings/antiCensorship/ViewAntiCensorship.qml
new file mode 100644
index 0000000000..41c81f68dd
--- /dev/null
+++ b/src/ui/screens/settings/antiCensorship/ViewAntiCensorship.qml
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import QtQuick.Layouts 1.15
+
+import Mozilla.Shared 1.0
+import Mozilla.VPN 1.0
+import components 0.1
+import components.forms 0.1
+import compat 0.1
+import "qrc:/nebula/utils/MZAssetLookup.js" as MZAssetLookup
+
+MZViewBase {
+ id: root
+ objectName: "antiCensorshipSettingsView"
+
+ property Component rightMenuButton: Component {
+ Loader {
+ active: true
+ sourceComponent: MZIconButton {
+ objectName: "antiCensorshipHelpButton"
+
+ onClicked: {
+ helpSheet.open()
+ }
+
+ accessibleName: MZI18n.GetHelpLinkText
+
+ Image {
+ anchors.centerIn: parent
+
+ source: MZAssetLookup.getImageSource("Question")
+ fillMode: Image.PreserveAspectFit
+ }
+ }
+ }
+ }
+
+ _menuTitle: MZI18n.SettingsAntiCensorshipSettings
+
+ _viewContentData: ColumnLayout {
+ spacing: MZTheme.theme.windowMargin * 1.5
+ Layout.preferredWidth: parent.width
+
+ MZInformationCard {
+ objectName: "antiCensorshipSettingsViewInformationCard"
+ cardType: MZInformationCard.CardType.Warning
+ Layout.preferredWidth: Math.min(window.width - MZTheme.theme.windowMargin * 2, MZTheme.theme.navBarMaxWidth)
+ Layout.alignment: Qt.AlignHCenter
+
+ _infoContent: ColumnLayout {
+ id: textBlocks
+ spacing: 0
+
+ MZTextBlock {
+ Layout.fillWidth: true
+ width: undefined
+ text: MZI18n.SettingsAntiCensorshipSettingsWarning
+ verticalAlignment: Text.AlignVCenter
+ }
+ Loader {
+ active: !VPNController.silentServerSwitchingSupported && VPNController.state !== VPNController.StateOff
+ Layout.fillWidth: true
+ visible: active
+ sourceComponent: MZTextBlock {
+ width: parent.width
+ text: MZI18n.SettingsDnsSettingsDisconnectWarning
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+ }
+ }
+
+ AntiCensorshipFeaturesList {
+ id: antiCensorshipFeaturesList
+ Layout.leftMargin: 16
+ Layout.rightMargin: 16
+ Layout.fillWidth: true
+ }
+ }
+
+ MZHelpSheet {
+ id: helpSheet
+ objectName: "antiCensorshipHelpSheet"
+
+ title: MZI18n.HelpSheetsAntiCensorshipTitle
+
+ model: [
+ {type: MZHelpSheet.BlockType.Title, text: MZI18n.HelpSheetsAntiCensorshipHeader},
+ {type: MZHelpSheet.BlockType.Text, text: MZI18n.HelpSheetsAntiCensorshipBody1, margin: MZTheme.theme.helpSheetTitleBodySpacing},
+ {type: MZHelpSheet.BlockType.Text, text: MZI18n.HelpSheetsAntiCensorshipBody2, margin: MZTheme.theme.helpSheetBodySpacing}
+ ]
+ }
+}
diff --git a/src/utils/interfaceconfig.cpp b/src/utils/interfaceconfig.cpp
index e4bf155bd1..209b67bb14 100644
--- a/src/utils/interfaceconfig.cpp
+++ b/src/utils/interfaceconfig.cpp
@@ -16,6 +16,7 @@ QJsonObject InterfaceConfig::toJson() const {
json.insert("hopType", QJsonValue(metaEnum.valueToKey(m_hopType)));
json.insert("privateKey", QJsonValue(m_privateKey));
+ json.insert("publicKey", QJsonValue(m_publicKey));
json.insert("deviceIpv4Address", QJsonValue(m_deviceIpv4Address));
json.insert("deviceIpv6Address", QJsonValue(m_deviceIpv6Address));
json.insert("serverPublicKey", QJsonValue(m_serverPublicKey));
@@ -46,6 +47,11 @@ QJsonObject InterfaceConfig::toJson() const {
}
json.insert("vpnDisabledApps", disabledApps);
+ QMetaEnum obfuscationMetaEnum =
+ QMetaEnum::fromType();
+ json.insert("obfuscationMethod",
+ QJsonValue(obfuscationMetaEnum.valueToKey(m_obfuscationMethod)));
+
return json;
}
diff --git a/src/utils/interfaceconfig.h b/src/utils/interfaceconfig.h
index 6a7d53a76d..a4e38c235d 100644
--- a/src/utils/interfaceconfig.h
+++ b/src/utils/interfaceconfig.h
@@ -10,6 +10,7 @@
#include
#include "ipaddress.h"
+#include "models/server.h"
class QJsonObject;
@@ -24,6 +25,7 @@ class InterfaceConfig {
HopType m_hopType;
QString m_privateKey;
+ QString m_publicKey;
QString m_deviceIpv4Address;
QString m_deviceIpv6Address;
QString m_serverIpv4Gateway;
@@ -37,6 +39,7 @@ class InterfaceConfig {
int m_serverPort = 0;
QList m_allowedIPAddressRanges;
QStringList m_vpnDisabledApps;
+ Server::ObfuscationMethod m_obfuscationMethod;
QJsonObject toJson() const;
QString toWgConf(
diff --git a/src/utils/models/server.cpp b/src/utils/models/server.cpp
index 7a90b5e73f..1d620e64fa 100644
--- a/src/utils/models/server.cpp
+++ b/src/utils/models/server.cpp
@@ -148,6 +148,26 @@ bool Server::forcePort(uint32_t port) {
return true;
}
+bool Server::supportObfuscationMethod(const ObfuscationMethod method) const {
+ switch (method) {
+ case NoObfuscation:
+ [[fallthrough]];
+ case UdpOverTcp:
+ return true;
+ case LWO:
+ [[fallthrough]];
+ case Masque:
+ [[fallthrough]];
+ case Shadowsocks:
+ // Shadowsocks currently falls under false as it looks like it's not
+ // supported by the server itself but we should communicate to the server
+ // through a relay and this requires changes in the guardian api
+ [[fallthrough]];
+ default:
+ return false;
+ }
+}
+
// static
const Server& Server::weightChooser(const QList& servers) {
static const Server emptyServer;
@@ -177,14 +197,16 @@ const Server& Server::weightChooser(const QList& servers) {
return emptyServer;
}
-uint32_t Server::choosePort() const {
- if (m_portRanges.isEmpty()) {
+uint32_t Server::choosePort(bool tcp) const {
+ QList> portRanges =
+ tcp ? m_udpOverTcpPorts : m_portRanges;
+ if (portRanges.isEmpty()) {
return 0;
}
// Count the total number of potential ports.
quint32 length = 0;
- for (const QPair& range : m_portRanges) {
+ for (const QPair& range : portRanges) {
Q_ASSERT(range.first <= range.second);
length += range.second - range.first + 1;
}
@@ -195,7 +217,7 @@ uint32_t Server::choosePort() const {
quint32 r = QRandomGenerator::global()->generate() % length;
quint32 port = 0;
- for (const QPair& range : m_portRanges) {
+ for (const QPair& range : portRanges) {
if (r <= (range.second - range.first)) {
port = r + range.first;
break;
diff --git a/src/utils/models/server.h b/src/utils/models/server.h
index 04e8faffe0..0e0c8959c6 100644
--- a/src/utils/models/server.h
+++ b/src/utils/models/server.h
@@ -6,12 +6,15 @@
#define SERVER_H
#include
+#include
#include
#include
class QJsonObject;
-class Server final {
+class Server final : public QObject {
+ Q_OBJECT
+
public:
Server();
Server(const QString& countryCode, const QString& cityName);
@@ -19,6 +22,17 @@ class Server final {
Server& operator=(const Server& other);
~Server();
+ // Obfuscation methods enum, remember to keep in sync with the one in
+ // settingsholder.h and in the obfuscators crate
+ enum ObfuscationMethod {
+ NoObfuscation,
+ LWO,
+ Masque,
+ UdpOverTcp,
+ Shadowsocks
+ };
+ Q_ENUM(ObfuscationMethod)
+
[[nodiscard]] bool fromJson(const QJsonObject& obj);
bool fromMultihop(const Server& exit, const Server& entry);
@@ -26,6 +40,8 @@ class Server final {
bool initialized() const { return !m_hostname.isEmpty(); }
+ bool supportObfuscationMethod(const ObfuscationMethod method) const;
+
const QString& hostname() const { return m_hostname; }
const QString& ipv4AddrIn() const { return m_ipv4AddrIn; }
@@ -42,7 +58,9 @@ class Server final {
uint32_t weight() const { return m_weight; }
- uint32_t choosePort() const;
+ uint32_t choosePort(bool tcp = false) const;
+
+ uint32_t chooseTcpPort() const { return choosePort(true); }
uint32_t multihopPort() const { return m_multihopPort; }
@@ -68,6 +86,11 @@ class Server final {
QString m_ipv6AddrIn;
QString m_ipv6Gateway;
QList> m_portRanges;
+ // Mullvad ports for UDP over TCP obfuscation, hardcoded as specified
+ // Use QPair so we can reuse choosePort logic, maybe expose these ports in
+ // guardian APIs in the future
+ QList> m_udpOverTcpPorts{
+ {80, 80}, {443, 443}, {5001, 5001}};
QString m_publicKey;
QString m_socksName;
uint32_t m_weight = 0;
diff --git a/taskcluster/docker/base/Dockerfile b/taskcluster/docker/base/Dockerfile
index 11eab150b3..6dc09c9f42 100644
--- a/taskcluster/docker/base/Dockerfile
+++ b/taskcluster/docker/base/Dockerfile
@@ -40,13 +40,15 @@ RUN apt-get update -qq \
git \
wget \
zip \
- cargo \
+ cargo-1.91 \
curl \
rpm \
libglib2.0-0 \
ccache \
&& apt-get clean
+RUN ln -s /usr/bin/cargo-1.91 /usr/bin/cargo
+
RUN pip install --upgrade pip
RUN pip install taskcluster
diff --git a/taskcluster/docker/linux-dpkg-build/Dockerfile b/taskcluster/docker/linux-dpkg-build/Dockerfile
index 8784e60ade..bcbba0740e 100644
--- a/taskcluster/docker/linux-dpkg-build/Dockerfile
+++ b/taskcluster/docker/linux-dpkg-build/Dockerfile
@@ -50,7 +50,7 @@ RUN /root/setup-worker.sh
WORKDIR /builds/worker/
# Grant the worker user the ability to install packages without login
-RUN echo "worker ALL=(ALL) SETENV:NOPASSWD:/usr/bin/apt,/usr/bin/apt-get,/usr/bin/dpkg,/usr/bin/mk-build-deps" > /etc/sudoers.d/worker-packages
+RUN echo "worker ALL=(ALL) SETENV:NOPASSWD:/usr/bin/apt,/usr/bin/apt-get,/usr/bin/dpkg,/usr/bin/mk-build-deps,/usr/bin/ln" > /etc/sudoers.d/worker-packages
#----------------------------------------------------------------------------------------------------------------------
#-- Task Setup --------------------------------------------------------------------------------------------------------
diff --git a/taskcluster/docker/linux-qt6-build/Dockerfile b/taskcluster/docker/linux-qt6-build/Dockerfile
index f12661388e..f4d7c0d47d 100644
--- a/taskcluster/docker/linux-qt6-build/Dockerfile
+++ b/taskcluster/docker/linux-qt6-build/Dockerfile
@@ -29,6 +29,7 @@ RUN apt-get -y install build-essential \
debhelper \
devscripts \
equivs \
+ gawk \
libclang-16-dev \
libgl1-mesa-dev \
locales \
@@ -89,21 +90,23 @@ RUN apt -y install libwayland-dev:${DEB_HOST_ARCH} \
# Also pull in packages from build-depends as early as possible
# %include linux/debian/control
ADD topsrcdir/linux/debian/control /root/mozillavpn-dpkg-control
-RUN sed -rie '/\s+(qt6-|qml6-|libqt6|qmake)/d' /root/mozillavpn-dpkg-control && \
- mk-build-deps -i -r --tool="apt-get --no-install-recommends --yes" --host-arch $DEB_HOST_ARCH /root/mozillavpn-dpkg-control
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo
+# Install rust toolchain using rustup, cargo version is parsed from control file
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
- sh -s -- -y --default-toolchain $(rustc --version | awk '{print $2}') --profile minimal --no-modify-path
+ sh -s -- -y --default-toolchain $(gawk 'match($0, /cargo-([0-9.]+):native/, a) { print a[1] }' /root/mozillavpn-dpkg-control) --profile minimal --no-modify-path
ENV PATH="/usr/local/cargo/bin:${PATH}"
RUN if [ "$DEB_HOST_ARCH" != "amd64" ]; then \
- rustup target add --toolchain $(rustc --version | awk '{print $2}') ${DEB_HOST_GNU_CPU}-unknown-linux-gnu; \
+ rustup target add ${DEB_HOST_GNU_CPU}-unknown-linux-gnu; \
fi
+RUN sed -rie '/\s+(qt6-|qml6-|libqt6|qmake|cargo)/d' /root/mozillavpn-dpkg-control && \
+ mk-build-deps -i -r --tool="apt-get --no-install-recommends --yes" --host-arch $DEB_HOST_ARCH /root/mozillavpn-dpkg-control
+
#----------------------------------------------------------------------------------------------------------------------
#-- Worker User -------------------------------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------------------------------------
@@ -115,7 +118,7 @@ RUN /root/setup-worker.sh
WORKDIR /builds/worker/
# Grant the worker user the ability to install packages without login
-RUN echo "worker ALL=(ALL) SETENV:NOPASSWD:/usr/bin/apt,/usr/bin/apt-get,/usr/bin/dpkg,/usr/bin/mk-build-deps" > /etc/sudoers.d/worker-packages
+RUN echo "worker ALL=(ALL) SETENV:NOPASSWD:/usr/bin/apt,/usr/bin/apt-get,/usr/bin/dpkg,/usr/bin/mk-build-deps,/usr/bin/ln" > /etc/sudoers.d/worker-packages
#----------------------------------------------------------------------------------------------------------------------
#-- Task Setup --------------------------------------------------------------------------------------------------------
diff --git a/taskcluster/scripts/build/linux_build_dpkg.sh b/taskcluster/scripts/build/linux_build_dpkg.sh
index a67f238fc4..cd89f1d559 100755
--- a/taskcluster/scripts/build/linux_build_dpkg.sh
+++ b/taskcluster/scripts/build/linux_build_dpkg.sh
@@ -102,8 +102,9 @@ dch -c "$(pwd)/mozillavpn-source/debian/changelog" \
-D "${DIST}" --force-distribution "Release for ${DIST}"
# For static Qt or cross builds, strip out the Qt build and runtime dependencies.
+# Also strip out the rust toolchain as it's installed in the docker image
if [[ "$STATICQT" == "Y" ]]; then
- sed -rie '/\s+(qt6-|qml6-|libqt6|qmake)/d' "$(pwd)/mozillavpn-source/debian/control"
+ sed -rie '/\s+(qt6-|qml6-|libqt6|qmake|cargo)/d' "$(pwd)/mozillavpn-source/debian/control"
fi
# Set up cross-compilation environment
@@ -132,6 +133,17 @@ fi
sudo mk-build-deps --tool 'apt-get -y -o Debug::pkgProblemResolver=yes --no-install-recommends' \
--install --remove $MK_BUILD_DEPS_ARGS "$(pwd)/mozillavpn-source/debian/control"
+# /usr/bin/cargo and /usr/bin/rustc may be installed as cargo- and rustc-
+# Find the latest versions of these binaries as installed by the build dependencies and link them to /usr/bin.
+
+if ! command -v cargo >/dev/null 2>&1; then
+ CARGO_BIN=$(find -L /usr/bin -maxdepth 1 -type f -name 'cargo-*' | sort -V | tail -n1)
+ RUSTC_BIN=$(find -L /usr/bin -maxdepth 1 -type f -name 'rustc-*' | sort -V | tail -n1)
+
+ [ -n "$CARGO_BIN" ] && sudo ln -sf "$CARGO_BIN" /usr/bin/cargo
+ [ -n "$RUSTC_BIN" ] && sudo ln -sf "$RUSTC_BIN" /usr/bin/rustc
+fi
+
(cd mozillavpn-source/ && dpkg-buildpackage $DPKG_PACKAGE_BUILD_ARGS)
# Compress all build artifacts into a tarball