Skip to content

Remove arti dependency in favor of tor proxy#319

Merged
rustaceanrob merged 3 commits intomasterfrom
remove-arti-3-29
Mar 31, 2025
Merged

Remove arti dependency in favor of tor proxy#319
rustaceanrob merged 3 commits intomasterfrom
remove-arti-3-29

Conversation

@rustaceanrob
Copy link
Copy Markdown
Collaborator

Having arti as a dependency has restricted the ability to make an MSRV guarantee for users. It is also difficult to say if it would even be used as a feature, but users can still take advantage of Tor without arti. Similar to the Electrum crates in the ecosystem, we can direct traffic to a Socks5 proxy that points at the Tor daemon. Again, the market is yet to be seen for using Tor specifically, so a simple local proxy pointing to 127.0.0.1:9050 should be sufficient in most cases.

This allows for a reduction in the abstraction of network connections as well. Using a simple enumeration instead of dynamic dispatch should not only perform better, it should also make the code more legible.

@rustaceanrob rustaceanrob marked this pull request as ready for review March 29, 2025 16:59
@rustaceanrob rustaceanrob force-pushed the remove-arti-3-29 branch 4 times, most recently from 35b66d4 to 06eaf5a Compare March 30, 2025 11:01
The Arti project does not have an official MSRV, and the dependency list has
a number of failed security audits caught by the CI job in this repository.
The legacy Tor client is sufficient for desktop and some mobile users, and
no developers have expressed interest in using Arti directly. A simple Socks5
proxy that connects to a Tor daemon allows for Tor connections without an
explicit dependency that causes MSRV conflicts, security audits, and lib-sqlite
version conflicts.
impl_sourceless_error!(PeerError);

#[derive(Debug, Clone)]
pub(crate) enum Socks5Error {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More org bike shedding, but I find it strange to have the Socks5Error type defined outside of the socks module.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, another all-or-nothing change though since I have the DNS errors here as well. I typically prefer anything that is an error just be thrown into the error module. I would consider it mostly a stylistic choice so I don't think I'll touch it for now

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I don't think it is mentioned explicitly in the official style guide. Just reminds me of OG java when we would put things in models and what not.

const THIRTY_MINS: u64 = 60 * 30;
const CONNECTION_TIMEOUT: u64 = 2;

pub(crate) type StreamReader = Box<dyn AsyncRead + Send + Sync + Unpin>;
Copy link
Copy Markdown
Collaborator

@nyonson nyonson Mar 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the overhead of dynamic dispatch worth it if this is pub(crate)? Usually the overhead is useful for flexibility for the caller, but here we are always the caller. Can't we just "hardcode" this to the types we are gonna use and keep static dispatch?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using Arti the most common thing between usual TCP and over Tor was the AsyncRead/Write traits, but this could be made concrete everywhere with OwnedReadHalf and OwnedWriteHalf. I will resist that refactor for now since web transport or something similar may come along and implement the AsyncRead/Write traits in the future (similar to Arti connections).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I do see what you mean, could be another refactor in the future where a peer is supplied a concrete transport enum that contains how they should write and read messages. Although, that's what dynamic dispatch is great for, since it will start to get unruly fast

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have only recently started to think through this, so not a ton of experience, but I think I have a preference to start static and only introduce they dynamic flexibility when the complexity justifies it. Again, not a game changer here, but something I might experiment with later to just get a better feel for it.

};
// Begin the handshake by requesting a connection to the proxy.
let mut tcp_stream = timeout.map_err(|_| Socks5Error::ConnectionFailed)?;
tcp_stream.write_all(&[VERSION, METHODS, NOAUTH]).await?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handshake is pretty straight forward, wonder if we can split it out so it is unit testable. The tokio runtime is buyin a ton for cancellation cleanup so the tight integration makes sense, just thinking another internal function or two to cover the protocol logic.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function isn't inherently cancel safe but it's called directly from the main application loop and is not apart of a select statement, so it is guaranteed to execute in full. Unit tests get tricky because what is the source of truth for the server side? Would either be hardcoded, which feels like homework, add arti as a dev dependency - which I would rather not, or do some edgy shell commands to launch a Tor daemon, also not great.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking a simple mock buffer just to exercise the state machine which is built in here. Don't think it would be too difficult and would probably make the handshake logic super clear. Don't need to refactor now, but might take a stab at it in the future.

Copy link
Copy Markdown
Collaborator

@nyonson nyonson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 64c9046

@rustaceanrob rustaceanrob merged commit 64a88ab into master Mar 31, 2025
12 checks passed
@rustaceanrob rustaceanrob deleted the remove-arti-3-29 branch March 31, 2025 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants