1use crate::InvocationError;
19use tokio::net::TcpStream;
20use tokio_socks::tcp::Socks5Stream;
21
22#[derive(Clone, Debug)]
24pub struct Socks5Config {
25 pub proxy_addr: String,
27 pub auth: Option<(String, String)>,
29}
30
31impl Socks5Config {
32 pub fn new(proxy_addr: impl Into<String>) -> Self {
34 Self {
35 proxy_addr: proxy_addr.into(),
36 auth: None,
37 }
38 }
39
40 pub fn with_auth(
42 proxy_addr: impl Into<String>,
43 username: impl Into<String>,
44 password: impl Into<String>,
45 ) -> Self {
46 Self {
47 proxy_addr: proxy_addr.into(),
48 auth: Some((username.into(), password.into())),
49 }
50 }
51
52 pub async fn connect(&self, target: &str) -> Result<TcpStream, InvocationError> {
56 tracing::info!("[socks5] Connecting via {} → {target}", self.proxy_addr);
57 let stream = match &self.auth {
58 None => Socks5Stream::connect(self.proxy_addr.as_str(), target)
59 .await
60 .map_err(|e| InvocationError::Io(std::io::Error::other(e)))?,
61 Some((user, pass)) => Socks5Stream::connect_with_password(
62 self.proxy_addr.as_str(),
63 target,
64 user.as_str(),
65 pass.as_str(),
66 )
67 .await
68 .map_err(|e| InvocationError::Io(std::io::Error::other(e)))?,
69 };
70 tracing::info!("[socks5] Connected ✓");
71 Ok(stream.into_inner())
72 }
73}