layer_client/socks5.rs
1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4// NOTE:
5// The "Layer" project is no longer maintained or supported.
6// Its original purpose for personal SDK/APK experimentation and learning
7// has been fulfilled.
8//
9// Please use Ferogram instead:
10// https://github.com/ankit-chaubey/ferogram
11// Ferogram will receive future updates and development, although progress
12// may be slower.
13//
14// Ferogram is an async Telegram MTProto client library written in Rust.
15// Its implementation follows the behaviour of the official Telegram clients,
16// particularly Telegram Desktop and TDLib, and aims to provide a clean and
17// modern async interface for building Telegram clients and tools.
18
19//! SOCKS5 proxy connector.
20//!
21//! Provides [`Socks5Config`] that can be attached to a [`crate::Config`]
22//! so every Telegram connection is routed through a SOCKS5 proxy.
23//!
24//! # Example
25//! ```rust,no_run
26//! use layer_client::{Config, proxy::Socks5Config};
27//! use std::sync::Arc;
28//! use layer_client::retry::AutoSleep;
29//!
30//! let cfg = Config {
31//! socks5: Some(Socks5Config::new("127.0.0.1:1080")),
32//! ..Default::default()
33//! };
34//! ```
35
36use crate::InvocationError;
37use tokio::net::TcpStream;
38use tokio_socks::tcp::Socks5Stream;
39
40/// SOCKS5 proxy configuration.
41#[derive(Clone, Debug)]
42pub struct Socks5Config {
43 /// Host:port of the SOCKS5 proxy server.
44 pub proxy_addr: String,
45 /// Optional username and password for proxy authentication.
46 pub auth: Option<(String, String)>,
47}
48
49impl Socks5Config {
50 /// Create an unauthenticated SOCKS5 config.
51 pub fn new(proxy_addr: impl Into<String>) -> Self {
52 Self {
53 proxy_addr: proxy_addr.into(),
54 auth: None,
55 }
56 }
57
58 /// Create a SOCKS5 config with username/password authentication.
59 pub fn with_auth(
60 proxy_addr: impl Into<String>,
61 username: impl Into<String>,
62 password: impl Into<String>,
63 ) -> Self {
64 Self {
65 proxy_addr: proxy_addr.into(),
66 auth: Some((username.into(), password.into())),
67 }
68 }
69
70 /// Establish a TCP connection through this SOCKS5 proxy.
71 ///
72 /// Returns a [`TcpStream`] tunnelled through the proxy to `target`.
73 pub async fn connect(&self, target: &str) -> Result<TcpStream, InvocationError> {
74 tracing::info!("[socks5] Connecting via {} → {target}", self.proxy_addr);
75 let stream = match &self.auth {
76 None => Socks5Stream::connect(self.proxy_addr.as_str(), target)
77 .await
78 .map_err(|e| InvocationError::Io(std::io::Error::other(e)))?,
79 Some((user, pass)) => Socks5Stream::connect_with_password(
80 self.proxy_addr.as_str(),
81 target,
82 user.as_str(),
83 pass.as_str(),
84 )
85 .await
86 .map_err(|e| InvocationError::Io(std::io::Error::other(e)))?,
87 };
88 tracing::info!("[socks5] Connected ✓");
89 Ok(stream.into_inner())
90 }
91}