1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// ferogram: async Telegram MTProto client in Rust
// https://github.com/ankit-chaubey/ferogram
//
// If you use or modify this code, keep this notice at the top of your file
// and include the LICENSE-MIT or LICENSE-APACHE file from this repository:
// https://github.com/ankit-chaubey/ferogram
use crate::InvocationError;
use tokio::net::TcpStream;
use tokio_socks::tcp::Socks5Stream;
/// SOCKS5 proxy configuration.
#[derive(Clone, Debug)]
pub struct Socks5Config {
/// Host:port of the SOCKS5 proxy server.
pub proxy_addr: String,
/// Optional username and password for proxy authentication.
pub auth: Option<(String, String)>,
}
impl Socks5Config {
/// Create an unauthenticated SOCKS5 config.
pub fn new(proxy_addr: impl Into<String>) -> Self {
Self {
proxy_addr: proxy_addr.into(),
auth: None,
}
}
/// Create a SOCKS5 config with username/password authentication.
pub fn with_auth(
proxy_addr: impl Into<String>,
username: impl Into<String>,
password: impl Into<String>,
) -> Self {
Self {
proxy_addr: proxy_addr.into(),
auth: Some((username.into(), password.into())),
}
}
/// Establish a TCP connection through this SOCKS5 proxy.
///
/// Returns a [`TcpStream`] tunnelled through the proxy to `target`.
pub async fn connect(&self, target: &str) -> Result<TcpStream, InvocationError> {
tracing::info!("[socks5] Connecting via {} → {target}", self.proxy_addr);
let stream = match &self.auth {
None => Socks5Stream::connect(self.proxy_addr.as_str(), target)
.await
.map_err(|e| InvocationError::Io(std::io::Error::other(e)))?,
Some((user, pass)) => Socks5Stream::connect_with_password(
self.proxy_addr.as_str(),
target,
user.as_str(),
pass.as_str(),
)
.await
.map_err(|e| InvocationError::Io(std::io::Error::other(e)))?,
};
tracing::info!("[socks5] Connected ✓");
Ok(stream.into_inner())
}
}