use socks5_impl::{client::UdpClientImpl, protocol::UserKey, Result}; use std::{ net::{SocketAddr, ToSocketAddrs}, time::Duration, }; use tokio::net::UdpSocket; /// Udp client through socks5 proxy. #[derive(clap::Parser, Debug, Clone, PartialEq, Eq)] #[command(author, version, about = "Udp client through socks5 proxy", long_about = None)] pub struct CmdOpt { /// Udp target server address. #[clap(short, long, value_name = "addr:port")] target_addr: SocketAddr, /// Data string to send. #[clap(short, long, value_name = "data")] data: String, /// Via socks5 proxy server. #[clap(short, long)] via_proxy: bool, /// Socket5 proxy server address. #[clap(short, long, value_name = "addr:port")] proxy_addr: Option, /// User name for authentication. #[clap(short, long, value_name = "user name")] username: Option, /// Password for authentication. #[clap(short = 'w', long, value_name = "password")] password: Option, /// Timeout in seconds. #[clap(short = 'm', long, value_name = "seconds", default_value = "2")] timeout: u64, } impl CmdOpt { pub fn validate(&self) -> Result<(), String> { if self.via_proxy && self.proxy_addr.is_none() { return Err("proxy_addr is required when via_proxy is true".into()); } Ok(()) } } #[tokio::main] async fn main() -> Result<()> { let opt: CmdOpt = clap::Parser::parse(); opt.validate()?; let user_key = match (opt.username, opt.password) { (Some(username), password) => Some(UserKey::new(username, password.unwrap_or_default())), _ => None, }; let timeout = Duration::from_secs(opt.timeout); if !opt.via_proxy { let target_addr = opt.target_addr.to_socket_addrs()?.next().ok_or("invalid address")?; let zero_addr = if target_addr.is_ipv4() { "0.0.0.0:0" } else { "[::]:0" }; let zero_addr = zero_addr.to_socket_addrs()?.next().ok_or("invalid address")?; let udp = UdpSocket::bind(zero_addr).await?; udp.send_to(opt.data.as_bytes(), target_addr).await?; let mut buf = [0u8; 1024]; let (len, _) = tokio::time::timeout(timeout, udp.recv_from(&mut buf)).await??; println!("{}", std::str::from_utf8(&buf[..len])?); } else { let proxy_addr = opt.proxy_addr.ok_or("proxy_addr is required")?; let data = UdpClientImpl::datagram(proxy_addr, opt.target_addr, user_key) .await? .transfer_data(opt.data.as_bytes(), timeout) .await?; println!("{}", std::str::from_utf8(data.as_slice())?); } Ok(()) }