use std::time::Duration;
use tokio::net::TcpStream;
use crate::connect::*;
use crate::validate_proxy_str;
use crate::{ErrorKind, ProxyAuth, ProxyError, ProxyResult};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Proxy {
address: String,
protocol: ProxyProtocol,
timeout: u64,
auth: Option<ProxyAuth>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProxyProtocol {
#[cfg(feature = "http")]
Http,
#[cfg(feature = "socks4")]
Socks4,
#[cfg(feature = "socks5")]
Socks5,
}
impl From<&str> for ProxyProtocol {
fn from(value: &str) -> Self {
match value {
"http" => Self::Http,
"socks4" => Self::Socks4,
"socks5" => Self::Socks5,
_ => Self::Socks5,
}
}
}
impl From<String> for ProxyProtocol {
fn from(value: String) -> Self {
Self::from(value.as_str())
}
}
impl Proxy {
pub fn new(addr: impl Into<String>, protocol: impl Into<ProxyProtocol>) -> Self {
Self {
address: addr.into(),
protocol: protocol.into(),
timeout: 20000,
auth: None,
}
}
pub fn new_with_auth(addr: impl Into<String>, protocol: impl Into<ProxyProtocol>, auth: impl Into<ProxyAuth>) -> Self {
Self {
address: addr.into(),
protocol: protocol.into(),
timeout: 20000,
auth: Some(auth.into()),
}
}
pub fn with_auth(mut self, auth: impl Into<ProxyAuth>) -> Self {
self.auth = Some(auth.into());
self
}
pub fn with_timeout(mut self, timeout: u64) -> Self {
self.timeout = timeout;
self
}
pub fn with_protocol(mut self, protocol: impl Into<ProxyProtocol>) -> Self {
self.protocol = protocol.into();
self
}
pub async fn is_available(&self) -> bool {
match tokio::time::timeout(Duration::from_millis(self.timeout), TcpStream::connect(&self.address)).await {
Ok(result) => match result {
Ok(_) => return true,
Err(_) => return false,
},
Err(_) => return false,
}
}
pub fn get_protocol(&self) -> &ProxyProtocol {
&self.protocol
}
pub fn get_ip(&self) -> String {
self.address.split(":").collect::<Vec<&str>>()[0].to_string()
}
pub fn get_port(&self) -> u16 {
let port_str = self.address.split(":").collect::<Vec<&str>>()[1];
port_str.parse::<u16>().unwrap_or(match self.protocol {
ProxyProtocol::Http => 80,
ProxyProtocol::Socks4 => 4145,
ProxyProtocol::Socks5 => 1080,
})
}
pub fn get_address(&self) -> &str {
&self.address
}
pub fn get_full_address(&self) -> String {
let protocol = match self.protocol {
#[cfg(feature = "http")]
ProxyProtocol::Http => "http",
#[cfg(feature = "socks4")]
ProxyProtocol::Socks4 => "socks4",
#[cfg(feature = "socks5")]
ProxyProtocol::Socks5 => "socks5",
};
format!("{}://{}", protocol, self.address)
}
pub async fn connect(&self, target_host: impl Into<String>, target_port: u16) -> ProxyResult<TcpStream> {
let mut stream = match tokio::time::timeout(Duration::from_millis(self.timeout), TcpStream::connect(&self.address)).await {
Ok(result) => match result {
Ok(s) => s,
Err(_) => return Err(ProxyError::new(ErrorKind::NotConnected, "could not connect to specified server")),
},
Err(_) => {
return Err(ProxyError::new(
ErrorKind::Timeout,
"failed to connect to server within specified time",
));
}
};
match self.protocol {
#[cfg(feature = "http")]
ProxyProtocol::Http => connect_http(&mut stream, target_host.into(), target_port, &self.auth).await?,
#[cfg(feature = "socks5")]
ProxyProtocol::Socks5 => connect_socks5(&mut stream, target_host.into(), target_port, &self.auth).await?,
#[cfg(feature = "socks4")]
ProxyProtocol::Socks4 => connect_socks4(&mut stream, target_host.into(), target_port, &self.auth).await?,
}
Ok(stream)
}
pub async fn connect_with_stream(&self, stream: &mut TcpStream, target_host: impl Into<String>, target_port: u16) -> ProxyResult<()> {
match self.protocol {
#[cfg(feature = "http")]
ProxyProtocol::Http => connect_http(stream, target_host.into(), target_port, &self.auth).await?,
#[cfg(feature = "socks5")]
ProxyProtocol::Socks5 => connect_socks5(stream, target_host.into(), target_port, &self.auth).await?,
#[cfg(feature = "socks4")]
ProxyProtocol::Socks4 => connect_socks4(stream, target_host.into(), target_port, &self.auth).await?,
}
Ok(())
}
}
impl From<String> for Proxy {
fn from(value: String) -> Self {
if !validate_proxy_str(&value) {
return Self::new("127.0.0.1:1080", ProxyProtocol::Socks5);
}
let proxy_split = value.split("://").collect::<Vec<&str>>();
let without_protocol = proxy_split[1].split("@").collect::<Vec<&str>>();
let (protocol, possible_auth, addr) = {
if without_protocol.len() == 2 {
(proxy_split[0], Some(without_protocol[0]), without_protocol[1])
} else {
(proxy_split[0], None, without_protocol[0])
}
};
if let Some(auth) = possible_auth {
Self::new_with_auth(addr, protocol, auth)
} else {
Self::new(addr, protocol)
}
}
}
impl From<&str> for Proxy {
fn from(value: &str) -> Self {
if !validate_proxy_str(value) {
return Self::new("127.0.0.1:1080", ProxyProtocol::Socks5);
}
let proxy_split = value.split("://").collect::<Vec<&str>>();
let without_protocol = proxy_split[1].split("@").collect::<Vec<&str>>();
let (protocol, possible_auth, addr) = {
if without_protocol.len() == 2 {
(proxy_split[0], Some(without_protocol[0]), without_protocol[1])
} else {
(proxy_split[0], None, without_protocol[0])
}
};
if let Some(auth) = possible_auth {
Self::new_with_auth(addr, protocol, auth)
} else {
Self::new(addr, protocol)
}
}
}