diff --git a/README.md b/README.md index 957e973..883dd30 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ git subtree pull --prefix cargokit https://github.com/irondash/cargokit.git main ## Development -To (re)generate Dart bindings run `just generate` +To (re)generate C bindings for Rust, `cargo build` the crate in `/rust`. For +Dart bindings, run `dart run ffigen --config ffigen.yaml` ## Example app diff --git a/example/lib/main.dart b/example/lib/main.dart index bb86765..030349b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -41,6 +41,8 @@ class _MyAppState extends State { // Flag to track if tor has started. bool torStarted = false; + String? exitNodeAddress; + // Set the default text for the host input field. final hostController = TextEditingController(text: 'https://icanhazip.com/'); // https://check.torproject.org is another good option. @@ -83,6 +85,24 @@ class _MyAppState extends State { }); print('Done awaiting; tor should be running'); + + // Fetch the exit node address. + try { + String? address = await Tor.instance.getExitNode(); + // getExitNode above should return like: [1.2.3.4:5 ed25519:Q6+... $7...] + setState(() { + if (address != null && address!.contains(":")) { + // Strip just the IP out of the above. + address = address?.split(":")[0].substring(1); + } + exitNodeAddress = address; + }); + } catch (e) { + print('Error getting exit node address: $e'); + setState(() { + exitNodeAddress = 'Error retrieving exit node address'; + }); + } } @override @@ -150,6 +170,14 @@ class _MyAppState extends State { }, child: const Text("Stop"), ), + // Display the exit node address. + if (exitNodeAddress != null) ...[ + spacerSmall, + Text( + 'Exit Node: $exitNodeAddress', + style: const TextStyle(fontSize: 16), + ), + ], ], ), Row( diff --git a/example/pubspec.lock b/example/pubspec.lock index 1ff8c91..a13f188 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" cupertino_icons: dependency: "direct main" description: @@ -87,18 +87,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -215,7 +215,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" socks5_proxy: dependency: "direct main" description: @@ -252,10 +252,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" term_glyph: dependency: transitive description: @@ -268,10 +268,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.3" tor: dependency: "direct main" description: diff --git a/ffigen.yaml b/ffigen.yaml index 62108b0..3b7d098 100644 --- a/ffigen.yaml +++ b/ffigen.yaml @@ -8,7 +8,7 @@ description: | Bindings for `tor.h`. Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`. -output: 'lib/tor_bindings_generated.dart' +output: 'lib/generated_bindings.dart' headers: entry-points: - 'rust/target/tor.h' diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index b5b15a5..97ca4d6 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -2,12 +2,20 @@ // // SPDX-License-Identifier: MIT +// ignore_for_file: always_specify_types +// ignore_for_file: camel_case_types +// ignore_for_file: non_constant_identifier_names + // AUTO GENERATED FILE, DO NOT EDIT. // // Generated by `package:ffigen`. // ignore_for_file: type=lint import 'dart:ffi' as ffi; +/// Bindings for `tor.h`. +/// +/// Regenerate bindings with `flutter pub run ffigen --config ffigen.yaml`. +/// class NativeLibrary { /// Holds the symbol lookup function. final ffi.Pointer Function(String symbolName) @@ -87,6 +95,39 @@ class NativeLibrary { late final _tor_proxy_stop = _tor_proxy_stopPtr.asFunction)>(); + /// Get the exit node's identity (e.g., nickname or ID) for the given client. + ffi.Pointer tor_get_exit_node( + ffi.Pointer client, + ) { + return _tor_get_exit_node( + client, + ); + } + + late final _tor_get_exit_nodePtr = _lookup< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer)>>('tor_get_exit_node'); + late final _tor_get_exit_node = _tor_get_exit_nodePtr + .asFunction Function(ffi.Pointer)>(); + + /// Free a C string allocated by this library. + /// + /// Used to free memory allocated by functions like `tor_get_exit_node`. + void tor_free_string( + ffi.Pointer s, + ) { + return _tor_free_string( + s, + ); + } + + late final _tor_free_stringPtr = + _lookup)>>( + 'tor_free_string'); + late final _tor_free_string = + _tor_free_stringPtr.asFunction)>(); + void tor_hello() { return _tor_hello(); } @@ -135,141 +176,3 @@ final class Tor extends ffi.Struct { external ffi.Pointer proxy; } - -const int true1 = 1; - -const int false1 = 0; - -const int INT8_MIN = -128; - -const int INT16_MIN = -32768; - -const int INT32_MIN = -2147483648; - -const int INT64_MIN = -9223372036854775808; - -const int INT8_MAX = 127; - -const int INT16_MAX = 32767; - -const int INT32_MAX = 2147483647; - -const int INT64_MAX = 9223372036854775807; - -const int UINT8_MAX = 255; - -const int UINT16_MAX = 65535; - -const int UINT32_MAX = 4294967295; - -const int UINT64_MAX = -1; - -const int INT_LEAST8_MIN = -128; - -const int INT_LEAST16_MIN = -32768; - -const int INT_LEAST32_MIN = -2147483648; - -const int INT_LEAST64_MIN = -9223372036854775808; - -const int INT_LEAST8_MAX = 127; - -const int INT_LEAST16_MAX = 32767; - -const int INT_LEAST32_MAX = 2147483647; - -const int INT_LEAST64_MAX = 9223372036854775807; - -const int UINT_LEAST8_MAX = 255; - -const int UINT_LEAST16_MAX = 65535; - -const int UINT_LEAST32_MAX = 4294967295; - -const int UINT_LEAST64_MAX = -1; - -const int INT_FAST8_MIN = -128; - -const int INT_FAST16_MIN = -9223372036854775808; - -const int INT_FAST32_MIN = -9223372036854775808; - -const int INT_FAST64_MIN = -9223372036854775808; - -const int INT_FAST8_MAX = 127; - -const int INT_FAST16_MAX = 9223372036854775807; - -const int INT_FAST32_MAX = 9223372036854775807; - -const int INT_FAST64_MAX = 9223372036854775807; - -const int UINT_FAST8_MAX = 255; - -const int UINT_FAST16_MAX = -1; - -const int UINT_FAST32_MAX = -1; - -const int UINT_FAST64_MAX = -1; - -const int INTPTR_MIN = -9223372036854775808; - -const int INTPTR_MAX = 9223372036854775807; - -const int UINTPTR_MAX = -1; - -const int INTMAX_MIN = -9223372036854775808; - -const int INTMAX_MAX = 9223372036854775807; - -const int UINTMAX_MAX = -1; - -const int PTRDIFF_MIN = -9223372036854775808; - -const int PTRDIFF_MAX = 9223372036854775807; - -const int SIG_ATOMIC_MIN = -2147483648; - -const int SIG_ATOMIC_MAX = 2147483647; - -const int SIZE_MAX = -1; - -const int WCHAR_MIN = -2147483648; - -const int WCHAR_MAX = 2147483647; - -const int WINT_MIN = 0; - -const int WINT_MAX = 4294967295; - -const int NULL = 0; - -const int WNOHANG = 1; - -const int WUNTRACED = 2; - -const int WSTOPPED = 2; - -const int WEXITED = 4; - -const int WCONTINUED = 8; - -const int WNOWAIT = 16777216; - -const int RAND_MAX = 2147483647; - -const int EXIT_FAILURE = 1; - -const int EXIT_SUCCESS = 0; - -const int LITTLE_ENDIAN = 1234; - -const int BIG_ENDIAN = 4321; - -const int PDP_ENDIAN = 3412; - -const int BYTE_ORDER = 1234; - -const int FD_SETSIZE = 1024; - -const int NFDBITS = 64; diff --git a/lib/tor.dart b/lib/tor.dart index 5960118..cf1c54a 100644 --- a/lib/tor.dart +++ b/lib/tor.dart @@ -261,6 +261,27 @@ class Tor { })); } + /// Gets the current exit node. + Future getExitNode() async { + if (_clientPtr == nullptr || !started || !bootstrapped) { + throw ClientNotActive(); + } + + final lib = rust.NativeLibrary(_lib); + Pointer addressPtr = lib.tor_get_exit_node(_clientPtr); + + if (addressPtr == nullptr) { + String rustError = + lib.tor_last_error_message().cast().toDartString(); + throw Exception("Failed to get exit node: $rustError"); + } + + String address = addressPtr.cast().toDartString(); + lib.tor_free_string(addressPtr); + + return address; + } + static throwRustException(rust.NativeLibrary lib) { String rustError = lib.tor_last_error_message().cast().toDartString(); diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 6e5fcb4..0987f71 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -401,9 +401,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -475,9 +475,9 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "caret" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac3ae35de516b8ee4bfb6affc72bd6c121d2967206f307c026890f99c7875a0" +checksum = "8c979a125c4d00f63d49b648530a952c6cc42e3387cc96f41f9a4687ee6b9273" [[package]] name = "cbindgen" @@ -500,12 +500,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -906,7 +907,7 @@ checksum = "e1b84d32b18d9a256d81e4fec2e4cfd0ab6dde5e5ff49be1713ae0adbd0060c2" dependencies = [ "heck 0.5.0", "indexmap 2.0.0", - "itertools 0.12.0", + "itertools 0.13.0", "proc-macro-crate", "proc-macro2", "quote", @@ -949,15 +950,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -1272,9 +1273,9 @@ dependencies = [ [[package]] name = "fs-mistrust" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa843fc37db9fe16a248421fa36454f903dbc2ff722cc613a7e12932ee572c7" +checksum = "43de34e45ddb3fc314aeae5c5b078b8e3549980cd45836f8364d7cca8d85aead" dependencies = [ "derive_builder_fork_arti", "dirs", @@ -1296,6 +1297,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "fslock-arti-fork" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21bd626aaab7b904b20bef6d9e06298914a0c8d9fb8b010483766b2e532791" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "fslock-guard" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82482849edd4067d2e0ace172a41f151f4ecb37bf132e15a2ba689d38bf0aa1" +dependencies = [ + "fslock-arti-fork", + "thiserror", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" @@ -1438,6 +1460,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "growable-bloom-filter" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c669fa03050eb3445343f215d62fc1ab831e8098bc9a55f26e9724faff11075c" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "xxhash-rust", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1693,15 +1727,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -1719,9 +1744,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -1735,6 +1760,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k12" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dc5fdb62af2f520116927304f15d25b3c2667b4817b90efdc045194c912c54" +dependencies = [ + "digest", + "sha3", +] + [[package]] name = "keccak" version = "0.1.4" @@ -1910,7 +1945,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", "filetime", "inotify", "kqueue", @@ -2057,7 +2092,7 @@ version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -2511,9 +2546,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "retry-error" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b960f1dc847b9e4383ca73b4e8002f725b2f562addb9ade88a6ba109b305e96d" +checksum = "eeb501c6079c6e2a1c9761b76ddb12ecb6818b8773748f5e0394b95f838e4a38" [[package]] name = "rfc6979" @@ -2563,7 +2598,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2593,7 +2628,7 @@ version = "0.38.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", "errno", "itoa", "libc", @@ -2616,9 +2651,9 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safelog" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1e9cd092ef5e122f1a34f3fe15de8e9685f8f610e31c4c0643976aa5e31737" +checksum = "cabd7492c13678058e680f161cf94ba34d9d9e48419d1fbc6c21a32926c23764" dependencies = [ "derive_more", "educe", @@ -2737,6 +2772,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.188" @@ -2856,6 +2900,12 @@ dependencies = [ "dirs", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -3307,7 +3357,10 @@ dependencies = [ "rlimit", "time", "tokio", + "tor-circmgr", "tor-config", + "tor-dirmgr", + "tor-hsservice", "tor-rtcompat", ] @@ -3364,7 +3417,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31a0ef0674d08e4ec1e7a6a8e0129784379463c72406aca987e82fdea9f4f0fd" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", "bytes", "caret", "derive_more", @@ -3390,6 +3443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bb3afa49a44e1610c03b6142337ba0c4de1a6d70aea59849878de8876099930" dependencies = [ "caret", + "derive_builder_fork_arti", "derive_more", "digest", "thiserror", @@ -3485,6 +3539,7 @@ dependencies = [ "tor-relay-selection", "tor-rtcompat", "tracing", + "visibility", "void", "weak-table", ] @@ -3725,6 +3780,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db824b336c082804882221188097f73bcd8f551da2f56144c7b560c971f44f9" dependencies = [ + "cipher", "data-encoding", "derive_more", "digest", @@ -3740,6 +3796,64 @@ dependencies = [ "tor-error", "tor-llcrypto", "tor-units", + "zeroize", +] + +[[package]] +name = "tor-hsservice" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ab7093a43a62ce62c962e99192bf91d544fbe46d2f35ec80a329cd52de0035" +dependencies = [ + "amplify", + "async-trait", + "base64ct", + "cfg-if", + "derive-deftly", + "derive_builder_fork_arti", + "derive_more", + "digest", + "educe", + "fs-mistrust", + "futures", + "growable-bloom-filter", + "hex", + "humantime", + "itertools 0.13.0", + "k12", + "once_cell", + "oneshot-fused-workaround", + "postage", + "rand", + "rand_core", + "retry-error", + "safelog", + "serde", + "serde_with", + "strum 0.26.3", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-circmgr", + "tor-config", + "tor-dirclient", + "tor-error", + "tor-hscrypto", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-log-ratelim", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tracing", + "void", ] [[package]] @@ -3854,7 +3968,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f493e5c390efa9625d5f44d0f33743fede0ff47bc1e4fada640a44e13004c963" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.6.0", "derive_more", "digest", "futures", @@ -3888,7 +4002,7 @@ checksum = "0fdce7a98b0d30652ca59c1e7c3595b2bc064eb805be56bc9b67a306a60d6592" dependencies = [ "amplify", "base64ct", - "bitflags 2.4.0", + "bitflags 2.6.0", "cipher", "derive_builder_fork_arti", "derive_more", @@ -3930,11 +4044,13 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b7942bb43a51129ae4e84124e82e48f96e453a6fb8381c5c2b23899116d411" dependencies = [ + "amplify", "derive-deftly", "derive_more", "filetime", "fs-mistrust", "fslock", + "fslock-guard", "futures", "itertools 0.13.0", "oneshot-fused-workaround", @@ -4590,6 +4706,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "xxhash-rust" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" + [[package]] name = "xz2" version = "0.1.7" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index f2ca521..a90440c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -14,10 +14,13 @@ crate-type = ["cdylib", "staticlib"] [dependencies] lazy_static = "1.4" tokio = { version = "1", features = ["full"] } -arti-client = { version = "0.22.0", features = ["static", "onion-service-client"] } +arti-client = { version = "0.22.0", features = ["static", "onion-service-client", "experimental-api"] } arti = { version = "1.2.7", features = ["experimental-api", "static"] } -tor-rtcompat = { version = "0.22.0", features = ["static"] } +tor-rtcompat = { version = "0.22.0", features = ["static", "native-tls", "tokio"] } tor-config = "0.22.0" +tor-circmgr = { version = "0.22.0", features = ["experimental-api"] } +tor-dirmgr = "0.22.0" +tor-hsservice = "0.22.0" log = "0.4.20" #android_log-sys = "0.3.1" rlimit = "0.10.1" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 327c4ca..1b40ca4 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -8,13 +8,16 @@ use arti::socks; use arti_client::config::CfgPath; use arti_client::{DormantMode, TorClient, TorClientConfig}; use lazy_static::lazy_static; -use std::ffi::{c_char, c_void, CStr}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::{io, ptr}; use tokio::runtime::{Builder, Runtime}; use tokio::task::JoinHandle; use tor_config::Listen; use tor_rtcompat::tokio::TokioNativeTlsRuntime; use tor_rtcompat::BlockOn; +use tor_dirmgr::Timeliness; +use tor_circmgr::isolation::StreamIsolation; +use tor_circmgr::DirInfo; pub use crate::error::tor_last_error_message; #[cfg(not(target_os = "windows"))] @@ -130,6 +133,62 @@ fn start_proxy( )) } +/// Get the exit node's identity (e.g., nickname or ID) for the given client. +#[no_mangle] +pub unsafe extern "C" fn tor_get_exit_node(client: *mut c_void) -> *mut c_char { + let client = { + assert!(!client.is_null()); + &*(client as *mut TorClient) + }; + + let runtime = client.runtime().clone(); + + let result = runtime.block_on(async { + if let Ok(netdir) = client.dirmgr().netdir(Timeliness::Timely) { + let circmgr = client.circmgr(); + + let ports = &[]; + let isolation = StreamIsolation::no_isolation(); + + // Get or launch an exit circuit. + match circmgr.get_or_launch_exit(DirInfo::Directory(&netdir), ports, isolation).await { + Ok(circuit) => { + let path = circuit.path_ref(); + if let Some(exit_hop) = path.hops().last() { + let exit_info = format!("{}", exit_hop); + Ok(exit_info) + } else { + Err("No exit hop in circuit".to_string()) + } + } + Err(e) => Err(format!("Failed to get circuit: {:?}", e)), + } + } else { + Err("No NetDir available".to_string()) + } + }); + + match result { + Ok(exit_info) => { + CString::new(exit_info).unwrap().into_raw() + } + Err(err) => { + update_last_error(std::io::Error::new(std::io::ErrorKind::Other, err)); + std::ptr::null_mut() + } + } +} + +/// Free a C string allocated by this library. +/// +/// Used to free memory allocated by functions like `tor_get_exit_node`. +#[no_mangle] +pub unsafe extern "C" fn tor_free_string(s: *mut c_char) { + if !s.is_null() { + drop(CString::from_raw(s)); + } +} + // Due to its simple signature this dummy function is the one added (unused) to iOS swift codebase to force Xcode to link the lib #[no_mangle] pub unsafe extern "C" fn tor_hello() { diff --git a/rust/target/tor.h b/rust/target/tor.h index 052f137..c50d6fb 100644 --- a/rust/target/tor.h +++ b/rust/target/tor.h @@ -16,6 +16,18 @@ void tor_client_set_dormant(void *client, bool soft_mode); void tor_proxy_stop(void *proxy); +/** + * Get the exit node's identity (e.g., nickname or ID) for the given client. + */ +char *tor_get_exit_node(void *client); + +/** + * Free a C string allocated by this library. + * + * Used to free memory allocated by functions like `tor_get_exit_node`. + */ +void tor_free_string(char *s); + void tor_hello(void); const char *tor_last_error_message(void);