Use SOCKS5 configuration for HTTP requests

This commit is contained in:
link2xt
2023-02-08 23:26:39 +00:00
parent 151b34ea79
commit fa198c3b5e
7 changed files with 80 additions and 12 deletions

View File

@@ -9,11 +9,13 @@ use fast_socks5::client::{Config, Socks5Stream};
use fast_socks5::util::target_addr::ToTargetAddr;
use fast_socks5::AuthenticationMethod;
use fast_socks5::Socks5Command;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use tokio::net::TcpStream;
use tokio_io_timeout::TimeoutStream;
use crate::context::Context;
use crate::net::connect_tcp;
use crate::sql::Sql;
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Socks5Config {
@@ -24,9 +26,7 @@ pub struct Socks5Config {
impl Socks5Config {
/// Reads SOCKS5 configuration from the database.
pub async fn from_database(context: &Context) -> Result<Option<Self>> {
let sql = &context.sql;
pub async fn from_database(sql: &Sql) -> Result<Option<Self>> {
let enabled = sql.get_raw_config_bool("socks5_enabled").await?;
if enabled {
let host = sql.get_raw_config("socks5_host").await?.unwrap_or_default();
@@ -55,6 +55,20 @@ impl Socks5Config {
}
}
/// Converts SOCKS5 configuration into URL.
pub fn to_url(&self) -> String {
// `socks5h` means that hostname is resolved into address by the proxy
// and DNS requests should not leak.
let mut url = "socks5h://".to_string();
if let Some((username, password)) = &self.user_password {
let username_urlencoded = utf8_percent_encode(username, NON_ALPHANUMERIC).to_string();
let password_urlencoded = utf8_percent_encode(password, NON_ALPHANUMERIC).to_string();
url += &format!("{username_urlencoded}:{password_urlencoded}@");
}
url += &format!("{}:{}", self.host, self.port);
url
}
/// If `load_dns_cache` is true, loads cached DNS resolution results.
/// Use this only if the connection is going to be protected with TLS checks.
pub async fn connect(
@@ -103,3 +117,35 @@ impl fmt::Display for Socks5Config {
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_socks5h_url() {
let config = Socks5Config {
host: "127.0.0.1".to_string(),
port: 9050,
user_password: None,
};
assert_eq!(config.to_url(), "socks5h://127.0.0.1:9050");
let config = Socks5Config {
host: "example.org".to_string(),
port: 1080,
user_password: Some(("root".to_string(), "toor".to_string())),
};
assert_eq!(config.to_url(), "socks5h://root:toor@example.org:1080");
let config = Socks5Config {
host: "example.org".to_string(),
port: 1080,
user_password: Some(("root".to_string(), "foo/?\\@".to_string())),
};
assert_eq!(
config.to_url(),
"socks5h://root:foo%2F%3F%5C%40@example.org:1080"
);
}
}