use std::time::Duration;
use tokio::net::TcpStream;
use tokio::time::timeout;
use crate::connect::*;
use crate::{ErrorKind, ProxyAuth, ProxyError, ProxyResult};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Proxy {
proxy_type: ProxyType,
proxy_address: String,
timeout: u64,
auth: Option<ProxyAuth>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProxyType {
#[cfg(feature = "http")]
Http,
#[cfg(feature = "socks4")]
Socks4,
#[cfg(feature = "socks5")]
Socks5,
}
impl Proxy {
pub fn new(proxy_address: impl Into<String>, proxy_type: ProxyType) -> Self {
Self {
proxy_address: proxy_address.into(),
proxy_type: proxy_type,
timeout: 20000,
auth: None,
}
}
pub fn new_with_auth(proxy_address: impl Into<String>, proxy_type: ProxyType, auth: ProxyAuth) -> Self {
Self {
proxy_address: proxy_address.into(),
proxy_type: proxy_type,
timeout: 20000,
auth: Some(auth),
}
}
pub fn with_auth(mut self, auth: ProxyAuth) -> Self {
self.auth = Some(auth);
self
}
pub fn with_timeout(mut self, timeout: u64) -> Self {
self.timeout = timeout;
self
}
pub fn with_proxy_type(mut self, proxy_type: ProxyType) -> Self {
self.proxy_type = proxy_type;
self
}
pub async fn is_available(&self) -> bool {
match timeout(Duration::from_millis(self.timeout), TcpStream::connect(&self.proxy_address)).await {
Ok(result) => match result {
Ok(_) => return true,
Err(_) => return false,
},
Err(_) => return false,
}
}
pub fn get_ip(&self) -> Option<String> {
if let Some(ip) = self.proxy_address.split(":").collect::<Vec<&str>>().get(0) {
Some(ip.to_string())
} else {
None
}
}
pub fn get_port(&self) -> Option<u16> {
if let Some(port_str) = self.proxy_address.split(":").collect::<Vec<&str>>().get(1) {
if let Ok(port) = (*port_str).parse::<u16>() {
return Some(port);
}
}
None
}
pub fn get_address(&self) -> &str {
&self.proxy_address
}
pub fn get_full_address(&self) -> String {
let protocol = match self.proxy_type {
#[cfg(feature = "http")]
ProxyType::Http => "http",
#[cfg(feature = "socks4")]
ProxyType::Socks4 => "socks4",
#[cfg(feature = "socks5")]
ProxyType::Socks5 => "socks5",
};
format!("{}://{}", protocol, self.proxy_address)
}
pub async fn connect(&self, target_host: impl Into<String>, target_port: u16) -> ProxyResult<TcpStream> {
let mut stream = match timeout(Duration::from_millis(self.timeout), TcpStream::connect(&self.proxy_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.proxy_type {
#[cfg(feature = "http")]
ProxyType::Http => connect_http(&mut stream, target_host.into(), target_port, &self.auth).await?,
#[cfg(feature = "socks5")]
ProxyType::Socks5 => connect_socks5(&mut stream, target_host.into(), target_port, &self.auth).await?,
#[cfg(feature = "socks4")]
ProxyType::Socks4 => connect_socks4(&mut stream, target_host.into(), target_port, &self.auth).await?,
}
Ok(stream)
}
pub async fn connect_with_stream(
&self,
mut stream: TcpStream,
target_host: impl Into<String>,
target_port: u16,
) -> ProxyResult<TcpStream> {
match self.proxy_type {
#[cfg(feature = "http")]
ProxyType::Http => connect_http(&mut stream, target_host.into(), target_port, &self.auth).await?,
#[cfg(feature = "socks5")]
ProxyType::Socks5 => connect_socks5(&mut stream, target_host.into(), target_port, &self.auth).await?,
#[cfg(feature = "socks4")]
ProxyType::Socks4 => connect_socks4(&mut stream, target_host.into(), target_port, &self.auth).await?,
}
Ok(stream)
}
}
impl From<String> for Proxy {
fn from(value: String) -> Self {
let split = value.split("://").collect::<Vec<&str>>();
let (protocol, proxy) = (split.get(0).unwrap_or(&"socks5"), split.get(1).unwrap_or(&"127.0.0.1"));
Self {
proxy_address: (*proxy).to_string(),
proxy_type: match *protocol {
#[cfg(feature = "http")]
"http" => ProxyType::Http,
#[cfg(feature = "socks5")]
"socks5" => ProxyType::Socks5,
#[cfg(feature = "socks4")]
"socks4" => ProxyType::Socks4,
#[cfg(feature = "socks5")]
_ => ProxyType::Socks5,
#[cfg(all(not(feature = "socks5"), feature = "socks4"))]
_ => ProxyType::Socks4,
#[cfg(all(not(feature = "socks5"), not(feature = "socks4"), feature = "http"))]
_ => ProxyType::Http,
},
timeout: 20000,
auth: None,
}
}
}
impl From<&str> for Proxy {
fn from(value: &str) -> Self {
let split = value.split("://").collect::<Vec<&str>>();
let (protocol, proxy) = (split.get(0).unwrap_or(&"socks5"), split.get(1).unwrap_or(&"127.0.0.1"));
Self {
proxy_address: (*proxy).to_string(),
proxy_type: match *protocol {
#[cfg(feature = "http")]
"http" => ProxyType::Http,
#[cfg(feature = "socks5")]
"socks5" => ProxyType::Socks5,
#[cfg(feature = "socks4")]
"socks4" => ProxyType::Socks4,
#[cfg(feature = "socks5")]
_ => ProxyType::Socks5,
#[cfg(all(not(feature = "socks5"), feature = "socks4"))]
_ => ProxyType::Socks4,
#[cfg(all(not(feature = "socks5"), not(feature = "socks4"), feature = "http"))]
_ => ProxyType::Http,
},
timeout: 20000,
auth: None,
}
}
}