Files
chatmail-core/src/socks.rs
2023-01-01 15:49:19 +00:00

105 lines
3.1 KiB
Rust

//! # SOCKS5 support.
use std::fmt;
use std::pin::Pin;
use std::time::Duration;
use anyhow::Result;
pub use async_smtp::ServerAddress;
use tokio::io::BufWriter;
use tokio::net::{self, TcpStream};
use tokio_io_timeout::TimeoutStream;
use crate::context::Context;
use crate::net::connect_buffered;
use fast_socks5::client::{Config, Socks5Stream};
use fast_socks5::AuthenticationMethod;
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Socks5Config {
pub host: String,
pub port: u16,
pub user_password: Option<(String, String)>,
}
impl Socks5Config {
/// Reads SOCKS5 configuration from the database.
pub async fn from_database(context: &Context) -> Result<Option<Self>> {
let sql = &context.sql;
let enabled = sql.get_raw_config_bool("socks5_enabled").await?;
if enabled {
let host = sql.get_raw_config("socks5_host").await?.unwrap_or_default();
let port: u16 = sql
.get_raw_config_int("socks5_port")
.await?
.unwrap_or_default() as u16;
let user = sql.get_raw_config("socks5_user").await?.unwrap_or_default();
let password = sql
.get_raw_config("socks5_password")
.await?
.unwrap_or_default();
let socks5_config = Self {
host,
port,
user_password: if !user.is_empty() {
Some((user, password))
} else {
None
},
};
Ok(Some(socks5_config))
} else {
Ok(None)
}
}
pub async fn connect(
&self,
target_addr: impl net::ToSocketAddrs,
timeout_val: Duration,
) -> Result<Socks5Stream<BufWriter<Pin<Box<TimeoutStream<TcpStream>>>>>> {
let buffered_stream = connect_buffered(target_addr, timeout_val).await?;
let authentication_method = if let Some((username, password)) = self.user_password.as_ref()
{
Some(AuthenticationMethod::Password {
username: username.into(),
password: password.into(),
})
} else {
None
};
let socks_stream =
Socks5Stream::use_stream(buffered_stream, authentication_method, Config::default())
.await?;
Ok(socks_stream)
}
pub fn to_async_smtp_socks5_config(&self) -> async_smtp::smtp::Socks5Config {
async_smtp::smtp::Socks5Config {
host: self.host.clone(),
port: self.port,
user_password: self.user_password.clone(),
}
}
}
impl fmt::Display for Socks5Config {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"host:{},port:{},user_password:{}",
self.host,
self.port,
if let Some(user_password) = self.user_password.clone() {
format!("user: {}, password: ***", user_password.0)
} else {
"user: None".to_string()
}
)
}
}