From 876edfcbb95b9cd13feec3b8f638407cfe56c96d Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Wed, 3 May 2023 20:56:53 +0200 Subject: [PATCH 1/9] send message to grpc tunnel --- Cargo.lock | 672 +++++++++++++++++- Cargo.toml | 3 + .../org/signal/libsignal/internal/Native.java | 2 + rust/bridge/ffi/src/util.rs | 1 + rust/bridge/shared/Cargo.toml | 1 + rust/bridge/shared/src/ffi/convert.rs | 7 + rust/bridge/shared/src/ffi/error.rs | 11 + rust/bridge/shared/src/grpc.rs | 16 + rust/bridge/shared/src/jni/convert.rs | 10 + rust/bridge/shared/src/jni/error.rs | 9 + rust/bridge/shared/src/jni/mod.rs | 3 +- rust/bridge/shared/src/lib.rs | 2 + rust/grpc/Cargo.toml | 20 + rust/grpc/build.rs | 18 + rust/grpc/src/client.rs | 34 + rust/grpc/src/error.rs | 15 + rust/grpc/src/lib.rs | 12 + rust/grpc/src/proto.rs | 6 + rust/grpc/src/proto/proxy.proto | 24 + rust/grpc/src/proto/proxy.rs | 8 + 20 files changed, 855 insertions(+), 19 deletions(-) create mode 100644 rust/bridge/shared/src/grpc.rs create mode 100644 rust/grpc/Cargo.toml create mode 100644 rust/grpc/build.rs create mode 100644 rust/grpc/src/client.rs create mode 100644 rust/grpc/src/error.rs create mode 100644 rust/grpc/src/lib.rs create mode 100644 rust/grpc/src/proto.rs create mode 100644 rust/grpc/src/proto/proxy.proto create mode 100644 rust/grpc/src/proto/proxy.rs diff --git a/Cargo.lock b/Cargo.lock index 889a897410..a9beb7507e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,13 +129,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.52" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -155,8 +155,8 @@ dependencies = [ "libc", "log", "num_enum", - "prost", - "prost-build", + "prost 0.9.0", + "prost-build 0.9.0", "rand_core 0.6.4", "serde", "serde_json", @@ -185,6 +185,51 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39" +dependencies = [ + "async-trait", + "axum-core", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.64" @@ -206,6 +251,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.6.0" @@ -750,6 +801,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.21" @@ -767,6 +827,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + [[package]] name = "futures-task" version = "0.3.21" @@ -841,6 +907,25 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "h2" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "1.8.2" @@ -862,6 +947,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -906,6 +997,76 @@ dependencies = [ "digest 0.9.0", ] +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + [[package]] name = "indexmap" version = "1.8.0" @@ -936,9 +1097,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jni" @@ -983,9 +1144,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.121" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "libloading" @@ -1031,6 +1192,7 @@ dependencies = [ "serde", "sha2", "signal-crypto", + "signal-grpc", "signal-neon-futures", "signal-pin", "static_assertions", @@ -1043,7 +1205,7 @@ dependencies = [ name = "libsignal-bridge-macros" version = "0.1.0" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2", "quote", "syn 1.0.109", @@ -1121,8 +1283,8 @@ dependencies = [ "itertools", "log", "num_enum", - "prost", - "prost-build", + "prost 0.9.0", + "prost-build 0.9.0", "rand 0.7.3", "sha2", "subtle", @@ -1170,6 +1332,12 @@ dependencies = [ "log", ] +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + [[package]] name = "memchr" version = "2.4.1" @@ -1185,6 +1353,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1201,6 +1375,18 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + [[package]] name = "multimap" version = "0.8.3" @@ -1317,6 +1503,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + [[package]] name = "oorandom" version = "11.1.3" @@ -1358,6 +1550,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + [[package]] name = "pest" version = "2.1.3" @@ -1377,6 +1575,26 @@ dependencies = [ "indexmap", ] +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "pin-project-lite" version = "0.2.8" @@ -1456,6 +1674,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + [[package]] name = "proc-macro-crate" version = "1.1.3" @@ -1502,7 +1730,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "444879275cb4fd84958b1a1d5420d15e6fcf7c235fe47f053c9c2a80aceb6001" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.9.0", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", ] [[package]] @@ -1512,19 +1750,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62941722fb675d463659e49c4f3fe1fe792ff24fe5bbaa9c08cd3b98a1c354f5" dependencies = [ "bytes", - "heck", + "heck 0.3.3", "itertools", "lazy_static", "log", "multimap", "petgraph", - "prost", - "prost-types", + "prost 0.9.0", + "prost-types 0.9.0", "regex", "tempfile", "which", ] +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes", + "heck 0.4.1", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost 0.11.9", + "prost-types 0.11.9", + "regex", + "syn 1.0.109", + "tempfile", + "which", +] + [[package]] name = "prost-derive" version = "0.9.0" @@ -1538,6 +1798,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "prost-types" version = "0.9.0" @@ -1545,7 +1818,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534b7a0e836e3c482d2693070f982e39e7611da9695d4d1f5a4b186b51faef0a" dependencies = [ "bytes", - "prost", + "prost 0.9.0", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost 0.11.9", ] [[package]] @@ -1730,6 +2012,12 @@ dependencies = [ "semver 0.11.0", ] +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + [[package]] name = "rusty-fork" version = "0.3.0" @@ -1877,6 +2165,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "signal-grpc" +version = "0.1.0" +dependencies = [ + "displaydoc", + "prost 0.11.9", + "thiserror", + "tonic", + "tonic-build", +] + [[package]] name = "signal-neon-futures" version = "0.1.0" @@ -1939,6 +2238,16 @@ dependencies = [ "x25519-dalek", ] +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -1984,6 +2293,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "synstructure" version = "0.12.6" @@ -2057,6 +2372,68 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tokio" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.8" @@ -2066,6 +2443,117 @@ dependencies = [ "serde", ] +[[package]] +name = "tonic" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" +dependencies = [ + "async-trait", + "axum", + "base64 0.21.0", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.11.9", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build 0.11.9", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "typenum" version = "1.15.0" @@ -2169,6 +2657,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -2181,6 +2679,12 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.79" @@ -2287,6 +2791,138 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "x25519-dalek" version = "1.1.1" @@ -2338,7 +2974,7 @@ version = "0.9.0" dependencies = [ "aead", "aes-gcm-siv", - "base64", + "base64 0.13.0", "bincode", "criterion", "curve25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index cd6ab3afa9..5a567e8d4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "rust/attest", "rust/crypto", "rust/device-transfer", + "rust/grpc", "rust/pin", "rust/poksho", "rust/protocol", @@ -16,12 +17,14 @@ members = [ default-members = [ "rust/crypto", "rust/device-transfer", + "rust/grpc", "rust/pin", "rust/poksho", "rust/protocol", "rust/usernames", "rust/zkcredential", "rust/zkgroup", + "rust/bridge/jni", ] resolver = "2" # so that our dev-dependency features don't leak into products diff --git a/java/shared/java/org/signal/libsignal/internal/Native.java b/java/shared/java/org/signal/libsignal/internal/Native.java index 96865b90c5..98a2f0d942 100644 --- a/java/shared/java/org/signal/libsignal/internal/Native.java +++ b/java/shared/java/org/signal/libsignal/internal/Native.java @@ -487,4 +487,6 @@ private Native() {} public static native void Username_Verify(byte[] proof, byte[] hash); public static native void UuidCiphertext_CheckValidContents(byte[] buffer); + + public static native byte[] Grpc_SendMessage(String method, String urlFragment, byte[] body); } diff --git a/rust/bridge/ffi/src/util.rs b/rust/bridge/ffi/src/util.rs index be8ee61e10..1a16cf9f07 100644 --- a/rust/bridge/ffi/src/util.rs +++ b/rust/bridge/ffi/src/util.rs @@ -72,6 +72,7 @@ impl From<&SignalFfiError> for SignalErrorCode { SignalFfiError::UnexpectedPanic(_) | SignalFfiError::DeviceTransfer(DeviceTransferError::InternalError(_)) + | SignalFfiError::Grpc(_) | SignalFfiError::Signal(SignalProtocolError::FfiBindingError(_)) => { SignalErrorCode::InternalError } diff --git a/rust/bridge/shared/Cargo.toml b/rust/bridge/shared/Cargo.toml index 580cf27005..45269895b5 100644 --- a/rust/bridge/shared/Cargo.toml +++ b/rust/bridge/shared/Cargo.toml @@ -19,6 +19,7 @@ zkgroup = { path = "../../zkgroup" } usernames = { path = "../../usernames" } signal-pin = { path = "../../pin" } libsignal-bridge-macros = { path = "macros" } +signal-grpc = { path = "../../grpc" } aes-gcm-siv = "0.10.1" async-trait = "0.1.41" bincode = "1.0" diff --git a/rust/bridge/shared/src/ffi/convert.rs b/rust/bridge/shared/src/ffi/convert.rs index 50c18571ef..cd41c8ff81 100644 --- a/rust/bridge/shared/src/ffi/convert.rs +++ b/rust/bridge/shared/src/ffi/convert.rs @@ -295,6 +295,13 @@ impl ResultTypeInfo for Result { } } +impl ResultTypeInfo for Result { + type ResultType = T::ResultType; + fn convert_into(self) -> SignalFfiResult { + T::convert_into(self?) + } +} + impl ResultTypeInfo for Result { type ResultType = T::ResultType; fn convert_into(self) -> SignalFfiResult { diff --git a/rust/bridge/shared/src/ffi/error.rs b/rust/bridge/shared/src/ffi/error.rs index 3865648585..57094c36ad 100644 --- a/rust/bridge/shared/src/ffi/error.rs +++ b/rust/bridge/shared/src/ffi/error.rs @@ -9,6 +9,7 @@ use std::fmt; use attest::hsm_enclave::Error as HsmEnclaveError; use attest::sgx_session::Error as SgxError; use device_transfer::Error as DeviceTransferError; +use signal_grpc::Error as GrpcError; use libsignal_protocol::*; use signal_crypto::Error as SignalCryptoError; use signal_pin::Error as PinError; @@ -24,6 +25,7 @@ use super::NullPointerError; pub enum SignalFfiError { Signal(SignalProtocolError), DeviceTransfer(DeviceTransferError), + Grpc(GrpcError), HsmEnclave(HsmEnclaveError), Sgx(SgxError), Pin(PinError), @@ -43,6 +45,9 @@ impl fmt::Display for SignalFfiError { SignalFfiError::DeviceTransfer(c) => { write!(f, "Device transfer operation failed: {}", c) } + SignalFfiError::Grpc(e) => { + write!(f, "Grpc operation failed: {}", e) + } SignalFfiError::HsmEnclave(e) => { write!(f, "HSM enclave operation failed: {}", e) } @@ -77,6 +82,12 @@ impl From for SignalFfiError { } } +impl From for SignalFfiError { + fn from(e: GrpcError) -> SignalFfiError { + SignalFfiError::Grpc(e) + } +} + impl From for SignalFfiError { fn from(e: HsmEnclaveError) -> SignalFfiError { SignalFfiError::HsmEnclave(e) diff --git a/rust/bridge/shared/src/grpc.rs b/rust/bridge/shared/src/grpc.rs new file mode 100644 index 0000000000..838ec8ba63 --- /dev/null +++ b/rust/bridge/shared/src/grpc.rs @@ -0,0 +1,16 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +use ::signal_grpc::GrpcClient; +use ::signal_grpc::Result; +use libsignal_bridge_macros::*; + +use crate::support::*; +use crate::*; + +#[bridge_fn] +pub fn Grpc_SendMessage(method: String, url_fragment: String, body: &[u8]) -> Result> { + GrpcClient::new().send_message(method, url_fragment, body).await +} diff --git a/rust/bridge/shared/src/jni/convert.rs b/rust/bridge/shared/src/jni/convert.rs index 70324e530a..52ad2dd91f 100644 --- a/rust/bridge/shared/src/jni/convert.rs +++ b/rust/bridge/shared/src/jni/convert.rs @@ -748,6 +748,16 @@ impl ResultTypeInfo for Result { } } +impl ResultTypeInfo for Result { + type ResultType = T::ResultType; + fn convert_into(self, env: &JNIEnv) -> SignalJniResult { + T::convert_into(self?, env) + } + fn convert_into_jobject(signal_jni_result: &SignalJniResult) -> JObject { + ::convert_into_jobject(signal_jni_result) + } +} + impl ResultTypeInfo for Result { type ResultType = T::ResultType; fn convert_into(self, env: &JNIEnv) -> SignalJniResult { diff --git a/rust/bridge/shared/src/jni/error.rs b/rust/bridge/shared/src/jni/error.rs index 02aa31726c..9051a9a39e 100644 --- a/rust/bridge/shared/src/jni/error.rs +++ b/rust/bridge/shared/src/jni/error.rs @@ -9,6 +9,7 @@ use std::fmt; use attest::hsm_enclave::Error as HsmEnclaveError; use device_transfer::Error as DeviceTransferError; +use signal_grpc::Error as GrpcError; use libsignal_protocol::*; use signal_crypto::Error as SignalCryptoError; use signal_pin::Error as PinError; @@ -24,6 +25,7 @@ use super::*; pub enum SignalJniError { Signal(SignalProtocolError), DeviceTransfer(DeviceTransferError), + Grpc(GrpcError), SignalCrypto(SignalCryptoError), HsmEnclave(HsmEnclaveError), Sgx(SgxError), @@ -45,6 +47,7 @@ impl fmt::Display for SignalJniError { match self { SignalJniError::Signal(s) => write!(f, "{}", s), SignalJniError::DeviceTransfer(s) => write!(f, "{}", s), + SignalJniError::Grpc(e) => write!(f, "{}", e), SignalJniError::HsmEnclave(e) => write!(f, "{}", e), SignalJniError::Sgx(e) => write!(f, "{}", e), SignalJniError::Pin(e) => write!(f, "{}", e), @@ -87,6 +90,12 @@ impl From for SignalJniError { } } +impl From for SignalJniError { + fn from(e: GrpcError) -> SignalJniError { + SignalJniError::Grpc(e) + } +} + impl From for SignalJniError { fn from(e: HsmEnclaveError) -> SignalJniError { SignalJniError::HsmEnclave(e) diff --git a/rust/bridge/shared/src/jni/mod.rs b/rust/bridge/shared/src/jni/mod.rs index f63252b5e7..4ddcd49479 100644 --- a/rust/bridge/shared/src/jni/mod.rs +++ b/rust/bridge/shared/src/jni/mod.rs @@ -220,7 +220,8 @@ fn throw_error(env: &JNIEnv, error: SignalJniError) { | SignalJniError::Signal(SignalProtocolError::ApplicationCallbackError(_, _)) | SignalJniError::Signal(SignalProtocolError::FfiBindingError(_)) | SignalJniError::DeviceTransfer(DeviceTransferError::InternalError(_)) - | SignalJniError::DeviceTransfer(DeviceTransferError::KeyDecodingFailed) => { + | SignalJniError::DeviceTransfer(DeviceTransferError::KeyDecodingFailed) + | SignalJniError::Grpc(_) => { jni_class_name!(java.lang.RuntimeException) } diff --git a/rust/bridge/shared/src/lib.rs b/rust/bridge/shared/src/lib.rs index 53a4c97aae..5ddf3b37f2 100644 --- a/rust/bridge/shared/src/lib.rs +++ b/rust/bridge/shared/src/lib.rs @@ -28,6 +28,8 @@ pub use support::describe_panic; pub mod crypto; pub mod protocol; +pub mod grpc; + // Desktop does not make use of device transfer certificates #[cfg(any(feature = "jni", feature = "ffi"))] pub mod device_transfer; diff --git a/rust/grpc/Cargo.toml b/rust/grpc/Cargo.toml new file mode 100644 index 0000000000..26167ef489 --- /dev/null +++ b/rust/grpc/Cargo.toml @@ -0,0 +1,20 @@ +# +# Copyright (C) 2023 Signal Messenger, LLC. +# SPDX-License-Identifier: AGPL-3.0-only +# + +[package] +name = "signal-grpc" +version = "0.1.0" +authors = ["Signal Messenger LLC"] +edition = "2021" +license = "AGPL-3.0-only" + +[dependencies] +displaydoc = "0.2" +prost = "0.11" +thiserror = "1.0.30" +tonic = "0.9" + +[build-dependencies] +tonic-build = "0.9" diff --git a/rust/grpc/build.rs b/rust/grpc/build.rs new file mode 100644 index 0000000000..a99f991ce7 --- /dev/null +++ b/rust/grpc/build.rs @@ -0,0 +1,18 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +fn main() -> Result<(), Box> { + let protos = [ + "src/proto/proxy.proto", + ]; + tonic_build::configure().build_server(false).compile( + &protos, + &["src"], + )?; + for proto in &protos { + println!("cargo:rerun-if-changed={}", proto); + } + Ok(()) +} diff --git a/rust/grpc/src/client.rs b/rust/grpc/src/client.rs new file mode 100644 index 0000000000..6e832a662c --- /dev/null +++ b/rust/grpc/src/client.rs @@ -0,0 +1,34 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +use crate::{error::{Error, Result}, proto}; + +pub struct GrpcClient { +} + +impl GrpcClient { + pub fn new() -> Self { + GrpcClient {} + } + + pub async fn send_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { + println!("Tunneling gRPC message: method={} url_fragment={}, body.len={}", method, url_fragment, body.len()); + + let mut tunnel = proto::proxy::tunnel_client::TunnelClient::connect("gluongrpcproxy.gluonhq.net:443").await + .map_err(|e| Error::InvalidArgument(format!("{}", e)))?; + + let request = proto::proxy::SignalRpcMessage { + body: body.to_vec(), + method, + urlfragment: url_fragment, + header: vec![], + }; + + let response = tunnel.send_some_message(request).await + .map_err(|e| Error::InvalidArgument(format!("{}", e)))?; + + Ok(response.get_ref().message.as_bytes().to_vec()) + } +} diff --git a/rust/grpc/src/error.rs b/rust/grpc/src/error.rs new file mode 100644 index 0000000000..9398c8c38a --- /dev/null +++ b/rust/grpc/src/error.rs @@ -0,0 +1,15 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +use displaydoc::Display; +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Debug, Display, Error)] +pub enum Error { + /// invalid argument: {0} + InvalidArgument(String), +} diff --git a/rust/grpc/src/lib.rs b/rust/grpc/src/lib.rs new file mode 100644 index 0000000000..fb9a8b54b1 --- /dev/null +++ b/rust/grpc/src/lib.rs @@ -0,0 +1,12 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +mod client; +mod error; +mod proto; + +pub use client::GrpcClient; +pub use error::{Error, Result}; + diff --git a/rust/grpc/src/proto.rs b/rust/grpc/src/proto.rs new file mode 100644 index 0000000000..d8590b470c --- /dev/null +++ b/rust/grpc/src/proto.rs @@ -0,0 +1,6 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +pub mod proxy; diff --git a/rust/grpc/src/proto/proxy.proto b/rust/grpc/src/proto/proxy.proto new file mode 100644 index 0000000000..95bd7ca8e8 --- /dev/null +++ b/rust/grpc/src/proto/proxy.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +package signal.grpc.proxy; + +service Tunnel { + rpc SendSomeMessage (SignalRpcMessage) returns (SignalRpcReply) {} +} + +message SignalRpcMessage { + string urlfragment = 1; + string method = 2; + repeated string header = 3; + bytes body = 4; +} + +message SignalRpcReply { + int32 statuscode = 1; + string message = 2; +} diff --git a/rust/grpc/src/proto/proxy.rs b/rust/grpc/src/proto/proxy.rs new file mode 100644 index 0000000000..a19f4b39e2 --- /dev/null +++ b/rust/grpc/src/proto/proxy.rs @@ -0,0 +1,8 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +#![allow(clippy::derive_partial_eq_without_eq)] + +include!(concat!(env!("OUT_DIR"), "/signal.grpc.proxy.rs")); From a0f9ff647810e4703d9724091b11373314585687 Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Thu, 4 May 2023 10:18:11 +0200 Subject: [PATCH 2/9] make native entry point blocking --- Cargo.lock | 1 + rust/bridge/shared/src/grpc.rs | 2 +- rust/grpc/Cargo.toml | 1 + rust/grpc/build.rs | 3 ++- rust/grpc/src/client.rs | 16 +++++++++++++--- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9beb7507e..52ae9939aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2172,6 +2172,7 @@ dependencies = [ "displaydoc", "prost 0.11.9", "thiserror", + "tokio", "tonic", "tonic-build", ] diff --git a/rust/bridge/shared/src/grpc.rs b/rust/bridge/shared/src/grpc.rs index 838ec8ba63..c4f0da5ac1 100644 --- a/rust/bridge/shared/src/grpc.rs +++ b/rust/bridge/shared/src/grpc.rs @@ -12,5 +12,5 @@ use crate::*; #[bridge_fn] pub fn Grpc_SendMessage(method: String, url_fragment: String, body: &[u8]) -> Result> { - GrpcClient::new().send_message(method, url_fragment, body).await + GrpcClient::new()?.send_message(method, url_fragment, body) } diff --git a/rust/grpc/Cargo.toml b/rust/grpc/Cargo.toml index 26167ef489..0ab47b94b2 100644 --- a/rust/grpc/Cargo.toml +++ b/rust/grpc/Cargo.toml @@ -14,6 +14,7 @@ license = "AGPL-3.0-only" displaydoc = "0.2" prost = "0.11" thiserror = "1.0.30" +tokio = { version = "1.28", features = [ "rt" ] } tonic = "0.9" [build-dependencies] diff --git a/rust/grpc/build.rs b/rust/grpc/build.rs index a99f991ce7..b164efc832 100644 --- a/rust/grpc/build.rs +++ b/rust/grpc/build.rs @@ -7,7 +7,8 @@ fn main() -> Result<(), Box> { let protos = [ "src/proto/proxy.proto", ]; - tonic_build::configure().build_server(false).compile( + tonic_build::configure() + .build_server(false).compile( &protos, &["src"], )?; diff --git a/rust/grpc/src/client.rs b/rust/grpc/src/client.rs index 6e832a662c..737054ea2c 100644 --- a/rust/grpc/src/client.rs +++ b/rust/grpc/src/client.rs @@ -6,16 +6,26 @@ use crate::{error::{Error, Result}, proto}; pub struct GrpcClient { + tokio_runtime: tokio::runtime::Runtime, } impl GrpcClient { - pub fn new() -> Self { - GrpcClient {} + pub fn new() -> Result { + Ok(GrpcClient { + tokio_runtime: tokio::runtime::Builder::new_current_thread() + .build() + .map_err(|e| Error::InvalidArgument(format!("{}", e)))? + }) } - pub async fn send_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { + pub fn send_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { println!("Tunneling gRPC message: method={} url_fragment={}, body.len={}", method, url_fragment, body.len()); + self.tokio_runtime.block_on(async { + self.tunnel_message(method, url_fragment, body).await + }) + } + async fn tunnel_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { let mut tunnel = proto::proxy::tunnel_client::TunnelClient::connect("gluongrpcproxy.gluonhq.net:443").await .map_err(|e| Error::InvalidArgument(format!("{}", e)))?; From 6bb9884491d5c8cf2a53567a1afd09b969b9a6b5 Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Thu, 4 May 2023 18:26:47 +0200 Subject: [PATCH 3/9] block calls --- rust/grpc/src/client.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rust/grpc/src/client.rs b/rust/grpc/src/client.rs index 737054ea2c..2971851bb3 100644 --- a/rust/grpc/src/client.rs +++ b/rust/grpc/src/client.rs @@ -13,21 +13,22 @@ impl GrpcClient { pub fn new() -> Result { Ok(GrpcClient { tokio_runtime: tokio::runtime::Builder::new_current_thread() + .enable_io() .build() - .map_err(|e| Error::InvalidArgument(format!("{}", e)))? + .map_err(|e| Error::InvalidArgument(format!("tokio.create_runtime: {:?}", e)))? }) } pub fn send_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { - println!("Tunneling gRPC message: method={} url_fragment={}, body.len={}", method, url_fragment, body.len()); + println!("Tunneling gRPC message: mmethod={} url_fragment={}, body.len={}", method, url_fragment, body.len()); self.tokio_runtime.block_on(async { self.tunnel_message(method, url_fragment, body).await }) } async fn tunnel_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { - let mut tunnel = proto::proxy::tunnel_client::TunnelClient::connect("gluongrpcproxy.gluonhq.net:443").await - .map_err(|e| Error::InvalidArgument(format!("{}", e)))?; + let mut tunnel = proto::proxy::tunnel_client::TunnelClient::connect("https://grpcproxy.gluonhq.net:443").await + .map_err(|e| Error::InvalidArgument(format!("tunnel.connect: {:?}", e)))?; let request = proto::proxy::SignalRpcMessage { body: body.to_vec(), @@ -37,7 +38,7 @@ impl GrpcClient { }; let response = tunnel.send_some_message(request).await - .map_err(|e| Error::InvalidArgument(format!("{}", e)))?; + .map_err(|e| Error::InvalidArgument(format!("tunnel.send_some_message: {:?}", e)))?; Ok(response.get_ref().message.as_bytes().to_vec()) } From bd37370c8c5a6d1bfd3139d96de938f11a17144a Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Fri, 5 May 2023 13:35:51 +0200 Subject: [PATCH 4/9] make it work with proxy tunnel server --- Cargo.lock | 113 ++++++++++++++++++++++++++++++++ rust/grpc/Cargo.toml | 2 +- rust/grpc/src/client.rs | 4 +- rust/grpc/src/proto/proxy.proto | 4 +- rust/grpc/src/proto/proxy.rs | 2 +- 5 files changed, 119 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52ae9939aa..dd31be0ebd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,28 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "async-trait" version = "0.1.68" @@ -1991,6 +2013,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -2012,6 +2049,37 @@ dependencies = [ "semver 0.11.0", ] +[[package]] +name = "rustls" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.0", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -2051,6 +2119,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "semver" version = "0.9.0" @@ -2249,6 +2327,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2410,6 +2494,16 @@ dependencies = [ "syn 2.0.15", ] +[[package]] +name = "tokio-rustls" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -2450,6 +2544,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ + "async-stream", "async-trait", "axum", "base64 0.21.0", @@ -2464,12 +2559,15 @@ dependencies = [ "percent-encoding", "pin-project", "prost 0.11.9", + "rustls-pemfile", "tokio", + "tokio-rustls", "tokio-stream", "tower", "tower-layer", "tower-service", "tracing", + "webpki-roots", ] [[package]] @@ -2595,6 +2693,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "unzip3" version = "1.0.0" @@ -2750,6 +2854,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa54963694b65584e170cf5dc46aeb4dcaa5584e652ff5f3952e56d66aff0125" +dependencies = [ + "rustls-webpki", +] + [[package]] name = "which" version = "4.2.4" diff --git a/rust/grpc/Cargo.toml b/rust/grpc/Cargo.toml index 0ab47b94b2..2a7cf46709 100644 --- a/rust/grpc/Cargo.toml +++ b/rust/grpc/Cargo.toml @@ -15,7 +15,7 @@ displaydoc = "0.2" prost = "0.11" thiserror = "1.0.30" tokio = { version = "1.28", features = [ "rt" ] } -tonic = "0.9" +tonic = { version = "0.9", features = [ "tls", "tls-webpki-roots" ] } [build-dependencies] tonic-build = "0.9" diff --git a/rust/grpc/src/client.rs b/rust/grpc/src/client.rs index 2971851bb3..9cfe91a010 100644 --- a/rust/grpc/src/client.rs +++ b/rust/grpc/src/client.rs @@ -20,7 +20,7 @@ impl GrpcClient { } pub fn send_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { - println!("Tunneling gRPC message: mmethod={} url_fragment={}, body.len={}", method, url_fragment, body.len()); + println!("Tunneling gRPC message: method={} url_fragment={}, body.len={}", method, url_fragment, body.len()); self.tokio_runtime.block_on(async { self.tunnel_message(method, url_fragment, body).await }) @@ -31,7 +31,7 @@ impl GrpcClient { .map_err(|e| Error::InvalidArgument(format!("tunnel.connect: {:?}", e)))?; let request = proto::proxy::SignalRpcMessage { - body: body.to_vec(), + body: std::str::from_utf8(body).unwrap().to_owned(), method, urlfragment: url_fragment, header: vec![], diff --git a/rust/grpc/src/proto/proxy.proto b/rust/grpc/src/proto/proxy.proto index 95bd7ca8e8..5c7990a78f 100644 --- a/rust/grpc/src/proto/proxy.proto +++ b/rust/grpc/src/proto/proxy.proto @@ -5,7 +5,7 @@ syntax = "proto3"; // SPDX-License-Identifier: AGPL-3.0-only // -package signal.grpc.proxy; +package io.privacyresource.grpcproxy; service Tunnel { rpc SendSomeMessage (SignalRpcMessage) returns (SignalRpcReply) {} @@ -15,7 +15,7 @@ message SignalRpcMessage { string urlfragment = 1; string method = 2; repeated string header = 3; - bytes body = 4; + string body = 4; } message SignalRpcReply { diff --git a/rust/grpc/src/proto/proxy.rs b/rust/grpc/src/proto/proxy.rs index a19f4b39e2..e44b4f03b8 100644 --- a/rust/grpc/src/proto/proxy.rs +++ b/rust/grpc/src/proto/proxy.rs @@ -5,4 +5,4 @@ #![allow(clippy::derive_partial_eq_without_eq)] -include!(concat!(env!("OUT_DIR"), "/signal.grpc.proxy.rs")); +include!(concat!(env!("OUT_DIR"), "/io.privacyresource.grpcproxy.rs")); From 15d4220ca928d2c0c3c1a5e33d12152f279c3615 Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Fri, 5 May 2023 17:12:14 +0200 Subject: [PATCH 5/9] support for headers --- .../org/signal/libsignal/internal/Native.java | 3 +- rust/bridge/shared/src/grpc.rs | 11 +++++-- rust/bridge/shared/src/jni/convert.rs | 32 +++++++++++++++++++ rust/bridge/shared/src/jni/mod.rs | 1 + rust/bridge/shared/src/lib.rs | 1 + rust/grpc/src/client.rs | 20 ++++++++---- rust/grpc/src/proto/proxy.proto | 4 +-- rust/grpc/src/proto/proxy.rs | 2 +- 8 files changed, 61 insertions(+), 13 deletions(-) diff --git a/java/shared/java/org/signal/libsignal/internal/Native.java b/java/shared/java/org/signal/libsignal/internal/Native.java index 98a2f0d942..e746173c05 100644 --- a/java/shared/java/org/signal/libsignal/internal/Native.java +++ b/java/shared/java/org/signal/libsignal/internal/Native.java @@ -22,6 +22,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; +import java.util.List; import java.util.UUID; import java.util.Map; @@ -488,5 +489,5 @@ private Native() {} public static native void UuidCiphertext_CheckValidContents(byte[] buffer); - public static native byte[] Grpc_SendMessage(String method, String urlFragment, byte[] body); + public static native byte[] Grpc_SendMessage(String method, String urlFragment, byte[] body, Map> headers); } diff --git a/rust/bridge/shared/src/grpc.rs b/rust/bridge/shared/src/grpc.rs index c4f0da5ac1..e3f0e5255e 100644 --- a/rust/bridge/shared/src/grpc.rs +++ b/rust/bridge/shared/src/grpc.rs @@ -10,7 +10,12 @@ use libsignal_bridge_macros::*; use crate::support::*; use crate::*; -#[bridge_fn] -pub fn Grpc_SendMessage(method: String, url_fragment: String, body: &[u8]) -> Result> { - GrpcClient::new()?.send_message(method, url_fragment, body) +use std::collections::HashMap; + +#[cfg(all(feature = "jni"))] +pub struct GrpcHeaders(pub HashMap>); + +#[bridge_fn(ffi = false, node = false)] +pub fn Grpc_SendMessage(method: String, url_fragment: String, body: &[u8], headers: GrpcHeaders) -> Result> { + GrpcClient::new()?.send_message(method, url_fragment, body, headers.0) } diff --git a/rust/bridge/shared/src/jni/convert.rs b/rust/bridge/shared/src/jni/convert.rs index 52ad2dd91f..5d45ec8392 100644 --- a/rust/bridge/shared/src/jni/convert.rs +++ b/rust/bridge/shared/src/jni/convert.rs @@ -8,6 +8,7 @@ use jni::sys::{jbyte, JNI_FALSE, JNI_TRUE}; use jni::JNIEnv; use libsignal_protocol::*; use paste::paste; +use std::collections::HashMap; use std::convert::TryInto; use std::ops::Deref; @@ -414,6 +415,34 @@ impl<'a> SimpleArgTypeInfo<'a> for CiphertextMessageRef<'a> { } } +impl<'a> SimpleArgTypeInfo<'a> for crate::grpc::GrpcHeaders { + type ArgType = JavaArgMap<'a>; + fn convert_from(env: &JNIEnv, foreign: Self::ArgType) -> SignalJniResult { + if foreign.is_null() { + return Err(SignalJniError::NullHandle); + } + + let mut headers = HashMap::new(); + + let jmap = env.get_map(foreign)?; + let mut jmap_iter = jmap.iter()?; + while let Some((key, value)) = jmap_iter.next() { + let header_key: String = env.get_string(key.into())?.into(); + let values = env.get_list(value)?; + let mut values_iter = values.iter()?; + while let Some(value) = values_iter.next() { + let header_value: String = env.get_string(value.into())?.into(); + headers + .entry(header_key.clone()) + .and_modify(|l: &mut Vec| l.push(header_value.clone())) + .or_insert_with(move || vec![header_value]); + } + } + + Ok(crate::grpc::GrpcHeaders(headers)) + } +} + #[cfg(not(target_os = "android"))] impl ResultTypeInfo for crate::cds2::Cds2Metrics { type ResultType = jobject; @@ -1101,6 +1130,9 @@ macro_rules! jni_arg_type { (Uuid) => { jni::JavaUUID }; + (GrpcHeaders) => { + jni::JavaArgMap + }; (jni::CiphertextMessageRef) => { jni::JavaCiphertextMessage }; diff --git a/rust/bridge/shared/src/jni/mod.rs b/rust/bridge/shared/src/jni/mod.rs index 4ddcd49479..b54e09b7bc 100644 --- a/rust/bridge/shared/src/jni/mod.rs +++ b/rust/bridge/shared/src/jni/mod.rs @@ -42,6 +42,7 @@ pub type ObjectHandle = jlong; pub type JavaObject<'a> = JObject<'a>; pub type JavaUUID<'a> = JObject<'a>; +pub type JavaArgMap<'a> = JObject<'a>; pub type JavaReturnUUID = jobject; pub type JavaCiphertextMessage<'a> = JObject<'a>; pub type JavaReturnCiphertextMessage = jobject; diff --git a/rust/bridge/shared/src/lib.rs b/rust/bridge/shared/src/lib.rs index 5ddf3b37f2..e43f52cf31 100644 --- a/rust/bridge/shared/src/lib.rs +++ b/rust/bridge/shared/src/lib.rs @@ -28,6 +28,7 @@ pub use support::describe_panic; pub mod crypto; pub mod protocol; +#[cfg(feature = "jni")] pub mod grpc; // Desktop does not make use of device transfer certificates diff --git a/rust/grpc/src/client.rs b/rust/grpc/src/client.rs index 9cfe91a010..b42a74b9f3 100644 --- a/rust/grpc/src/client.rs +++ b/rust/grpc/src/client.rs @@ -4,6 +4,7 @@ // use crate::{error::{Error, Result}, proto}; +use std::collections::HashMap; pub struct GrpcClient { tokio_runtime: tokio::runtime::Runtime, @@ -19,22 +20,29 @@ impl GrpcClient { }) } - pub fn send_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { - println!("Tunneling gRPC message: method={} url_fragment={}, body.len={}", method, url_fragment, body.len()); + pub fn send_message(&self, method: String, url_fragment: String, body: &[u8], headers: HashMap>) -> Result> { + println!("Tunneling gRPC message: method={} url_fragment={}, body.len={}, headers={:?}", method, url_fragment, body.len(), headers); self.tokio_runtime.block_on(async { - self.tunnel_message(method, url_fragment, body).await + self.tunnel_message(method, url_fragment, body, headers).await }) } - async fn tunnel_message(&self, method: String, url_fragment: String, body: &[u8]) -> Result> { + async fn tunnel_message(&self, method: String, url_fragment: String, body: &[u8], headers: HashMap>) -> Result> { let mut tunnel = proto::proxy::tunnel_client::TunnelClient::connect("https://grpcproxy.gluonhq.net:443").await .map_err(|e| Error::InvalidArgument(format!("tunnel.connect: {:?}", e)))?; + let mut request_headers = vec![]; + for (header_name, header_values) in headers.iter() { + for header_value in header_values.iter() { + request_headers.push(format!("{}={}", header_name, header_value)) + } + } + let request = proto::proxy::SignalRpcMessage { - body: std::str::from_utf8(body).unwrap().to_owned(), + body: body.to_vec(), method, urlfragment: url_fragment, - header: vec![], + header: request_headers, }; let response = tunnel.send_some_message(request).await diff --git a/rust/grpc/src/proto/proxy.proto b/rust/grpc/src/proto/proxy.proto index 5c7990a78f..95bd7ca8e8 100644 --- a/rust/grpc/src/proto/proxy.proto +++ b/rust/grpc/src/proto/proxy.proto @@ -5,7 +5,7 @@ syntax = "proto3"; // SPDX-License-Identifier: AGPL-3.0-only // -package io.privacyresource.grpcproxy; +package signal.grpc.proxy; service Tunnel { rpc SendSomeMessage (SignalRpcMessage) returns (SignalRpcReply) {} @@ -15,7 +15,7 @@ message SignalRpcMessage { string urlfragment = 1; string method = 2; repeated string header = 3; - string body = 4; + bytes body = 4; } message SignalRpcReply { diff --git a/rust/grpc/src/proto/proxy.rs b/rust/grpc/src/proto/proxy.rs index e44b4f03b8..a19f4b39e2 100644 --- a/rust/grpc/src/proto/proxy.rs +++ b/rust/grpc/src/proto/proxy.rs @@ -5,4 +5,4 @@ #![allow(clippy::derive_partial_eq_without_eq)] -include!(concat!(env!("OUT_DIR"), "/io.privacyresource.grpcproxy.rs")); +include!(concat!(env!("OUT_DIR"), "/signal.grpc.proxy.rs")); From 2ad18645534d77c8ac8bae67a99a5d814ae77940 Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Fri, 5 May 2023 17:26:36 +0200 Subject: [PATCH 6/9] remove ffi changes --- Cargo.toml | 2 -- rust/bridge/ffi/src/util.rs | 1 - rust/bridge/shared/src/ffi/convert.rs | 7 ------- rust/bridge/shared/src/ffi/error.rs | 11 ----------- 4 files changed, 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5a567e8d4e..1980ed2a75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,14 +17,12 @@ members = [ default-members = [ "rust/crypto", "rust/device-transfer", - "rust/grpc", "rust/pin", "rust/poksho", "rust/protocol", "rust/usernames", "rust/zkcredential", "rust/zkgroup", - "rust/bridge/jni", ] resolver = "2" # so that our dev-dependency features don't leak into products diff --git a/rust/bridge/ffi/src/util.rs b/rust/bridge/ffi/src/util.rs index 1a16cf9f07..be8ee61e10 100644 --- a/rust/bridge/ffi/src/util.rs +++ b/rust/bridge/ffi/src/util.rs @@ -72,7 +72,6 @@ impl From<&SignalFfiError> for SignalErrorCode { SignalFfiError::UnexpectedPanic(_) | SignalFfiError::DeviceTransfer(DeviceTransferError::InternalError(_)) - | SignalFfiError::Grpc(_) | SignalFfiError::Signal(SignalProtocolError::FfiBindingError(_)) => { SignalErrorCode::InternalError } diff --git a/rust/bridge/shared/src/ffi/convert.rs b/rust/bridge/shared/src/ffi/convert.rs index cd41c8ff81..50c18571ef 100644 --- a/rust/bridge/shared/src/ffi/convert.rs +++ b/rust/bridge/shared/src/ffi/convert.rs @@ -295,13 +295,6 @@ impl ResultTypeInfo for Result { } } -impl ResultTypeInfo for Result { - type ResultType = T::ResultType; - fn convert_into(self) -> SignalFfiResult { - T::convert_into(self?) - } -} - impl ResultTypeInfo for Result { type ResultType = T::ResultType; fn convert_into(self) -> SignalFfiResult { diff --git a/rust/bridge/shared/src/ffi/error.rs b/rust/bridge/shared/src/ffi/error.rs index 57094c36ad..3865648585 100644 --- a/rust/bridge/shared/src/ffi/error.rs +++ b/rust/bridge/shared/src/ffi/error.rs @@ -9,7 +9,6 @@ use std::fmt; use attest::hsm_enclave::Error as HsmEnclaveError; use attest::sgx_session::Error as SgxError; use device_transfer::Error as DeviceTransferError; -use signal_grpc::Error as GrpcError; use libsignal_protocol::*; use signal_crypto::Error as SignalCryptoError; use signal_pin::Error as PinError; @@ -25,7 +24,6 @@ use super::NullPointerError; pub enum SignalFfiError { Signal(SignalProtocolError), DeviceTransfer(DeviceTransferError), - Grpc(GrpcError), HsmEnclave(HsmEnclaveError), Sgx(SgxError), Pin(PinError), @@ -45,9 +43,6 @@ impl fmt::Display for SignalFfiError { SignalFfiError::DeviceTransfer(c) => { write!(f, "Device transfer operation failed: {}", c) } - SignalFfiError::Grpc(e) => { - write!(f, "Grpc operation failed: {}", e) - } SignalFfiError::HsmEnclave(e) => { write!(f, "HSM enclave operation failed: {}", e) } @@ -82,12 +77,6 @@ impl From for SignalFfiError { } } -impl From for SignalFfiError { - fn from(e: GrpcError) -> SignalFfiError { - SignalFfiError::Grpc(e) - } -} - impl From for SignalFfiError { fn from(e: HsmEnclaveError) -> SignalFfiError { SignalFfiError::HsmEnclave(e) From 8a187f0d7162d3c321a381808a5a88cb8f5771fe Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Mon, 8 May 2023 14:55:13 +0200 Subject: [PATCH 7/9] wrap native call in java specific code --- .../org/signal/libsignal/grpc/GrpcClient.java | 26 ++++++++ .../libsignal/grpc/SignalRpcMessage.java | 59 +++++++++++++++++++ .../signal/libsignal/grpc/SignalRpcReply.java | 28 +++++++++ rust/grpc/src/client.rs | 7 ++- 4 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 java/shared/java/org/signal/libsignal/grpc/GrpcClient.java create mode 100644 java/shared/java/org/signal/libsignal/grpc/SignalRpcMessage.java create mode 100644 java/shared/java/org/signal/libsignal/grpc/SignalRpcReply.java diff --git a/java/shared/java/org/signal/libsignal/grpc/GrpcClient.java b/java/shared/java/org/signal/libsignal/grpc/GrpcClient.java new file mode 100644 index 0000000000..d502aacb6e --- /dev/null +++ b/java/shared/java/org/signal/libsignal/grpc/GrpcClient.java @@ -0,0 +1,26 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +package org.signal.libsignal.grpc; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.signal.libsignal.internal.Native; + +public class GrpcClient { + + public SignalRpcReply sendMessage(SignalRpcMessage message) { + byte[] reply = Native.Grpc_SendMessage(message.getMethod(), message.getUrlFragment(), message.getBody(), message.getHeaders()); + + SignalRpcReply signalRpcReply = new SignalRpcReply(); + + ByteBuffer replyBuffer = ByteBuffer.wrap(reply, 0, 4); + signalRpcReply.setStatusCode(replyBuffer.getInt()); + + signalRpcReply.setMessage(new String(reply, 4, reply.length - 4, StandardCharsets.UTF_8)); + return signalRpcReply; + } +} diff --git a/java/shared/java/org/signal/libsignal/grpc/SignalRpcMessage.java b/java/shared/java/org/signal/libsignal/grpc/SignalRpcMessage.java new file mode 100644 index 0000000000..700134c3fe --- /dev/null +++ b/java/shared/java/org/signal/libsignal/grpc/SignalRpcMessage.java @@ -0,0 +1,59 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +package org.signal.libsignal.grpc; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class SignalRpcMessage { + + private String method; + private String urlFragment; + private byte[] body; + private Map> headers; + + public SignalRpcMessage() { + this.headers = new HashMap<>(); + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public byte[] getBody() { + return body; + } + + public void setBody(byte[] body) { + this.body = body; + } + + public String getUrlFragment() { + return urlFragment; + } + + public void setUrlFragment(String urlFragment) { + this.urlFragment = urlFragment; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } + + public void header(String headerKey, String headerValue) { + this.headers.computeIfAbsent(headerKey, key -> new LinkedList<>()).add(headerValue); + } +} diff --git a/java/shared/java/org/signal/libsignal/grpc/SignalRpcReply.java b/java/shared/java/org/signal/libsignal/grpc/SignalRpcReply.java new file mode 100644 index 0000000000..c7ee6ba424 --- /dev/null +++ b/java/shared/java/org/signal/libsignal/grpc/SignalRpcReply.java @@ -0,0 +1,28 @@ +// +// Copyright 2023 Signal Messenger, LLC. +// SPDX-License-Identifier: AGPL-3.0-only +// + +package org.signal.libsignal.grpc; + +public class SignalRpcReply { + + private int statusCode; + private String message; + + public int getStatusCode() { + return statusCode; + } + + public void setStatusCode(int statusCode) { + this.statusCode = statusCode; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/rust/grpc/src/client.rs b/rust/grpc/src/client.rs index b42a74b9f3..ca8c146e66 100644 --- a/rust/grpc/src/client.rs +++ b/rust/grpc/src/client.rs @@ -48,6 +48,11 @@ impl GrpcClient { let response = tunnel.send_some_message(request).await .map_err(|e| Error::InvalidArgument(format!("tunnel.send_some_message: {:?}", e)))?; - Ok(response.get_ref().message.as_bytes().to_vec()) + let mut result = vec![]; + + result.extend(response.get_ref().statuscode.to_be_bytes()); + result.extend(response.get_ref().message.as_bytes()); + + Ok(result) } } From 3bfdcbcb3d477d17aab75f1a684979b4a8cb4b09 Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Tue, 9 May 2023 08:14:31 +0200 Subject: [PATCH 8/9] update gradle wrapper to 7.6.1 --- java/gradle/wrapper/gradle-wrapper.jar | Bin 55616 -> 59821 bytes java/gradle/wrapper/gradle-wrapper.properties | 4 +- java/gradlew | 260 +++++++++++------- java/gradlew.bat | 25 +- 4 files changed, 161 insertions(+), 128 deletions(-) diff --git a/java/gradle/wrapper/gradle-wrapper.jar b/java/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf016b3885f6930543d57b744ea8c220a1a..41d9927a4d4fb3f96a785543079b8df6723c946b 100644 GIT binary patch delta 26784 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

_` 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 diff --git a/java/gradle/wrapper/gradle-wrapper.properties b/java/gradle/wrapper/gradle-wrapper.properties index a0c86dbe85..774fae8767 100644 --- a/java/gradle/wrapper/gradle-wrapper.properties +++ b/java/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,5 @@ -# Note: Check https://gradle.org/release-checksums/ before updating wrapper or distribution distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=8cc27038d5dbd815759851ba53e70cf62e481b87494cc97cfd97982ada5ba634 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/java/gradlew b/java/gradlew index 83f2acfdc3..1b6c787337 100755 --- a/java/gradlew +++ b/java/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,78 +17,113 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -105,84 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/java/gradlew.bat b/java/gradlew.bat index 24467a141f..ac1b06f938 100644 --- a/java/gradlew.bat +++ b/java/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell From c25949c9a0279b9b6850e224ef84d0b6af01a952 Mon Sep 17 00:00:00 2001 From: Joeri Sykora Date: Tue, 9 May 2023 08:14:51 +0200 Subject: [PATCH 9/9] use a gluon specific version --- java/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/build.gradle b/java/build.gradle index 5ecaa6aeee..0521f524bc 100644 --- a/java/build.gradle +++ b/java/build.gradle @@ -2,7 +2,7 @@ plugins { id "de.undercouch.download" version "5.0.2" } -def version_number = "0.23.0" +def version_number = "0.23.0-gluon-1" subprojects { ext.version_number = version_number