layer-client 0.5.0

Production-grade async Telegram client: updates, bots, flood-wait, dialogs, messages
Documentation
// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
// SPDX-License-Identifier: MIT OR Apache-2.0

// NOTE:
// The "Layer" project is no longer maintained or supported.
// Its original purpose for personal SDK/APK experimentation and learning
// has been fulfilled.
//
// Please use Ferogram instead:
// https://github.com/ankit-chaubey/ferogram
// Ferogram will receive future updates and development, although progress
// may be slower.
//
// Ferogram is an async Telegram MTProto client library written in Rust.
// Its implementation follows the behaviour of the official Telegram clients,
// particularly Telegram Desktop and TDLib, and aims to provide a clean and
// modern async interface for building Telegram clients and tools.

//! SOCKS5 proxy connector.
//!
//! Provides [`Socks5Config`] that can be attached to a [`crate::Config`]
//! so every Telegram connection is routed through a SOCKS5 proxy.
//!
//! # Example
//! ```rust,no_run
//! use layer_client::{Config, proxy::Socks5Config};
//! use std::sync::Arc;
//! use layer_client::retry::AutoSleep;
//!
//! let cfg = Config {
//! socks5: Some(Socks5Config::new("127.0.0.1:1080")),
//! ..Default::default()
//! };
//! ```

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())
    }
}