Pass proxy via URL

This commit is contained in:
B. Blechschmidt 2023-03-22 11:17:28 +01:00
parent 0fb58bec5d
commit 3223ca4e22
3 changed files with 64 additions and 42 deletions

View file

@ -11,6 +11,7 @@ env_logger = "0.10"
log = "0.4" log = "0.4"
mio = { version = "0.8", features = ["os-poll", "net", "os-ext"] } mio = { version = "0.8", features = ["os-poll", "net", "os-ext"] }
smoltcp = { version = "0.9", features = ["std"] } smoltcp = { version = "0.9", features = ["std"] }
url = "2.3"
[dev-dependencies] [dev-dependencies]
ctor = "0.1" ctor = "0.1"

View file

@ -7,7 +7,7 @@ pub mod socks5;
pub mod tun2proxy; pub mod tun2proxy;
pub mod virtdevice; pub mod virtdevice;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum ProxyType { pub enum ProxyType {
Socks5, Socks5,
Http, Http,

View file

@ -1,6 +1,6 @@
use std::net::SocketAddr; use std::net::{SocketAddr, ToSocketAddrs};
use clap::{Parser, ValueEnum}; use clap::Parser;
use env_logger::Env; use env_logger::Env;
use tun2proxy::tun2proxy::Credentials; use tun2proxy::tun2proxy::Credentials;
@ -11,55 +11,76 @@ use tun2proxy::{main_entry, ProxyType};
#[command(author, version, about = "Tunnel interface to proxy.", long_about = None)] #[command(author, version, about = "Tunnel interface to proxy.", long_about = None)]
struct Args { struct Args {
/// Name of the tun interface /// Name of the tun interface
#[arg(short, long, value_name = "name")] #[arg(short, long, value_name = "name", default_value = "tun0")]
tun: String, tun: String,
/// What proxy type to run /// What proxy type to run
#[arg(short, long = "proxy", value_name = "type", value_enum)] #[arg(short, long = "proxy", value_parser = proxy_url_parser)]
proxy_type: ArgProxyType, proxy: ArgProxy,
/// Server address with format ip:port
#[clap(short, long, value_name = "ip:port")]
addr: SocketAddr,
/// Username for authentication
#[clap(long, value_name = "username")]
username: Option<String>,
/// Password for authentication
#[clap(long, value_name = "password")]
password: Option<String>,
} }
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] #[derive(Clone)]
enum ArgProxyType { struct ArgProxy {
/// SOCKS5 server to use proxy_type: ProxyType,
Socks5, addr: SocketAddr,
/// HTTP server to use credentials: Credentials,
Http, }
fn proxy_url_parser(s: &str) -> Result<ArgProxy, String> {
let url = url::Url::parse(s).map_err(|_| format!("`{s}` is not a valid proxy URL"))?;
let host = url
.host_str()
.ok_or(format!("`{s}` does not contain a host"))?;
let mut url_host = String::from(host);
let port = url.port().ok_or(format!("`{s}` does not contain a port"))?;
url_host.push(':');
url_host.push_str(port.to_string().as_str());
let mut addr_iter = url_host
.to_socket_addrs()
.map_err(|_| format!("`{host}` could not be resolved"))?;
let addr = addr_iter
.next()
.ok_or(format!("`{host}` does not resolve to a usable IP address"))?;
let credentials = if url.username() == "" && url.password().is_none() {
Credentials::none()
} else {
Credentials::new(
String::from(url.username()),
String::from(url.password().unwrap_or("")),
)
};
let scheme = url.scheme();
let proxy_type = match url.scheme().to_ascii_lowercase().as_str() {
"socks5" => Some(ProxyType::Socks5),
"http" => Some(ProxyType::Http),
_ => None,
}
.ok_or(format!("`{scheme}` is an invalid proxy type"))?;
Ok(ArgProxy {
proxy_type,
addr,
credentials,
})
} }
fn main() { fn main() {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
let args = Args::parse(); let args = Args::parse();
let credentials = if args.username.is_some() || args.password.is_some() { let addr = args.proxy.addr;
Credentials::new( log::info!("Proxy server: {addr}");
args.username.unwrap_or(String::from("")),
args.password.unwrap_or(String::from("")),
)
} else {
Credentials::none()
};
match args.proxy_type { main_entry(
ArgProxyType::Socks5 => { &args.tun,
log::info!("SOCKS5 server: {}", args.addr); args.proxy.addr,
main_entry(&args.tun, args.addr, ProxyType::Socks5, credentials); args.proxy.proxy_type,
} args.proxy.credentials,
ArgProxyType::Http => { );
log::info!("HTTP server: {}", args.addr);
main_entry(&args.tun, args.addr, ProxyType::Http, credentials);
}
}
} }