mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-24 05:59:42 -04:00
refactor: Move libp2p-community-tor into monorepo (#484)
* refactor: Move libp2p-community-tor into monorepo * fmt
This commit is contained in:
parent
69ddd2486d
commit
db5d02ea3d
12 changed files with 1795 additions and 473 deletions
1199
Cargo.lock
generated
1199
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -2,6 +2,7 @@
|
|||
resolver = "2"
|
||||
members = [
|
||||
"electrum-pool",
|
||||
"libp2p-community-tor",
|
||||
"monero-rpc",
|
||||
"monero-rpc-pool",
|
||||
"monero-seed",
|
||||
|
@ -36,6 +37,13 @@ typeshare = "1.0"
|
|||
url = { version = "2", features = ["serde"] }
|
||||
uuid = { version = "1", features = ["v4"] }
|
||||
|
||||
# Tor/Arti crates
|
||||
arti-client = { git = "https://github.com/eigenwallet/arti", rev = "5db9ecbd2872d76243dd62be887efd67e4609c87", default-features = false }
|
||||
tor-cell = { git = "https://github.com/eigenwallet/arti", rev = "5db9ecbd2872d76243dd62be887efd67e4609c87" }
|
||||
tor-hsservice = { git = "https://github.com/eigenwallet/arti", rev = "5db9ecbd2872d76243dd62be887efd67e4609c87" }
|
||||
tor-proto = { git = "https://github.com/eigenwallet/arti", rev = "5db9ecbd2872d76243dd62be887efd67e4609c87" }
|
||||
tor-rtcompat = { git = "https://github.com/eigenwallet/arti", rev = "5db9ecbd2872d76243dd62be887efd67e4609c87" }
|
||||
|
||||
[patch.crates-io]
|
||||
# patch until new release https://github.com/thomaseizinger/rust-jsonrpc-client/pull/51
|
||||
jsonrpc_client = { git = "https://github.com/delta1/rust-jsonrpc-client.git", rev = "3b6081697cd616c952acb9c2f02d546357d35506" }
|
||||
|
|
47
libp2p-community-tor/CHANGELOG.md
Normal file
47
libp2p-community-tor/CHANGELOG.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# 0.4.1
|
||||
|
||||
- Remove double features: See [PR 21]
|
||||
- Correct typo in `src/lib.rs`: See [PR 21]
|
||||
- Update `CHANGELOG.md`: See [PR 21]
|
||||
|
||||
[PR 21]: https://github.com/umgefahren/libp2p-tor/pull/21
|
||||
|
||||
# 0.4.0
|
||||
|
||||
## Changes
|
||||
|
||||
- Updated dependencies: See [PR 18]
|
||||
- [`arti-client` to `v0.24.0`]
|
||||
- [`libp2p` to `v0.53.0`]
|
||||
- [`tor-rtcompat` to `v0.24.0`]
|
||||
- Add tracing: See [PR 18]
|
||||
- Update CI: See [PR 20]
|
||||
- `actions/checkout` to `v4`
|
||||
- Remove `arduino/setup-protoc`
|
||||
|
||||
## First time contributor
|
||||
|
||||
- @binarybaron
|
||||
|
||||
Thanks! :rocket:
|
||||
|
||||
[PR 18]: https://github.com/umgefahren/libp2p-tor/pull/18
|
||||
[PR 20]: https://github.com/umgefahren/libp2p-tor/pull/20
|
||||
|
||||
# 0.3.0-alpha
|
||||
|
||||
- Updated dependencies: See [PR 6].
|
||||
- [`arti-client` to `v0.8`
|
||||
|
||||
- Updated dependencies: See [PR 8].
|
||||
- `libp2p-core` to `v0.39`
|
||||
- `libp2p` to `0.51`
|
||||
|
||||
[PR 6]: https://github.com/umgefahren/libp2p-tor/pull/6
|
||||
[PR 8]: https://github.com/umgefahren/libp2p-tor/pull/8
|
||||
|
||||
# 0.2.0-alpha
|
||||
|
||||
- Updated dependencies:
|
||||
- [`libp2p` to `v0.50.0`](#2)
|
||||
- [`libp2p-core` to `v0.38.0`](#3)
|
47
libp2p-community-tor/Cargo.toml
Normal file
47
libp2p-community-tor/Cargo.toml
Normal file
|
@ -0,0 +1,47 @@
|
|||
[package]
|
||||
name = "libp2p-community-tor"
|
||||
version = "0.5.0"
|
||||
authors = ["umgefahren <hannes@umgefahren.xyz>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/umgefahren/libp2p-tor"
|
||||
resolver = "2"
|
||||
description = "Tor transport for libp2p."
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
arti-client = { workspace = true, features = ["tokio", "rustls", "onion-service-client", "static-sqlite"] }
|
||||
libp2p = { workspace = true, features = ["tokio", "tcp", "tls"] }
|
||||
|
||||
data-encoding = { version = "2.6.0" }
|
||||
tor-cell = { workspace = true, optional = true }
|
||||
tor-hsservice = { workspace = true, optional = true }
|
||||
tor-proto = { workspace = true, optional = true }
|
||||
tor-rtcompat = { workspace = true, features = ["tokio", "rustls"] }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
libp2p = { workspace = true, features = ["tokio", "noise", "yamux", "ping", "macros", "tcp", "tls"] }
|
||||
tokio = { workspace = true, features = ["macros"] }
|
||||
tokio-test = "0.4.4"
|
||||
tracing-subscriber = { workspace = true }
|
||||
|
||||
[features]
|
||||
listen-onion-service = [
|
||||
"arti-client/onion-service-service",
|
||||
"dep:tor-hsservice",
|
||||
"dep:tor-cell",
|
||||
"dep:tor-proto",
|
||||
]
|
||||
|
||||
[[example]]
|
||||
name = "ping-onion"
|
||||
required-features = ["listen-onion-service"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
21
libp2p-community-tor/LICENSE
Normal file
21
libp2p-community-tor/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Hannes Furmans
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
51
libp2p-community-tor/README.md
Normal file
51
libp2p-community-tor/README.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
[](https://github.com/umgefahren/libp2p-tor/actions/workflows/ci.yml)
|
||||
[](https://docs.rs/libp2p-community-tor/latest)
|
||||
[](https://crates.io/crates/libp2p-community-tor)
|
||||
|
||||
# libp2p Tor
|
||||
|
||||
Tor based transport for libp2p. Connect through the Tor network to TCP listeners.
|
||||
|
||||
Build on top of [Arti](https://gitlab.torproject.org/tpo/core/arti).
|
||||
|
||||
## New Feature
|
||||
|
||||
This crate supports, since #21 (thanks to @binarybaron), listening as a Tor hidden service as well as connecting to them.
|
||||
|
||||
## ⚠️ Misuse warning ⚠️ - read carefully before using
|
||||
|
||||
Although the sound of "Tor" might convey a sense of security it is _very_ easy to misuse this
|
||||
crate and leaking private information while using. Study libp2p carefully and try to make sure
|
||||
you fully understand it's current limits regarding privacy. I.e. using identify might already
|
||||
render this transport obsolete.
|
||||
|
||||
This transport explicitly **doesn't** provide any enhanced privacy if it's just used like a regular transport.
|
||||
Use with caution and at your own risk. **Don't** just blindly advertise Tor without fully understanding what you
|
||||
are dealing with.
|
||||
|
||||
### Add to your dependencies
|
||||
|
||||
```bash
|
||||
cargo add libp2p-community-tor
|
||||
```
|
||||
|
||||
This crate uses tokio with rustls for its runtime and TLS implementation.
|
||||
No other combinations are supported.
|
||||
|
||||
- [`rustls`](https://github.com/rustls/rustls)
|
||||
- [`tokio`](https://github.com/tokio-rs/tokio)
|
||||
|
||||
### Example
|
||||
|
||||
```rust
|
||||
let address = "/dns/www.torproject.org/tcp/1000".parse()?;
|
||||
let mut transport = libp2p_community_tor::TorTransport::bootstrapped().await?;
|
||||
// we have achieved tor connection
|
||||
let _conn = transport.dial(address)?.await?;
|
||||
```
|
||||
|
||||
### About
|
||||
|
||||
This crate originates in a PR to bring Tor support too rust-libp2p. Read more about it here: libp2p/rust-libp2p#2899
|
||||
|
||||
License: MIT
|
159
libp2p-community-tor/examples/ping-onion.rs
Normal file
159
libp2p-community-tor/examples/ping-onion.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2022 Hannes Furmans
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
//! Ping-Onion example
|
||||
//!
|
||||
//! See ../src/tutorial.rs for a step-by-step guide building the example below.
|
||||
//!
|
||||
//! This example requires two seperate computers, one of which has to be reachable from the
|
||||
//! internet.
|
||||
//!
|
||||
//! On the first computer run:
|
||||
//! ```sh
|
||||
//! cargo run --example ping
|
||||
//! ```
|
||||
//!
|
||||
//! It will print the PeerId and the listening addresses, e.g. `Listening on
|
||||
//! "/ip4/0.0.0.0/tcp/24915"`
|
||||
//!
|
||||
//! Make sure that the first computer is reachable under one of these ip addresses and port.
|
||||
//!
|
||||
//! On the second computer run:
|
||||
//! ```sh
|
||||
//! cargo run --example ping-onion -- /ip4/123.45.67.89/tcp/24915
|
||||
//! ```
|
||||
//!
|
||||
//! The two nodes establish a connection, negotiate the ping protocol
|
||||
//! and begin pinging each other over Tor.
|
||||
|
||||
use futures::StreamExt;
|
||||
use libp2p::core::upgrade::Version;
|
||||
use libp2p::Transport;
|
||||
use libp2p::{
|
||||
core::muxing::StreamMuxerBox,
|
||||
identity, noise,
|
||||
swarm::{NetworkBehaviour, SwarmEvent},
|
||||
yamux, Multiaddr, PeerId, SwarmBuilder,
|
||||
};
|
||||
use libp2p_community_tor::{AddressConversion, TorTransport};
|
||||
use std::error::Error;
|
||||
use tor_hsservice::config::OnionServiceConfigBuilder;
|
||||
|
||||
/// Create a transport
|
||||
/// Returns a tuple of the transport and the onion address we can instruct it to listen on
|
||||
async fn onion_transport(
|
||||
keypair: identity::Keypair,
|
||||
) -> Result<
|
||||
(
|
||||
libp2p::core::transport::Boxed<(PeerId, libp2p::core::muxing::StreamMuxerBox)>,
|
||||
Multiaddr,
|
||||
),
|
||||
Box<dyn Error>,
|
||||
> {
|
||||
let mut transport = TorTransport::bootstrapped()
|
||||
.await?
|
||||
.with_address_conversion(AddressConversion::IpAndDns);
|
||||
|
||||
// We derive the nickname for the onion address from the peer id
|
||||
let svg_cfg = OnionServiceConfigBuilder::default()
|
||||
.nickname(
|
||||
keypair
|
||||
.public()
|
||||
.to_peer_id()
|
||||
.to_base58()
|
||||
.to_ascii_lowercase()
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.num_intro_points(3)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let onion_listen_address = transport.add_onion_service(svg_cfg, 999).unwrap();
|
||||
|
||||
let auth_upgrade = noise::Config::new(&keypair)?;
|
||||
let multiplex_upgrade = yamux::Config::default();
|
||||
|
||||
let transport = transport
|
||||
.boxed()
|
||||
.upgrade(Version::V1)
|
||||
.authenticate(auth_upgrade)
|
||||
.multiplex(multiplex_upgrade)
|
||||
.map(|(peer, muxer), _| (peer, StreamMuxerBox::new(muxer)))
|
||||
.boxed();
|
||||
|
||||
Ok((transport, onion_listen_address))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let local_key = identity::Keypair::generate_ed25519();
|
||||
let local_peer_id = PeerId::from(local_key.public());
|
||||
|
||||
println!("Local peer id: {local_peer_id}");
|
||||
|
||||
let (transport, onion_listen_address) = onion_transport(local_key).await?;
|
||||
|
||||
let mut swarm = SwarmBuilder::with_new_identity()
|
||||
.with_tokio()
|
||||
.with_other_transport(|_| transport)
|
||||
.unwrap()
|
||||
.with_behaviour(|_| Behaviour {
|
||||
ping: libp2p::ping::Behaviour::default(),
|
||||
})
|
||||
.unwrap()
|
||||
.build();
|
||||
|
||||
// Dial the peer identified by the multi-address given as the second
|
||||
// command-line argument, if any.
|
||||
if let Some(addr) = std::env::args().nth(1) {
|
||||
let remote: Multiaddr = addr.parse()?;
|
||||
swarm.dial(remote)?;
|
||||
println!("Dialed {addr}")
|
||||
} else {
|
||||
// If we are not dialing, we need to listen
|
||||
// Tell the swarm to listen on a specific onion address
|
||||
swarm.listen_on(onion_listen_address).unwrap();
|
||||
}
|
||||
|
||||
loop {
|
||||
match swarm.select_next_some().await {
|
||||
SwarmEvent::ConnectionEstablished {
|
||||
endpoint, peer_id, ..
|
||||
} => {
|
||||
println!("Connection established with {peer_id} on {endpoint:?}");
|
||||
}
|
||||
SwarmEvent::OutgoingConnectionError { peer_id, error, .. } => {
|
||||
println!("Outgoing connection error with {peer_id:?}: {error:?}");
|
||||
}
|
||||
SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"),
|
||||
SwarmEvent::Behaviour(event) => println!("{event:?}"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Our network behaviour.
|
||||
#[derive(NetworkBehaviour)]
|
||||
struct Behaviour {
|
||||
ping: libp2p::ping::Behaviour,
|
||||
}
|
176
libp2p-community-tor/src/address.rs
Normal file
176
libp2p-community-tor/src/address.rs
Normal file
|
@ -0,0 +1,176 @@
|
|||
// Copyright 2022 Hannes Furmans
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
use arti_client::{DangerouslyIntoTorAddr, IntoTorAddr, TorAddr};
|
||||
use libp2p::{core::multiaddr::Protocol, multiaddr::Onion3Addr, Multiaddr};
|
||||
use std::net::SocketAddr;
|
||||
|
||||
/// "Dangerously" extract a Tor address from the provided [`Multiaddr`].
|
||||
///
|
||||
/// See [`DangerouslyIntoTorAddr`] for details around the safety / privacy considerations.
|
||||
pub fn dangerous_extract(multiaddr: &Multiaddr) -> Option<TorAddr> {
|
||||
if let Some(tor_addr) = safe_extract(multiaddr) {
|
||||
return Some(tor_addr);
|
||||
}
|
||||
|
||||
let mut protocols = multiaddr.into_iter();
|
||||
|
||||
let tor_addr = try_to_socket_addr(&protocols.next()?, &protocols.next()?)?
|
||||
.into_tor_addr_dangerously()
|
||||
.ok()?;
|
||||
|
||||
Some(tor_addr)
|
||||
}
|
||||
|
||||
/// "Safely" extract a Tor address from the provided [`Multiaddr`].
|
||||
///
|
||||
/// See [`IntoTorAddr`] for details around the safety / privacy considerations.
|
||||
pub fn safe_extract(multiaddr: &Multiaddr) -> Option<TorAddr> {
|
||||
let mut protocols = multiaddr.into_iter();
|
||||
|
||||
let tor_addr = try_to_domain_and_port(&protocols.next()?, &protocols.next())?
|
||||
.into_tor_addr()
|
||||
.ok()?;
|
||||
|
||||
Some(tor_addr)
|
||||
}
|
||||
|
||||
fn libp2p_onion_address_to_domain_and_port<'a>(
|
||||
onion_address: &'a Onion3Addr<'_>,
|
||||
) -> (&'a str, u16) {
|
||||
// Here we convert from Onion3Addr to TorAddr
|
||||
// We need to leak the string because it's a temporary string that would otherwise be freed
|
||||
let hash = data_encoding::BASE32.encode(onion_address.hash());
|
||||
let onion_domain = format!("{hash}.onion");
|
||||
let onion_domain = Box::leak(onion_domain.into_boxed_str());
|
||||
|
||||
(onion_domain, onion_address.port())
|
||||
}
|
||||
|
||||
fn try_to_domain_and_port<'a>(
|
||||
maybe_domain: &'a Protocol,
|
||||
maybe_port: &Option<Protocol>,
|
||||
) -> Option<(&'a str, u16)> {
|
||||
match (maybe_domain, maybe_port) {
|
||||
(
|
||||
Protocol::Dns(domain) | Protocol::Dns4(domain) | Protocol::Dns6(domain),
|
||||
Some(Protocol::Tcp(port)),
|
||||
) => Some((domain.as_ref(), *port)),
|
||||
(Protocol::Onion3(domain), _) => Some(libp2p_onion_address_to_domain_and_port(domain)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn try_to_socket_addr(maybe_ip: &Protocol, maybe_port: &Protocol) -> Option<SocketAddr> {
|
||||
match (maybe_ip, maybe_port) {
|
||||
(Protocol::Ip4(ip), Protocol::Tcp(port)) => Some(SocketAddr::from((*ip, *port))),
|
||||
(Protocol::Ip6(ip), Protocol::Tcp(port)) => Some(SocketAddr::from((*ip, *port))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use arti_client::TorAddr;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
#[test]
|
||||
fn extract_correct_address_from_dns() {
|
||||
let addresses = [
|
||||
"/dns/ip.tld/tcp/10".parse().unwrap(),
|
||||
"/dns4/dns.ip4.tld/tcp/11".parse().unwrap(),
|
||||
"/dns6/dns.ip6.tld/tcp/12".parse().unwrap(),
|
||||
];
|
||||
|
||||
let actual = addresses
|
||||
.iter()
|
||||
.filter_map(safe_extract)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
&[
|
||||
TorAddr::from(("ip.tld", 10)).unwrap(),
|
||||
TorAddr::from(("dns.ip4.tld", 11)).unwrap(),
|
||||
TorAddr::from(("dns.ip6.tld", 12)).unwrap(),
|
||||
],
|
||||
actual.as_slice()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_correct_address_from_ips() {
|
||||
let addresses = [
|
||||
"/ip4/127.0.0.1/tcp/10".parse().unwrap(),
|
||||
"/ip6/::1/tcp/10".parse().unwrap(),
|
||||
];
|
||||
|
||||
let actual = addresses
|
||||
.iter()
|
||||
.filter_map(dangerous_extract)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
&[
|
||||
TorAddr::dangerously_from((Ipv4Addr::LOCALHOST, 10)).unwrap(),
|
||||
TorAddr::dangerously_from((Ipv6Addr::LOCALHOST, 10)).unwrap(),
|
||||
],
|
||||
actual.as_slice()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dangerous_extract_works_on_domains_too() {
|
||||
let addresses = [
|
||||
"/dns/ip.tld/tcp/10".parse().unwrap(),
|
||||
"/ip4/127.0.0.1/tcp/10".parse().unwrap(),
|
||||
"/ip6/::1/tcp/10".parse().unwrap(),
|
||||
];
|
||||
|
||||
let actual = addresses
|
||||
.iter()
|
||||
.filter_map(dangerous_extract)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
&[
|
||||
TorAddr::from(("ip.tld", 10)).unwrap(),
|
||||
TorAddr::dangerously_from((Ipv4Addr::LOCALHOST, 10)).unwrap(),
|
||||
TorAddr::dangerously_from((Ipv6Addr::LOCALHOST, 10)).unwrap(),
|
||||
],
|
||||
actual.as_slice()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detect_incorrect_address() {
|
||||
let addresses = [
|
||||
"/tcp/10/udp/12".parse().unwrap(),
|
||||
"/dns/ip.tld/dns4/ip.tld/dns6/ip.tld".parse().unwrap(),
|
||||
"/tcp/10/ip4/1.1.1.1".parse().unwrap(),
|
||||
];
|
||||
|
||||
let all_correct = addresses.iter().map(safe_extract).all(|res| res.is_none());
|
||||
|
||||
assert!(
|
||||
all_correct,
|
||||
"During the parsing of the faulty addresses, there was an incorrectness"
|
||||
);
|
||||
}
|
||||
}
|
466
libp2p-community-tor/src/lib.rs
Normal file
466
libp2p-community-tor/src/lib.rs
Normal file
|
@ -0,0 +1,466 @@
|
|||
// Copyright 2022 Hannes Furmans
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![deny(unsafe_code)]
|
||||
//! Tor based transport for libp2p. Connect through the Tor network to TCP listeners.
|
||||
//!
|
||||
//! # ⚠️ Misuse warning ⚠️ - read carefully before using
|
||||
//! Although the sound of "Tor" might convey a sense of security it is *very* easy to misuse this
|
||||
//! crate and leaking private information while using. Study libp2p carefully and try to make sure
|
||||
//! you fully understand it's current limits regarding privacy. I.e. using identify might already
|
||||
//! render this transport obsolete.
|
||||
//!
|
||||
//! This transport explicitly **doesn't** provide any enhanced privacy if it's just used like a regular transport.
|
||||
//! Use with caution and at your own risk. **Don't** just blindly advertise Tor without fully understanding what you
|
||||
//! are dealing with.
|
||||
//!
|
||||
//! ## Runtime
|
||||
//!
|
||||
//! This crate uses tokio with rustls for its runtime and TLS implementation.
|
||||
//! No other combinations are supported.
|
||||
//!
|
||||
//! ## Example
|
||||
//! ```no_run
|
||||
//! use libp2p::core::Transport;
|
||||
//! # async fn test_func() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let address = "/dns/www.torproject.org/tcp/1000".parse()?;
|
||||
//! let mut transport = libp2p_community_tor::TorTransport::bootstrapped().await?;
|
||||
//! // we have achieved tor connection
|
||||
//! let _conn = transport.dial(address)?.await?;
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! # tokio_test::block_on(test_func());
|
||||
//! ```
|
||||
|
||||
use arti_client::{TorClient, TorClientBuilder};
|
||||
use futures::future::BoxFuture;
|
||||
use libp2p::{
|
||||
core::transport::{ListenerId, TransportEvent},
|
||||
Multiaddr, Transport, TransportError,
|
||||
};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use thiserror::Error;
|
||||
use tor_rtcompat::tokio::TokioRustlsRuntime;
|
||||
|
||||
// We only need these imports if the `listen-onion-service` feature is enabled
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
use std::collections::HashMap;
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
use std::str::FromStr;
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
use tor_cell::relaycell::msg::{Connected, End, EndReason};
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
use tor_hsservice::{
|
||||
handle_rend_requests, status::OnionServiceStatus, HsId, OnionServiceConfig,
|
||||
RunningOnionService, StreamRequest,
|
||||
};
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
use tor_proto::stream::IncomingStreamRequest;
|
||||
|
||||
mod address;
|
||||
mod provider;
|
||||
|
||||
use address::{dangerous_extract, safe_extract};
|
||||
pub use provider::TokioTorStream;
|
||||
|
||||
pub type TorError = arti_client::Error;
|
||||
|
||||
type PendingUpgrade = BoxFuture<'static, Result<TokioTorStream, TorTransportError>>;
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
type OnionServiceStream = futures::stream::BoxStream<'static, StreamRequest>;
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
type OnionServiceStatusStream = futures::stream::BoxStream<'static, OnionServiceStatus>;
|
||||
|
||||
/// Struct representing an onion address we are listening on for libp2p connections.
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
struct TorListener {
|
||||
#[allow(dead_code)] // We need to own this to keep the RunningOnionService alive
|
||||
/// The onion service we are listening on
|
||||
service: Arc<RunningOnionService>,
|
||||
/// The stream of status updates for the onion service
|
||||
status_stream: OnionServiceStatusStream,
|
||||
/// The stream incoming [`StreamRequest`]s
|
||||
request_stream: OnionServiceStream,
|
||||
|
||||
/// The port we are listening on
|
||||
port: u16,
|
||||
/// The onion address we are listening on
|
||||
onion_address: Multiaddr,
|
||||
/// Whether we have already announced this address
|
||||
announced: bool,
|
||||
}
|
||||
|
||||
/// Mode of address conversion.
|
||||
/// Refer tor [arti_client::TorAddr](https://docs.rs/arti-client/latest/arti_client/struct.TorAddr.html) for details
|
||||
#[derive(Debug, Clone, Copy, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum AddressConversion {
|
||||
/// Uses only DNS for address resolution (default).
|
||||
#[default]
|
||||
DnsOnly,
|
||||
/// Uses IP and DNS for addresses.
|
||||
IpAndDns,
|
||||
}
|
||||
|
||||
pub struct TorTransport {
|
||||
pub conversion_mode: AddressConversion,
|
||||
|
||||
/// The Tor client.
|
||||
client: Arc<TorClient<TokioRustlsRuntime>>,
|
||||
|
||||
/// Onion services we are listening on.
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
listeners: HashMap<ListenerId, TorListener>,
|
||||
|
||||
/// Onion services we are running but currently not listening on
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
services: Vec<(Arc<RunningOnionService>, OnionServiceStream)>,
|
||||
}
|
||||
|
||||
impl TorTransport {
|
||||
/// Creates a new `TorClientBuilder`.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if the current runtime is not a `TokioRustlsRuntime`.
|
||||
pub fn builder() -> TorClientBuilder<TokioRustlsRuntime> {
|
||||
let runtime =
|
||||
TokioRustlsRuntime::current().expect("Couldn't get the current tokio rustls runtime");
|
||||
TorClient::with_runtime(runtime)
|
||||
}
|
||||
|
||||
/// Creates a bootstrapped `TorTransport`
|
||||
///
|
||||
/// # Errors
|
||||
/// Could return error emitted during Tor bootstrap by Arti.
|
||||
pub async fn bootstrapped() -> Result<Self, TorError> {
|
||||
let builder = Self::builder();
|
||||
let ret = Self::from_builder(&builder, AddressConversion::DnsOnly)?;
|
||||
ret.bootstrap().await?;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Builds a `TorTransport` from an Arti `TorClientBuilder` but does not bootstrap it.
|
||||
///
|
||||
/// # Errors
|
||||
/// Could return error emitted during creation of the `TorClient`.
|
||||
pub fn from_builder(
|
||||
builder: &TorClientBuilder<TokioRustlsRuntime>,
|
||||
conversion_mode: AddressConversion,
|
||||
) -> Result<Self, TorError> {
|
||||
let client = Arc::new(builder.create_unbootstrapped()?);
|
||||
|
||||
Ok(Self::from_client(client, conversion_mode))
|
||||
}
|
||||
|
||||
/// Builds a `TorTransport` from an existing Arti `TorClient`.
|
||||
pub fn from_client(
|
||||
client: Arc<TorClient<TokioRustlsRuntime>>,
|
||||
conversion_mode: AddressConversion,
|
||||
) -> Self {
|
||||
Self {
|
||||
conversion_mode,
|
||||
client,
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
listeners: HashMap::new(),
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
services: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Bootstraps the `TorTransport` into the Tor network.
|
||||
///
|
||||
/// # Errors
|
||||
/// Could return error emitted during bootstrap by Arti.
|
||||
pub async fn bootstrap(&self) -> Result<(), TorError> {
|
||||
self.client.bootstrap().await
|
||||
}
|
||||
|
||||
/// Set the address conversion mode
|
||||
#[must_use]
|
||||
pub fn with_address_conversion(mut self, conversion_mode: AddressConversion) -> Self {
|
||||
self.conversion_mode = conversion_mode;
|
||||
self
|
||||
}
|
||||
|
||||
/// Call this function to instruct the transport to listen on a specific onion address
|
||||
/// You need to call this function **before** calling `listen_on`
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns the Multiaddr of the onion address that the transport can be instructed to listen on
|
||||
/// To actually listen on the address, you need to call [`listen_on`] with the returned address
|
||||
///
|
||||
/// # Errors
|
||||
/// Returns an error if we cannot get the onion address of the service
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
pub fn add_onion_service(
|
||||
&mut self,
|
||||
svc_cfg: OnionServiceConfig,
|
||||
port: u16,
|
||||
) -> anyhow::Result<Multiaddr> {
|
||||
let (service, request_stream) = self.client.launch_onion_service(svc_cfg)?;
|
||||
let request_stream = Box::pin(handle_rend_requests(request_stream));
|
||||
|
||||
let multiaddr = service
|
||||
.onion_name()
|
||||
.ok_or_else(|| anyhow::anyhow!("Onion service has no onion address"))?
|
||||
.to_multiaddr(port);
|
||||
|
||||
self.services.push((service, request_stream));
|
||||
|
||||
Ok(multiaddr)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum TorTransportError {
|
||||
#[error(transparent)]
|
||||
Client(#[from] TorError),
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
#[error(transparent)]
|
||||
Service(#[from] tor_hsservice::ClientError),
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
#[error("Stream closed before receiving data")]
|
||||
StreamClosed,
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
#[error("Stream port does not match listener port")]
|
||||
StreamPortMismatch,
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
#[error("Onion service is broken")]
|
||||
Broken,
|
||||
}
|
||||
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
trait HsIdExt {
|
||||
fn to_multiaddr(&self, port: u16) -> Multiaddr;
|
||||
}
|
||||
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
impl HsIdExt for HsId {
|
||||
/// Convert an `HsId` to a `Multiaddr`
|
||||
fn to_multiaddr(&self, port: u16) -> Multiaddr {
|
||||
let onion_domain = self.to_string();
|
||||
let onion_without_dot_onion = onion_domain
|
||||
.split('.')
|
||||
.nth(0)
|
||||
.expect("Display formatting of HsId to contain .onion suffix");
|
||||
let multiaddress_string = format!("/onion3/{onion_without_dot_onion}:{port}");
|
||||
|
||||
Multiaddr::from_str(&multiaddress_string)
|
||||
.expect("A valid onion address to be convertible to a Multiaddr")
|
||||
}
|
||||
}
|
||||
|
||||
impl Transport for TorTransport {
|
||||
type Output = TokioTorStream;
|
||||
type Error = TorTransportError;
|
||||
type Dial = BoxFuture<'static, Result<Self::Output, Self::Error>>;
|
||||
type ListenerUpgrade = PendingUpgrade;
|
||||
|
||||
#[cfg(not(feature = "listen-onion-service"))]
|
||||
fn listen_on(
|
||||
&mut self,
|
||||
_id: ListenerId,
|
||||
onion_address: Multiaddr,
|
||||
) -> Result<(), TransportError<Self::Error>> {
|
||||
// If the `listen-onion-service` feature is not enabled, we do not support listening
|
||||
Err(TransportError::MultiaddrNotSupported(onion_address.clone()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
fn listen_on(
|
||||
&mut self,
|
||||
id: ListenerId,
|
||||
onion_address: Multiaddr,
|
||||
) -> Result<(), TransportError<Self::Error>> {
|
||||
// If the address is not an onion3 address, return an error
|
||||
let Some(libp2p::multiaddr::Protocol::Onion3(address)) = onion_address.into_iter().nth(0)
|
||||
else {
|
||||
return Err(TransportError::MultiaddrNotSupported(onion_address.clone()));
|
||||
};
|
||||
|
||||
// Find the running onion service that matches the requested address
|
||||
// If we find it, remove it from [`services`] and insert it into [`listeners`]
|
||||
let position = self
|
||||
.services
|
||||
.iter()
|
||||
.position(|(service, _)| {
|
||||
service.onion_name().map_or(false, |name| {
|
||||
name.to_multiaddr(address.port()) == onion_address
|
||||
})
|
||||
})
|
||||
.ok_or_else(|| TransportError::MultiaddrNotSupported(onion_address.clone()))?;
|
||||
|
||||
let (service, request_stream) = self.services.remove(position);
|
||||
|
||||
let status_stream = Box::pin(service.status_events());
|
||||
|
||||
self.listeners.insert(
|
||||
id,
|
||||
TorListener {
|
||||
service,
|
||||
request_stream,
|
||||
onion_address: onion_address.clone(),
|
||||
port: address.port(),
|
||||
status_stream,
|
||||
announced: false,
|
||||
},
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// We do not support removing listeners if the `listen-onion-service` feature is not enabled
|
||||
#[cfg(not(feature = "listen-onion-service"))]
|
||||
fn remove_listener(&mut self, _id: ListenerId) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
fn remove_listener(&mut self, id: ListenerId) -> bool {
|
||||
// Take the listener out of the map. This will stop listening on onion service for libp2p connections (we will not poll it anymore)
|
||||
// However, we will not stop the onion service itself because we might want to reuse it later
|
||||
// The onion service will be stopped when the transport is dropped
|
||||
if let Some(listener) = self.listeners.remove(&id) {
|
||||
self.services
|
||||
.push((listener.service, listener.request_stream));
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn dial(&mut self, addr: Multiaddr) -> Result<Self::Dial, TransportError<Self::Error>> {
|
||||
let maybe_tor_addr = match self.conversion_mode {
|
||||
AddressConversion::DnsOnly => safe_extract(&addr),
|
||||
AddressConversion::IpAndDns => dangerous_extract(&addr),
|
||||
};
|
||||
|
||||
let tor_address =
|
||||
maybe_tor_addr.ok_or(TransportError::MultiaddrNotSupported(addr.clone()))?;
|
||||
let onion_client = self.client.clone();
|
||||
|
||||
Ok(Box::pin(async move {
|
||||
let stream = onion_client.connect(tor_address).await?;
|
||||
|
||||
tracing::debug!(%addr, "Established connection to peer through Tor");
|
||||
|
||||
Ok(TokioTorStream::from(stream))
|
||||
}))
|
||||
}
|
||||
|
||||
fn dial_as_listener(
|
||||
&mut self,
|
||||
addr: Multiaddr,
|
||||
) -> Result<Self::Dial, TransportError<Self::Error>> {
|
||||
self.dial(addr)
|
||||
}
|
||||
|
||||
fn address_translation(&self, _listen: &Multiaddr, _observed: &Multiaddr) -> Option<Multiaddr> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "listen-onion-service"))]
|
||||
fn poll(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<TransportEvent<Self::ListenerUpgrade, Self::Error>> {
|
||||
// If the `listen-onion-service` feature is not enabled, we do not support listening
|
||||
Poll::Pending
|
||||
}
|
||||
|
||||
#[cfg(feature = "listen-onion-service")]
|
||||
fn poll(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<TransportEvent<Self::ListenerUpgrade, Self::Error>> {
|
||||
for (listener_id, listener) in &mut self.listeners {
|
||||
// Check if the service has any new statuses
|
||||
if let Poll::Ready(Some(status)) = listener.status_stream.as_mut().poll_next(cx) {
|
||||
tracing::debug!(
|
||||
status = ?status.state(),
|
||||
address = listener.onion_address.to_string(),
|
||||
"Onion service status changed"
|
||||
);
|
||||
}
|
||||
|
||||
// Check if we have already announced this address, if not, do it now
|
||||
if !listener.announced {
|
||||
listener.announced = true;
|
||||
|
||||
// We announce the address here to the swarm even though we technically cannot guarantee
|
||||
// that the address is reachable yet from the outside. We might not have registered the
|
||||
// onion service fully yet (introduction points, hsdir, ...)
|
||||
//
|
||||
// However, we need to announce it now because otherwise libp2p might not poll the listener
|
||||
// again and we will not be able to announce it later.
|
||||
// TODO: Find out why this is the case, if this is intended behaviour or a bug
|
||||
return Poll::Ready(TransportEvent::NewAddress {
|
||||
listener_id: *listener_id,
|
||||
listen_addr: listener.onion_address.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
match listener.request_stream.as_mut().poll_next(cx) {
|
||||
Poll::Ready(Some(request)) => {
|
||||
let port = listener.port;
|
||||
let upgrade: PendingUpgrade = Box::pin(async move {
|
||||
// Check if the port matches what we expect
|
||||
if let IncomingStreamRequest::Begin(begin) = request.request() {
|
||||
if begin.port() != port {
|
||||
// Reject the connection with CONNECTREFUSED
|
||||
request
|
||||
.reject(End::new_with_reason(EndReason::CONNECTREFUSED))
|
||||
.await?;
|
||||
|
||||
return Err(TorTransportError::StreamPortMismatch);
|
||||
}
|
||||
}
|
||||
|
||||
// Accept the stream and forward it to the swarm
|
||||
let data_stream = request.accept(Connected::new_empty()).await?;
|
||||
Ok(TokioTorStream::from(data_stream))
|
||||
});
|
||||
|
||||
return Poll::Ready(TransportEvent::Incoming {
|
||||
listener_id: *listener_id,
|
||||
upgrade,
|
||||
local_addr: listener.onion_address.clone(),
|
||||
send_back_addr: listener.onion_address.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
// The stream has ended
|
||||
// This means that the onion service was shut down, and we will not receive any more connections on it
|
||||
Poll::Ready(None) => {
|
||||
return Poll::Ready(TransportEvent::ListenerClosed {
|
||||
listener_id: *listener_id,
|
||||
reason: Ok(()),
|
||||
});
|
||||
}
|
||||
Poll::Pending => {}
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
86
libp2p-community-tor/src/provider.rs
Normal file
86
libp2p-community-tor/src/provider.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2022 Hannes Furmans
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use arti_client::DataStream;
|
||||
use futures::{AsyncRead, AsyncWrite};
|
||||
use tokio::io::{AsyncRead as TokioAsyncRead, AsyncWrite as TokioAsyncWrite, ReadBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TokioTorStream {
|
||||
inner: DataStream,
|
||||
}
|
||||
|
||||
impl From<DataStream> for TokioTorStream {
|
||||
fn from(inner: DataStream) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for TokioTorStream {
|
||||
fn poll_read(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> std::task::Poll<std::io::Result<usize>> {
|
||||
let mut read_buf = ReadBuf::new(buf);
|
||||
futures::ready!(TokioAsyncRead::poll_read(
|
||||
std::pin::Pin::new(&mut self.inner),
|
||||
cx,
|
||||
&mut read_buf
|
||||
))?;
|
||||
std::task::Poll::Ready(Ok(read_buf.filled().len()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for TokioTorStream {
|
||||
#[inline]
|
||||
fn poll_write(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> std::task::Poll<std::io::Result<usize>> {
|
||||
TokioAsyncWrite::poll_write(std::pin::Pin::new(&mut self.inner), cx, buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<std::io::Result<()>> {
|
||||
TokioAsyncWrite::poll_flush(std::pin::Pin::new(&mut self.inner), cx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_close(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<std::io::Result<()>> {
|
||||
TokioAsyncWrite::poll_shutdown(std::pin::Pin::new(&mut self.inner), cx)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_write_vectored(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> std::task::Poll<std::io::Result<usize>> {
|
||||
TokioAsyncWrite::poll_write_vectored(std::pin::Pin::new(&mut self.inner), cx, bufs)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
[toolchain]
|
||||
# also update this in the readme, changelog, and github actions
|
||||
channel = "1.85"
|
||||
channel = "1.87"
|
||||
components = ["clippy"]
|
||||
targets = ["armv7-unknown-linux-gnueabihf"]
|
||||
|
|
|
@ -13,7 +13,7 @@ tauri = ["dep:tauri", "dep:dfx-swiss-sdk"]
|
|||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
arti-client = { version = "0.25.0", features = ["static-sqlite", "tokio", "rustls", "onion-service-service"], default-features = false }
|
||||
arti-client = { workspace = true, features = ["static-sqlite", "tokio", "rustls", "onion-service-service"] }
|
||||
async-compression = { version = "0.3", features = ["bzip2", "tokio"] }
|
||||
async-trait = "0.1"
|
||||
asynchronous-codec = "0.7.0"
|
||||
|
@ -42,7 +42,7 @@ fns = "0.0.7"
|
|||
futures = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
libp2p = { workspace = true, features = ["tcp", "yamux", "dns", "noise", "request-response", "ping", "rendezvous", "identify", "macros", "cbor", "json", "tokio", "serde", "rsa"] }
|
||||
libp2p-community-tor = { git = "https://github.com/umgefahren/libp2p-tor", rev = "e6b913e0f1ac1fc90b3ee4dd31b5511140c4a9af", features = ["listen-onion-service"] }
|
||||
libp2p-community-tor = { path = "../libp2p-community-tor", features = ["listen-onion-service"] }
|
||||
moka = { version = "0.12", features = ["sync", "future"] }
|
||||
monero = { workspace = true }
|
||||
monero-rpc = { path = "../monero-rpc" }
|
||||
|
@ -80,7 +80,7 @@ time = "0.3"
|
|||
tokio = { workspace = true, features = ["process", "fs", "net", "parking_lot", "rt"] }
|
||||
tokio-tungstenite = { version = "0.15", features = ["rustls-tls"] }
|
||||
tokio-util = { version = "0.7", features = ["io", "codec", "rt"] }
|
||||
tor-rtcompat = { version = "0.25.0", features = ["tokio"] }
|
||||
tor-rtcompat = { workspace = true, features = ["tokio"] }
|
||||
tower = { version = "0.4.13", features = ["full"] }
|
||||
tower-http = { version = "0.3.4", features = ["full"] }
|
||||
tracing = { workspace = true }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue